From 70910c4bc288cbc2bcf1ca9b859039429da9a176 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 4 May 2018 16:04:36 +0100 Subject: [PATCH 01/26] fix timing issues with collect loan tests --- test/loanCollection.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/loanCollection.js b/test/loanCollection.js index 1d3fbdd6..84640f94 100644 --- a/test/loanCollection.js +++ b/test/loanCollection.js @@ -96,21 +96,25 @@ contract("Loans collection tests", accounts => { it("Should get and collect a loan with discountRate = 1 (zero interest)", async function() { const loan = await loanTestHelpers.createLoan(this, products.zeroInterest, accounts[0], web3.toWei(0.05)); + await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); it("Should get and collect a loan with discountRate > 1 (negative interest)", async function() { const loan = await loanTestHelpers.createLoan(this, products.negativeInterest, accounts[0], web3.toWei(0.05)); + await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); it("Should get and collect a loan with collateralRatio = 1", async function() { const loan = await loanTestHelpers.createLoan(this, products.fullCoverage, accounts[0], web3.toWei(0.05)); + await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); it("Should get and collect a loan with collateralRatio > 1", async function() { const loan = await loanTestHelpers.createLoan(this, products.moreCoverage, accounts[0], web3.toWei(0.05)); + await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); From c3455c25f3be862a9c3dfaed08990d6fd5ff9b1d Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 4 May 2018 18:41:51 +0100 Subject: [PATCH 02/26] use web3v1 in tests (prep for tx signature test, requires web3v1) --- package.json | 3 +- test/exchangeMatching.js | 52 +- test/exchangeOrders.js | 31 +- test/exchangeRandom.js | 2 +- test/feeAccount.js | 9 +- test/helpers/exchangeTestHelpers.js | 6 +- test/helpers/loanTestHelpers.js | 17 +- test/helpers/ratesTestHelpers.js | 2 +- test/helpers/testHelpers.js | 44 +- test/helpers/tokenTestHelpers.js | 4 +- test/loanCollection.js | 87 +- test/loanToDepositRatioLimits.js | 14 +- test/loans.js | 84 +- test/locker.js | 8 +- test/rates.js | 14 +- yarn.lock | 1321 ++++++++++++++++++++++++++- 16 files changed, 1573 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 2e2db654..fef2b7d5 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "random-seed": "0.3.0", "solidity-coverage": "0.5.0", "stringifier": "1.3.0", - "truffle": "4.1.8" + "truffle": "4.1.8", + "web3v1": "npm:web3@1.0.0-beta.34" }, "greenkeeper": { "ignore": ["bignumber.js"] diff --git a/test/exchangeMatching.js b/test/exchangeMatching.js index fed23a08..70918860 100644 --- a/test/exchangeMatching.js +++ b/test/exchangeMatching.js @@ -7,12 +7,14 @@ const TOKEN_SELL = testHelpers.TOKEN_SELL; let snapshotId; let exchange = null; -const maker = web3.eth.accounts[1]; -const taker = web3.eth.accounts[2]; +let maker; +let taker; -contract("Exchange matching tests", accounts => { +contract("Exchange matching tests", () => { before(async function() { exchange = exchangeTestHelper.exchange; + maker = global.accounts[1]; + taker = global.accounts[2]; await tokenTestHelpers.issueToReserve(10000000); await tokenTestHelpers.withdrawFromReserve(maker, 1000000); @@ -28,7 +30,12 @@ contract("Exchange matching tests", accounts => { }); it("should match two matching orders (buy token fully filled)", async function() { - const buyOrder = { amount: web3.toWei(0.535367), maker: maker, price: 1010000, orderType: TOKEN_BUY }; + const buyOrder = { + amount: global.web3v1.utils.toWei("0.535367"), + maker: maker, + price: 1010000, + orderType: TOKEN_BUY + }; const sellOrder = { amount: 95582, maker: taker, price: 990000, orderType: TOKEN_SELL }; await exchangeTestHelper.newOrder(this, buyOrder); @@ -43,7 +50,12 @@ contract("Exchange matching tests", accounts => { }); it("should match two matching orders (sell token fully filled)", async function() { - const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 1010000, orderType: TOKEN_BUY }; + const buyOrder = { + amount: global.web3v1.utils.toWei("1.7504"), + maker: maker, + price: 1010000, + orderType: TOKEN_BUY + }; const sellOrder = { amount: 56141, maker: taker, price: 990000, orderType: TOKEN_SELL }; await exchangeTestHelper.newOrder(this, buyOrder); @@ -57,7 +69,7 @@ contract("Exchange matching tests", accounts => { }); it("should match two matching orders (both fully filled)", async function() { - const buyOrder = { amount: web3.toWei(1), maker: maker, price: 1000000, orderType: TOKEN_BUY }; + const buyOrder = { amount: global.web3v1.utils.toWei("1"), maker: maker, price: 1000000, orderType: TOKEN_BUY }; const sellOrder = { amount: 99800, maker: maker, price: 990000, orderType: TOKEN_SELL }; await exchangeTestHelper.newOrder(this, buyOrder); @@ -75,7 +87,12 @@ contract("Exchange matching tests", accounts => { Sell: 100A€ / 998 A€/ETH = 0.1002004008 ETH Buy: 0.1002004008 ETH * 998 A€/ETH = 99.9999999984 A€ wich is 100A€ b/c A€ is w/ 2 decimals */ - const buyOrder = { amount: web3.toWei(0.1002004008), maker: maker, price: 1000000, orderType: TOKEN_BUY }; + const buyOrder = { + amount: global.web3v1.utils.toWei("0.1002004008"), + maker: maker, + price: 1000000, + orderType: TOKEN_BUY + }; const sellOrder = { amount: 10000, maker: maker, price: 1000000, orderType: TOKEN_SELL }; await exchangeTestHelper.newOrder(this, buyOrder); @@ -91,7 +108,12 @@ contract("Exchange matching tests", accounts => { }); it("should match two matching orders from the same account on sell price if placed first ", async function() { - const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 1010000, orderType: TOKEN_BUY }; + const buyOrder = { + amount: global.web3v1.utils.toWei("1.7504"), + maker: maker, + price: 1010000, + orderType: TOKEN_BUY + }; const sellOrder = { amount: 56141, maker: maker, price: 990000, orderType: TOKEN_SELL }; await exchangeTestHelper.newOrder(this, sellOrder); @@ -106,7 +128,12 @@ contract("Exchange matching tests", accounts => { it("should NOT match two non-matching orders", async function() { // buy price lower then sell price, should fail - const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 1010000, orderType: TOKEN_BUY }; + const buyOrder = { + amount: global.web3v1.utils.toWei("1.7504"), + maker: maker, + price: 1010000, + orderType: TOKEN_BUY + }; const sellOrder = { amount: 56141, maker: maker, price: 1010001, orderType: TOKEN_SELL }; await exchangeTestHelper.newOrder(this, buyOrder); @@ -115,7 +142,12 @@ contract("Exchange matching tests", accounts => { }); it("shouldn't matchmultiple if different count of buy&sell orders passed ", async function() { - const buyOrder1 = { amount: web3.toWei(0.535367), maker: maker, price: 1010000, orderType: TOKEN_BUY }; + const buyOrder1 = { + amount: global.web3v1.utils.toWei("0.535367"), + maker: maker, + price: 1010000, + orderType: TOKEN_BUY + }; const sellOrder1 = { amount: 95582, maker: taker, price: 900000, orderType: TOKEN_SELL }; const sellOrder2 = { amount: 95582, maker: taker, price: 900000, orderType: TOKEN_SELL }; diff --git a/test/exchangeOrders.js b/test/exchangeOrders.js index 7293ac19..7eca993f 100644 --- a/test/exchangeOrders.js +++ b/test/exchangeOrders.js @@ -4,7 +4,8 @@ const exchangeTestHelpers = require("./helpers/exchangeTestHelpers.js"); const TOKEN_BUY = testHelpers.TOKEN_BUY; const TOKEN_SELL = testHelpers.TOKEN_SELL; -const makers = [web3.eth.accounts[1], web3.eth.accounts[2]]; + +let makers; let snapshotId; let augmintToken = null; @@ -12,6 +13,7 @@ let exchange = null; contract("Exchange orders tests", accounts => { before(async function() { + makers = [global.accounts[1], global.accounts[2]]; exchange = exchangeTestHelpers.exchange; augmintToken = tokenTestHelpers.augmintToken; @@ -29,7 +31,12 @@ contract("Exchange orders tests", accounts => { }); it("place buy token orders", async function() { - const order = { amount: web3.toWei(1), maker: makers[0], price: 1000000, orderType: TOKEN_BUY }; + const order = { + amount: global.web3v1.utils.toWei("1"), + maker: makers[0], + price: 1000000, + orderType: TOKEN_BUY + }; await exchangeTestHelpers.newOrder(this, order); await exchangeTestHelpers.newOrder(this, order); @@ -99,7 +106,7 @@ contract("Exchange orders tests", accounts => { it("shouldn't place a BUY token order with 0 price", async function() { const price = 0; - await testHelpers.expectThrow(exchange.placeBuyTokenOrder(price, { value: web3.toWei(0.1) })); + await testHelpers.expectThrow(exchange.placeBuyTokenOrder(price, { value: global.web3v1.utils.toWei("0.1") })); }); it("no SELL token order when user doesn't have enough ACE", async function() { @@ -111,7 +118,12 @@ contract("Exchange orders tests", accounts => { }); it("should cancel a BUY token order", async function() { - const order = { amount: web3.toWei(1), maker: makers[0], price: 1010000, orderType: TOKEN_BUY }; + const order = { + amount: global.web3v1.utils.toWei("1"), + maker: makers[0], + price: 1010000, + orderType: TOKEN_BUY + }; await exchangeTestHelpers.newOrder(this, order); await exchangeTestHelpers.cancelOrder(this, order); @@ -125,7 +137,12 @@ contract("Exchange orders tests", accounts => { }); it("only own orders should be possible to cancel", async function() { - const buyOrder = { amount: web3.toWei(1), maker: makers[0], price: 1020000, orderType: TOKEN_BUY }; + const buyOrder = { + amount: global.web3v1.utils.toWei("1"), + maker: makers[0], + price: 1020000, + orderType: TOKEN_BUY + }; const sellOrder = { amount: 45454, maker: makers[0], price: 1010000, orderType: TOKEN_SELL }; await exchangeTestHelpers.newOrder(this, buyOrder); @@ -138,7 +155,9 @@ contract("Exchange orders tests", accounts => { const orderCount = 4; const orders = []; for (let i = 0; i < orderCount; i++) { - orders.push(exchange.placeBuyTokenOrder(1000000 + i, { value: web3.toWei(0.5), from: makers[1] })); + orders.push( + exchange.placeBuyTokenOrder(1000000 + i, { value: global.web3v1.utils.toWei("0.5"), from: makers[1] }) + ); } const txs = await Promise.all(orders); assert(txs.length, orderCount); diff --git a/test/exchangeRandom.js b/test/exchangeRandom.js index 12de8e88..05882587 100644 --- a/test/exchangeRandom.js +++ b/test/exchangeRandom.js @@ -15,7 +15,7 @@ const MIN_ORDER_RATE = 990000; // 99% - 1% below parity const MAX_ORDER_RATE = 1010000; // 101% - 1 % above parity const MIN_TOKEN = 10000; // 100 ACE const MAX_TOKEN = 100000; // 1,000 ACE -const TEST_ACCS_CT = web3.eth.accounts.length; +const TEST_ACCS_CT = 10; // accounts.length; const ACC_INIT_ACE = 1000000; const CHUNK_SIZE = 100; const random = new RandomSeed("Have the same test data"); diff --git a/test/feeAccount.js b/test/feeAccount.js index b1f489c0..6999b349 100644 --- a/test/feeAccount.js +++ b/test/feeAccount.js @@ -33,11 +33,16 @@ contract("FeeAccount tests", accounts => { }); it("should be possible to withdraw from fee account", async function() { - const weiAmount = web3.toWei(0.2); + const weiAmount = global.web3v1.utils.toWei("0.2"); + const tokenAmount = 10000; const narrative = "test withdrawal"; // top up feeAccount with ETH - await web3.eth.sendTransaction({ from: accounts[0], to: feeAccountInstance.address, value: weiAmount }); + await global.web3v1.eth.sendTransaction({ + from: accounts[0], + to: feeAccountInstance.address, + value: weiAmount + }); // top up feeAccount with tokens await tokenTestHelpers.issueToReserve(tokenAmount); diff --git a/test/helpers/exchangeTestHelpers.js b/test/helpers/exchangeTestHelpers.js index 3c145e90..72cc1e55 100644 --- a/test/helpers/exchangeTestHelpers.js +++ b/test/helpers/exchangeTestHelpers.js @@ -221,7 +221,7 @@ async function matchOrders(testInstance, buyTokenOrder, sellTokenOrder) { buyer: buyTokenOrder.maker }); - const matchCaller = web3.eth.accounts[0]; + const matchCaller = global.accounts[0]; const currentRate = parseInt((await rates.rates("EUR"))[0]); const expPrice = buyTokenOrder.id > sellTokenOrder.id ? sellTokenOrder.price : buyTokenOrder.price; @@ -393,8 +393,8 @@ async function printOrderBook(_limit) { }); buyOrders.slice(0, _limit).map((order, i) => { console.log( - ` ${i}. BUY token: price: ${order.price / 10000}% amount: ${web3.fromWei(order.amount)} ETH` + - ` orderId: ${order.id} acc: ${order.maker}` + ` ${i}. BUY token: price: ${order.price / 10000}% amount: ${order.amount / + testHelpers.ONE_ETH} ETH` + ` orderId: ${order.id} acc: ${order.maker}` ); }); diff --git a/test/helpers/loanTestHelpers.js b/test/helpers/loanTestHelpers.js index 0cc886a5..6b34f58a 100644 --- a/test/helpers/loanTestHelpers.js +++ b/test/helpers/loanTestHelpers.js @@ -252,19 +252,18 @@ async function collectLoan(testInstance, loan, collector) { // ` *** Collection params: // A-EUR/EUR: ${rate[0] / 100} // defaulting fee pt: ${loan.product.defaultingFeePt / 10000} % - // repaymentAmount: ${loan.repaymentAmount / 10000} A-EUR = ${web3.fromWei(repaymentAmountInWei)} ETH - // collateral: ${web3.fromWei(loan.collateralAmount).toString()} ETH = ${collateralInToken / 100} A-EUR + // repaymentAmount: ${loan.repaymentAmount / 10000} A-EUR = ${repaymentAmountInWei / testHelpers.ONE_ETH} ETH + // collateral: ${loan.collateralAmount / testHelpers.ONE_ETH} ETH = ${collateralInToken / 100} A-EUR // -------------------- - // targetFee: ${targetFeeInToken / 100} A-EUR = ${web3.fromWei(targetFeeInWei).toString()} ETH - // target collection : ${targetCollectionInToken / 100} A-EUR = ${web3 - // .fromWei(targetCollectionInWei) - // .toString()} ETH - // collected: ${web3.fromWei(collectedCollateral).toString()} ETH - // released: ${web3.fromWei(releasedCollateral).toString()} ETH - // defaultingFee: ${web3.fromWei(defaultingFee).toString()} ETH` + // targetFee: ${targetFeeInToken / 100} A-EUR = ${targetFeeInWei / testHelpers.ONE_ETH} ETH + // target collection : ${targetCollectionInToken / 100} A-EUR = ${targetCollectionInWei / testHelpers.ONE_ETH} ETH + // collected: ${collectedCollateral / testHelpers.ONE_ETH} ETH + // released: ${releasedCollateral / testHelpers.ONE_ETH} ETH + // defaultingFee: ${defaultingFee / testHelpers.ONE_ETH} ETH` // ); const tx = await loanManager.collect([loan.id], { from: loan.collector }); + testHelpers.logGasUse(testInstance, tx, "collect 1"); const [totalSupplyAfter, totalLoanAmountAfter, , ,] = await Promise.all([ diff --git a/test/helpers/ratesTestHelpers.js b/test/helpers/ratesTestHelpers.js index 10970ca5..801ed626 100644 --- a/test/helpers/ratesTestHelpers.js +++ b/test/helpers/ratesTestHelpers.js @@ -22,7 +22,7 @@ async function newRatesAsserts(tx, symbols, newRates) { for (let i = 0; i < symbols.length; i++) { assert.equal(tx.logs[i].event, "RateChanged", "RateChanged event should be emited for " + i + ". symbol"); assert.equal( - web3.toAscii(tx.logs[i].args.symbol).slice(0, symbols[i].length), + global.web3v1.utils.toAscii(tx.logs[i].args.symbol).slice(0, symbols[i].length), symbols[i], "symbol " + i + ". should be set in RateChanged event" ); diff --git a/test/helpers/testHelpers.js b/test/helpers/testHelpers.js index a1cb7343..9293d8f7 100644 --- a/test/helpers/testHelpers.js +++ b/test/helpers/testHelpers.js @@ -1,6 +1,21 @@ const stringifier = require("stringifier"); var gasUseLog = []; let gasPrice = null; +let networkId; + +// Use web3v1 in tests. Nb: truffle uses web3 v0.x while web3 v1 is required for tx signatures +const Web3v1 = require("web3v1"); + +//global.web3v0 = new Web3v0(new Web3v0.providers.HttpProvider("http://localhost:8545")); +global.web3v1 = new Web3v1(new Web3v1.providers.HttpProvider("http://localhost:8545")); +global.accounts = []; + +//dirty hack for web3@1.0.0 support for localhost testrpc, see https://github.com/trufflesuite/truffle-contract/issues/56#issuecomment-331084530 +if (typeof global.web3v1.currentProvider.sendAsync !== "function") { + global.web3v1.currentProvider.sendAsync = function() { + return global.web3v1.currentProvider.send.apply(global.web3v1.currentProvider, arguments); + }; +} module.exports = { stringify, @@ -44,7 +59,11 @@ const gasUseLogDisabled = process.env.TEST_DISABLE_LOG_GAS_USE && process.env.TEST_DISABLE_LOG_GAS_USE.trim().toLowerCase() === "true"; before(async function() { - gasPrice = await getGasPrice(); + [global.accounts, gasPrice, networkId] = await Promise.all([ + global.web3v1.eth.getAccounts().then(res => res.map(acc => acc.toLowerCase())), + global.web3v1.eth.getGasPrice(), + global.web3v1.eth.net.getId() + ]); }); function stringify(values) { @@ -67,20 +86,6 @@ async function getGasCost(gas) { return gasPrice.mul(gas); } -async function getGasPrice() { - // TODO: could we read gasPrice (and other global params) in a global beforeAll somehow ? - // web3 0.x doesn't seem to have promisified fx for this - return new Promise(function(resolve, reject) { - web3.eth.getGasPrice((error, gasPrice) => { - if (error) { - reject(new Error("Can't get gasprice from web3 (getGasPrice).\n " + error)); - } else { - resolve(gasPrice); - } - }); - }); -} - async function assertEvent(contractInstance, eventName, expectedArgs) { const events = await getEvents(contractInstance, eventName); assert(events.length === 1, `Expected ${eventName} event wasn't received from ${contractInstance.address}`); // how to get contract name? @@ -149,7 +154,7 @@ async function assertNoEvents(contractInstance, eventName) { //let snapshotCount = 0; function takeSnapshot() { return new Promise(function(resolve, reject) { - web3.currentProvider.sendAsync( + global.web3v1.currentProvider.sendAsync( { method: "evm_snapshot", params: [], @@ -169,7 +174,7 @@ function takeSnapshot() { function revertSnapshot(snapshotId) { return new Promise(function(resolve, reject) { - web3.currentProvider.sendAsync( + global.web3v1.currentProvider.sendAsync( { method: "evm_revert", params: [snapshotId], @@ -199,11 +204,10 @@ function waitFor(durationInMs = 1000) { setTimeout(() => { // make a transaction to force the local dev node to create a new block with // new timestamp: - web3.eth.sendTransaction({ from: web3.eth.accounts[0] }, err => { + global.web3v1.eth.sendTransaction({ from: global.accounts[0] }, err => { if (err) { return reject(err); } - resolve(); }); }, durationInMs); @@ -215,7 +219,7 @@ async function waitForTimeStamp(UnixTimestamp) { } function expectThrow(promise) { - const onPrivateChain = web3.version.network === 1976 ? true : false; // set by .runprivatechain.sh (geth ... --networkid 1976 ..) + const onPrivateChain = networkId === 1976 ? true : false; // set by .runprivatechain.sh (geth ... --networkid 1976 ..) return promise .then(res => { if (!onPrivateChain) { diff --git a/test/helpers/tokenTestHelpers.js b/test/helpers/tokenTestHelpers.js index 20f7e8c3..de02c3ef 100644 --- a/test/helpers/tokenTestHelpers.js +++ b/test/helpers/tokenTestHelpers.js @@ -54,7 +54,7 @@ before(async function() { interestEarnedAccount = InterestEarnedAccount.at(InterestEarnedAccount.address); feeAccount = FeeAccount.at(FeeAccount.address); - peggedSymbol = web3.toAscii(await augmintToken.peggedSymbol()); + peggedSymbol = global.web3v1.utils.toAscii(await augmintToken.peggedSymbol()); }); async function issueToReserve(amount) { @@ -228,7 +228,7 @@ async function getAllBalances(accs) { const address = accs[ac].address ? accs[ac].address : accs[ac]; ret[ac] = {}; ret[ac].address = address; - ret[ac].eth = await web3.eth.getBalance(address); + ret[ac].eth = new BigNumber(await global.web3v1.eth.getBalance(address)); ret[ac].ace = await augmintToken.balanceOf(address); } diff --git a/test/loanCollection.js b/test/loanCollection.js index 84640f94..6ea7a142 100644 --- a/test/loanCollection.js +++ b/test/loanCollection.js @@ -50,7 +50,12 @@ contract("Loans collection tests", accounts => { }); it("Should collect a defaulted A-EUR loan and send back leftover collateral ", async function() { - const loan = await loanTestHelpers.createLoan(this, products.defaulting, accounts[1], web3.toWei(0.5)); + const loan = await loanTestHelpers.createLoan( + this, + products.defaulting, + accounts[1], + global.web3v1.utils.toWei("0.5") + ); await testHelpers.waitForTimeStamp(loan.maturity); @@ -59,7 +64,12 @@ contract("Loans collection tests", accounts => { it("Should collect a defaulted A-EUR loan when no leftover collateral (collection exactly covered)", async function() { await rates.setRate("EUR", 100000); - const loan = await loanTestHelpers.createLoan(this, products.defaultingNoLeftOver, accounts[1], web3.toWei(1)); + const loan = await loanTestHelpers.createLoan( + this, + products.defaultingNoLeftOver, + accounts[1], + global.web3v1.utils.toWei("1") + ); await Promise.all([rates.setRate("EUR", 99000), testHelpers.waitForTimeStamp(loan.maturity)]); @@ -72,7 +82,7 @@ contract("Loans collection tests", accounts => { this, products.defaultingNoLeftOver, accounts[1], - web3.toWei(0.2) + global.web3v1.utils.toWei("0.2") ); await Promise.all([rates.setRate("EUR", 98900), testHelpers.waitForTimeStamp(loan.maturity)]); @@ -86,7 +96,7 @@ contract("Loans collection tests", accounts => { this, products.defaultingNoLeftOver, accounts[1], - web3.toWei(0.2) + global.web3v1.utils.toWei("0.2") ); await Promise.all([rates.setRate("EUR", 1), testHelpers.waitForTimeStamp(loan.maturity)]); @@ -95,25 +105,45 @@ contract("Loans collection tests", accounts => { }); it("Should get and collect a loan with discountRate = 1 (zero interest)", async function() { - const loan = await loanTestHelpers.createLoan(this, products.zeroInterest, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.zeroInterest, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); it("Should get and collect a loan with discountRate > 1 (negative interest)", async function() { - const loan = await loanTestHelpers.createLoan(this, products.negativeInterest, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.negativeInterest, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); it("Should get and collect a loan with collateralRatio = 1", async function() { - const loan = await loanTestHelpers.createLoan(this, products.fullCoverage, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.fullCoverage, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); it("Should get and collect a loan with collateralRatio > 1", async function() { - const loan = await loanTestHelpers.createLoan(this, products.moreCoverage, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.moreCoverage, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await testHelpers.waitForTimeStamp(loan.maturity); await loanTestHelpers.collectLoan(this, loan, accounts[2]); }); @@ -121,9 +151,18 @@ contract("Loans collection tests", accounts => { it("Should collect multiple defaulted loans", async function() { const loanCount = (await loanManager.getLoanCount()).toNumber(); await Promise.all([ - loanManager.newEthBackedLoan(products.zeroInterest.id, { from: accounts[0], value: web3.toWei(0.05) }), - loanManager.newEthBackedLoan(products.fullCoverage.id, { from: accounts[1], value: web3.toWei(0.05) }), - loanManager.newEthBackedLoan(products.negativeInterest.id, { from: accounts[1], value: web3.toWei(0.05) }) + loanManager.newEthBackedLoan(products.zeroInterest.id, { + from: accounts[0], + value: global.web3v1.utils.toWei("0.05") + }), + loanManager.newEthBackedLoan(products.fullCoverage.id, { + from: accounts[1], + value: global.web3v1.utils.toWei("0.05") + }), + loanManager.newEthBackedLoan(products.negativeInterest.id, { + from: accounts[1], + value: global.web3v1.utils.toWei("0.05") + }) ]); await testHelpers.waitFor(1000); @@ -135,8 +174,14 @@ contract("Loans collection tests", accounts => { it("Should NOT collect multiple loans if one is not due", async function() { const loanCount = (await loanManager.getLoanCount()).toNumber(); await Promise.all([ - loanManager.newEthBackedLoan(products.notDue.id, { from: accounts[0], value: web3.toWei(0.05) }), - loanManager.newEthBackedLoan(products.defaulting.id, { from: accounts[1], value: web3.toWei(0.05) }) + loanManager.newEthBackedLoan(products.notDue.id, { + from: accounts[0], + value: global.web3v1.utils.toWei("0.05") + }), + loanManager.newEthBackedLoan(products.defaulting.id, { + from: accounts[1], + value: global.web3v1.utils.toWei("0.05") + }) ]); await testHelpers.waitFor(1000); @@ -145,7 +190,12 @@ contract("Loans collection tests", accounts => { }); it("Should NOT collect a loan before it's due", async function() { - const loan = await loanTestHelpers.createLoan(this, products.notDue, accounts[1], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.notDue, + accounts[1], + global.web3v1.utils.toWei("0.05") + ); await testHelpers.expectThrow(loanManager.collect([loan.id])); }); @@ -155,7 +205,7 @@ contract("Loans collection tests", accounts => { this, products.defaultingNoLeftOver, accounts[1], - web3.toWei(0.05) + global.web3v1.utils.toWei("0.05") ); await Promise.all([rates.setRate("EUR", 0), testHelpers.waitForTimeStamp(loan.maturity)]); @@ -164,7 +214,12 @@ contract("Loans collection tests", accounts => { }); it("Should NOT collect an already collected loan", async function() { - const loan = await loanTestHelpers.createLoan(this, products.defaulting, accounts[1], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.defaulting, + accounts[1], + global.web3v1.utils.toWei("0.05") + ); await testHelpers.waitForTimeStamp(loan.maturity); diff --git a/test/loanToDepositRatioLimits.js b/test/loanToDepositRatioLimits.js index e647ff72..69756aa0 100644 --- a/test/loanToDepositRatioLimits.js +++ b/test/loanToDepositRatioLimits.js @@ -158,7 +158,7 @@ contract("Loan to Deposit ratio tests", accounts => { it("LTD when totalLock = 0 and totalLoan > 0 and allowed difference amount is in effect", async function() { // get a loan - const collateralAmount = web3.toWei(3000 / rate); + const collateralAmount = global.web3v1.utils.toWei((3000 / rate).toString()); await loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount }); // Earned interest 0 @@ -187,7 +187,7 @@ contract("Loan to Deposit ratio tests", accounts => { it("LTD when totalLock = totalLoan and allowed difference amount is in effect", async function() { // get a loan - const collateralAmount = web3.toWei(3000 / rate); + const collateralAmount = global.web3v1.utils.toWei((3000 / rate).toString()); await loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount }); // lock the same amount @@ -225,7 +225,7 @@ contract("Loan to Deposit ratio tests", accounts => { await monetarySupervisor.setLtdParams(ltdParams.lockDifferenceLimit, ltdParams.loanDifferenceLimit, 1000000); // get a loan - const collateralAmount = web3.toWei(640000 / rate); + const collateralAmount = global.web3v1.utils.toWei((640000 / rate).toString()); await loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount }); // lock less than the loan @@ -270,7 +270,7 @@ contract("Loan to Deposit ratio tests", accounts => { await monetarySupervisor.setLtdParams(ltdParams.lockDifferenceLimit, ltdParams.loanDifferenceLimit, 1000000); // get a loan - const collateralAmount = web3.toWei(640000 / rate); + const collateralAmount = global.web3v1.utils.toWei((640000 / rate).toString()); await loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount }); // lock less than the loan @@ -311,7 +311,7 @@ contract("Loan to Deposit ratio tests", accounts => { await monetarySupervisor.setLtdParams(ltdParams.lockDifferenceLimit, ltdParams.loanDifferenceLimit, 1000000); // get a loan - const collateralAmount = web3.toWei(600000 / rate); + const collateralAmount = global.web3v1.utils.toWei((600000 / rate).toString()); await loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount }); // lock more than the loan @@ -356,7 +356,7 @@ contract("Loan to Deposit ratio tests", accounts => { await monetarySupervisor.setLtdParams(ltdParams.lockDifferenceLimit, ltdParams.loanDifferenceLimit, 10000000); // get a loan - const collateralAmount = web3.toWei(359974 / rate); + const collateralAmount = global.web3v1.utils.toWei((359974 / rate).toString()); await loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount }); // lock more than the loan @@ -409,7 +409,7 @@ contract("Loan to Deposit ratio tests", accounts => { }); it("should NOT allow to borrow more than maxLoanAmountAllowedByLtd ", async function() { - const collateralAmount = web3.toWei((ltdParams.allowedDifferenceAmount + 1) / rate); + const collateralAmount = global.web3v1.utils.toWei(((ltdParams.allowedDifferenceAmount + 1) / rate).toString()); await testHelpers.expectThrow(loanManager.newEthBackedLoan(loanProductId, { value: collateralAmount })); }); }); diff --git a/test/loans.js b/test/loans.js index e1a7daf2..9bae12df 100644 --- a/test/loans.js +++ b/test/loans.js @@ -60,7 +60,7 @@ contract("Loans tests", accounts => { }); it("Should get an A-EUR loan", async function() { - await loanTestHelpers.createLoan(this, products.repaying, accounts[0], web3.toWei(0.5)); + await loanTestHelpers.createLoan(this, products.repaying, accounts[0], global.web3v1.utils.toWei("0.5")); }); it("Should NOT get a loan less than minDisbursedAmount", async function() { @@ -80,12 +80,20 @@ contract("Loans tests", accounts => { it("Shouldn't get a loan for a disabled product", async function() { await testHelpers.expectThrow( - loanManager.newEthBackedLoan(products.disabledProduct.id, { from: accounts[0], value: web3.toWei(0.05) }) + loanManager.newEthBackedLoan(products.disabledProduct.id, { + from: accounts[0], + value: global.web3v1.utils.toWei("0.05") + }) ); }); it("Should repay an A-EUR loan before maturity", async function() { - const loan = await loanTestHelpers.createLoan(this, products.notDue, accounts[1], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.notDue, + accounts[1], + global.web3v1.utils.toWei("0.05") + ); // send interest to borrower to have enough A-EUR to repay in test await augmintToken.transfer(loan.borrower, loan.interestAmount, { from: accounts[0] @@ -94,27 +102,52 @@ contract("Loans tests", accounts => { }); it("Should get and repay a loan whith discountRate = 1 (zero interest)", async function() { - const loan = await loanTestHelpers.createLoan(this, products.zeroInterest, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.zeroInterest, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await loanTestHelpers.repayLoan(this, loan); }); it("Should get and repay a loan whith discountRate > 1 (negative interest)", async function() { - const loan = await loanTestHelpers.createLoan(this, products.negativeInterest, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.negativeInterest, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await loanTestHelpers.repayLoan(this, loan); }); it("Should get and repay a loan with colletaralRatio = 1", async function() { - const loan = await loanTestHelpers.createLoan(this, products.fullCoverage, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.fullCoverage, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await loanTestHelpers.repayLoan(this, loan); }); it("Should get and repay a loan with colletaralRatio > 1", async function() { - const loan = await loanTestHelpers.createLoan(this, products.moreCoverage, accounts[0], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.moreCoverage, + accounts[0], + global.web3v1.utils.toWei("0.05") + ); await loanTestHelpers.repayLoan(this, loan); }); it("Non owner should be able to repay a loan too", async function() { - const loan = await loanTestHelpers.createLoan(this, products.notDue, accounts[1], web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.notDue, + accounts[1], + global.web3v1.utils.toWei("0.05") + ); await augmintToken.transferAndNotify(loanManager.address, loan.repaymentAmount, loan.id, { from: accounts[0] @@ -128,7 +161,12 @@ contract("Loans tests", accounts => { it("Should NOT repay an A-EUR loan on maturity if A-EUR balance is insufficient", async function() { const borrower = accounts[2]; - const loan = await loanTestHelpers.createLoan(this, products.notDue, borrower, web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.notDue, + borrower, + global.web3v1.utils.toWei("0.05") + ); const accBal = await augmintToken.balanceOf(borrower); // send just 0.01 A€ less than required for repayment @@ -145,7 +183,12 @@ contract("Loans tests", accounts => { it("should not repay a loan with smaller amount than repaymentAmount", async function() { const borrower = accounts[1]; - const loan = await loanTestHelpers.createLoan(this, products.notDue, borrower, web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.notDue, + borrower, + global.web3v1.utils.toWei("0.05") + ); await augmintToken.transfer(loan.borrower, loan.interestAmount, { from: accounts[0] @@ -165,7 +208,12 @@ contract("Loans tests", accounts => { it("Should NOT repay a loan after maturity", async function() { const borrower = accounts[0]; - const loan = await loanTestHelpers.createLoan(this, products.defaulting, borrower, web3.toWei(0.05)); + const loan = await loanTestHelpers.createLoan( + this, + products.defaulting, + borrower, + global.web3v1.utils.toWei("0.05") + ); const [accBal] = await Promise.all([ augmintToken.balanceOf(borrower), @@ -185,7 +233,7 @@ contract("Loans tests", accounts => { await testHelpers.expectThrow( loanManager.newEthBackedLoan(products.repaying.id, { from: accounts[1], - value: web3.toWei(0.1) + value: global.web3v1.utils.toWei("0.1") }) ); await rates.setRate("EUR", 99800); // restore rates @@ -195,8 +243,8 @@ contract("Loans tests", accounts => { const product = products.repaying; const product2 = products.defaulting; - const loan1 = await loanTestHelpers.createLoan(this, product, accounts[1], web3.toWei(0.05)); - const loan2 = await loanTestHelpers.createLoan(this, product2, accounts[2], web3.toWei(0.06)); + const loan1 = await loanTestHelpers.createLoan(this, product, accounts[1], global.web3v1.utils.toWei("0.05")); + const loan2 = await loanTestHelpers.createLoan(this, product2, accounts[2], global.web3v1.utils.toWei("0.06")); await testHelpers.waitForTimeStamp(loan2.maturity); @@ -236,8 +284,8 @@ contract("Loans tests", accounts => { const product2 = products.defaulting; const borrower = accounts[1]; - const loan1 = await loanTestHelpers.createLoan(this, product1, borrower, web3.toWei(0.05)); - const loan2 = await loanTestHelpers.createLoan(this, product2, borrower, web3.toWei(0.06)); + const loan1 = await loanTestHelpers.createLoan(this, product1, borrower, global.web3v1.utils.toWei("0.05")); + const loan2 = await loanTestHelpers.createLoan(this, product2, borrower, global.web3v1.utils.toWei("0.06")); const accountLoanCount = await loanManager.getLoanCountForAddress(borrower); await testHelpers.waitForTimeStamp(loan2.maturity); @@ -274,11 +322,11 @@ contract("Loans tests", accounts => { await craftedLender.addLoanProduct(100000, 1000000, 1000000, 1000, 50000, true); // testing Lender not having "LoanManagerContracts" permission on monetarySupervisor: - await testHelpers.expectThrow(craftedLender.newEthBackedLoan(0, { value: web3.toWei(0.05) })); + await testHelpers.expectThrow(craftedLender.newEthBackedLoan(0, { value: global.web3v1.utils.toWei("0.05") })); // grant permission to create new loan await monetarySupervisor.grantPermission(craftedLender.address, "LoanManagerContracts"); - await craftedLender.newEthBackedLoan(0, { value: web3.toWei(0.05) }); + await craftedLender.newEthBackedLoan(0, { value: global.web3v1.utils.toWei("0.05") }); // revoke permission and try to repay await monetarySupervisor.revokePermission(craftedLender.address, "LoanManagerContracts"), diff --git a/test/locker.js b/test/locker.js index 013adbd1..52c7bb38 100644 --- a/test/locker.js +++ b/test/locker.js @@ -210,7 +210,7 @@ contract("Lock", accounts => { const interestEarned = Math.floor(amountToLock * perTermInterest / 1000000); // need the block to get the timestamp to check lockedUntil in NewLock event: - const block = await web3.eth.getBlock(lockingTransaction.receipt.blockHash); + const block = await global.web3v1.eth.getBlock(lockingTransaction.receipt.blockHash); const expectedLockedUntil = block.timestamp + durationInSecs; // sanity check: assert(expectedLockedUntil > Math.floor(Date.now() / 1000)); @@ -366,7 +366,7 @@ contract("Lock", accounts => { const expectedDurationInSecs = product[1].toNumber(); // need the block to get the timestamp to check lockedUntil in NewLock event: - const block = await web3.eth.getBlock(lockingTransaction.receipt.blockHash); + const block = await global.web3v1.eth.getBlock(lockingTransaction.receipt.blockHash); const expectedLockedUntil = block.timestamp + expectedDurationInSecs; // sanity check: assert(expectedLockedUntil > Math.floor(Date.now() / 1000)); @@ -403,7 +403,7 @@ contract("Lock", accounts => { const expectedInterestEarned = Math.floor(amountToLock * expectedPerTermInterest / 1000000); // need the block to get the timestamp to check lockedUntil in NewLock event: - const block = await web3.eth.getBlock(lockingTransaction.receipt.blockHash); + const block = await global.web3v1.eth.getBlock(lockingTransaction.receipt.blockHash); const expectedLockedUntil = block.timestamp + expectedDurationInSecs; // sanity check: assert(expectedLockedUntil > Math.floor(Date.now() / 1000)); @@ -464,7 +464,7 @@ contract("Lock", accounts => { const expectedInterestEarned = Math.floor(amountToLock * expectedPerTermInterest / 1000000); // need the block to get the timestamp to check lockedUntil in NewLock event: - const block = await web3.eth.getBlock(lockingTransaction.receipt.blockHash); + const block = await global.web3v1.eth.getBlock(lockingTransaction.receipt.blockHash); const expectedLockedUntil = block.timestamp + expectedDurationInSecs; // sanity check: assert(expectedLockedUntil > Math.floor(Date.now() / 1000)); diff --git a/test/rates.js b/test/rates.js index 9d0d187c..d753a4ff 100644 --- a/test/rates.js +++ b/test/rates.js @@ -51,10 +51,18 @@ contract("Rates tests", accounts => { await rates.setRate("EUR", rate); const ethValue = await rates.convertToWei("EUR", testEur); - assert.equal(web3.fromWei(ethValue), testEur / rate, "ethValue converted should be correct"); + assert.equal( + global.web3v1.utils.fromWei(ethValue.toString()), + testEur / rate, + "ethValue converted should be correct" + ); const eurValue = await rates.convertFromWei("EUR", ethValue); - assert.equal(eurValue.toString(), web3.fromWei(ethValue) * rate, "eurValue converted should be correct"); - //console.log(web3.fromWei(ethValue).toString(), eurValue.toString()); + assert.equal( + eurValue.toString(), + global.web3v1.utils.fromWei(ethValue.toString()) * rate, + "eurValue converted should be correct" + ); + //console.log(global.web3v1.utils.fromWei(ethValue).toString(), eurValue.toString()); }); it("setRate should allow to set 0 rate", async function() { diff --git a/yarn.lock b/yarn.lock index f36dcb91..d4c0c065 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,6 +29,13 @@ abiniser@0.3.0: json-stable-stringify "1.0.1" md5 "2.2.1" +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + ajv@^5.1.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -84,6 +91,10 @@ any-observable@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242" +any-promise@1.3.0, any-promise@^1.0.0, any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -112,6 +123,10 @@ array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -134,6 +149,14 @@ arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -154,6 +177,10 @@ ast-types@0.11.3: version "0.11.3" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + async@1.x, async@^1.4.0, async@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -772,6 +799,14 @@ balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +base64-js@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -806,6 +841,50 @@ binaryextensions@2: version "2.1.1" resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935" +bl@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^2.9.34: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + +bluebird@^3.5.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.6, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + +body-parser@1.18.2, body-parser@^1.16.0: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + boom@4.x.x: version "4.3.1" resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" @@ -848,24 +927,120 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.1.tgz#3343124db6d7ad53e26a8826318712bdc8450f9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + browserify-sha3@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/browserify-sha3/-/browserify-sha3-0.0.1.tgz#3ff34a3006ef15c0fb3567e541b91a2340123d11" dependencies: js-sha3 "^0.3.1" +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +buffer-alloc-unsafe@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-0.1.1.tgz#ffe1f67551dd055737de253337bfe853dfab1a6a" + +buffer-alloc@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.1.0.tgz#05514d33bf1656d3540c684f65b1202e90eca303" + dependencies: + buffer-alloc-unsafe "^0.1.0" + buffer-fill "^0.1.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + +buffer-fill@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-0.1.1.tgz#76d825c4d6e50e06b7a31eb520c04d08cc235071" + buffer-from@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^3.0.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb" + dependencies: + base64-js "0.0.8" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.0.5: + version "5.1.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.1.0.tgz#c913e43678c7cb7c8bd16afbcddb6c5505e8f9fe" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -953,6 +1128,13 @@ charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -1096,7 +1278,7 @@ commander@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" -commander@2.15.1: +commander@2.15.1, commander@^2.8.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -1110,6 +1292,12 @@ commander@2.9.0: dependencies: graceful-readlink ">= 1.0.0" +commander@~2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" + dependencies: + graceful-readlink ">= 1.0.0" + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1122,10 +1310,26 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + convert-source-map@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1138,6 +1342,13 @@ core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cors@^2.8.1: + version "2.8.4" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686" + dependencies: + object-assign "^4" + vary "^1" + coveralls@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.1.tgz#12e15914eaa29204e56869a5ece7b9e1492d2ae2" @@ -1148,6 +1359,34 @@ coveralls@3.0.1: minimist "^1.2.0" request "^2.79.0" +create-ecdh@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.1.tgz#44223dfed533193ba5ba54e0df5709b89acf1f82" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -1176,6 +1415,22 @@ cryptiles@3.x.x: dependencies: boom "5.x.x" +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + crypto-js@^3.1.4: version "3.1.8" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.8.tgz#715f070bf6014f2ae992a98b3929258b713f08d5" @@ -1214,7 +1469,7 @@ debug@2.6.8: dependencies: ms "2.0.0" -debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1240,6 +1495,54 @@ decompress-response@^3.2.0, decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + deep-extend@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f" @@ -1271,6 +1574,25 @@ delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" +depd@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + +depd@~1.1.1, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + detect-conflict@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e" @@ -1293,6 +1615,14 @@ diff@^3.3.1, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + dir-glob@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" @@ -1300,6 +1630,10 @@ dir-glob@^2.0.0: arrify "^1.0.1" path-type "^3.0.0" +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -1314,6 +1648,10 @@ editions@^1.3.3: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + ejs@^2.5.9: version "2.5.9" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5" @@ -1322,10 +1660,32 @@ elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" +elliptic@^6.0.0, elliptic@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + +end-of-stream@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + dependencies: + once "^1.4.0" + enhanced-resolve@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz#e34a6eaa790f62fccd71d93959f56b2b432db10a" @@ -1357,6 +1717,10 @@ error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + escape-string-regexp@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" @@ -1392,6 +1756,30 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +eth-lib@0.1.27, eth-lib@^0.1.26: + version "0.1.27" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + keccakjs "^0.2.1" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-lib@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + ethereumjs-testrpc-sc@6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/ethereumjs-testrpc-sc/-/ethereumjs-testrpc-sc-6.1.2.tgz#bd1d8306abb2d51481f3f01538fa71fb7fd44a4d" @@ -1399,6 +1787,24 @@ ethereumjs-testrpc-sc@6.1.2: source-map-support "^0.5.3" webpack-cli "^2.0.9" +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +eventemitter3@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.1.1.tgz#47786bdaa087caf7b1b75e73abc5c7d540158cd0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -1445,6 +1851,41 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +express@^4.14.0: + version "4.16.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.3" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -1519,6 +1960,12 @@ fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + dependencies: + pend "~1.2.0" + figures@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -1532,6 +1979,18 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + +file-type@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -1555,6 +2014,18 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -1578,6 +2049,12 @@ flow-parser@^0.*: version "0.71.0" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.71.0.tgz#da2479b83f9207905b4b17ab0c4e6d17bd505250" +for-each@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" + dependencies: + is-function "~1.0.0" + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1600,12 +2077,20 @@ form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -1613,6 +2098,10 @@ from2@^2.1.1: inherits "^2.0.1" readable-stream "^2.0.0" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -1623,10 +2112,35 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^2.0.0, fs-extra@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + +fs-promise@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-2.0.3.tgz#f64e4f854bcf689aa8bddcba268916db3db46854" + dependencies: + any-promise "^1.3.0" + fs-extra "^2.0.0" + mz "^2.6.0" + thenify-all "^1.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +fstream@^1.0.2, fstream@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + ganache-cli@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.1.0.tgz#486c846497204b644166b5f0f74c9b41d02bdc25" @@ -1642,6 +2156,13 @@ get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -1753,6 +2274,13 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +global@~4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + dependencies: + min-document "^2.19.0" + process "~0.5.1" + globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -1779,7 +2307,7 @@ globby@^8.0.0: pify "^3.0.0" slash "^1.0.0" -got@^7.0.0: +got@7.1.0, got@^7.0.0, got@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" dependencies: @@ -1820,7 +2348,7 @@ got@^8.2.0: url-parse-lax "^3.0.0" url-to-options "^1.0.1" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -1914,6 +2442,20 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + hawk@~6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" @@ -1927,6 +2469,14 @@ he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hoek@4.x.x: version "4.2.1" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" @@ -1952,6 +2502,28 @@ http-cache-semantics@3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" +http-errors@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -1960,12 +2532,20 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + iconv-lite@^0.4.17: version "0.4.21" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.21.tgz#c47f8733d02171189ebc4a400f3218d348094798" dependencies: safer-buffer "^2.1.0" +ieee754@^1.1.4: + version "1.1.11" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455" + ignore@^3.3.5: version "3.3.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b" @@ -1998,7 +2578,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2064,6 +2644,10 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -2162,6 +2746,10 @@ is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" +is-function@^1.0.1, is-function@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" + is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -2180,6 +2768,14 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -2248,7 +2844,7 @@ is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" -is-typedarray@~1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -2260,7 +2856,7 @@ is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" -isarray@1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2696,7 +3292,7 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -make-dir@^1.1.0: +make-dir@^1.0.0, make-dir@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" dependencies: @@ -2712,6 +3308,13 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + md5@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" @@ -2720,6 +3323,10 @@ md5@2.2.1: crypt "~0.0.1" is-buffer "~1.1.1" +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + mem-fs-editor@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-4.0.1.tgz#27e6b59df91b37248e9be2145b1bea84695103ed" @@ -2761,9 +3368,17 @@ memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + merge2@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.1.tgz#271d2516ff52d4af7f7b710b8bf3e16e183fef66" + version "1.2.2" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" micromatch@^2.3.7: version "2.3.11" @@ -2801,16 +3416,27 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-types@^2.1.12, mime-types@~2.1.17: +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.17, mime-types@~2.1.18: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: mime-db "~1.33.0" +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -2819,6 +3445,20 @@ mimic-response@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + minimatch@0.3: version "0.3.0" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" @@ -2855,16 +3495,22 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + dependencies: + mkdirp "*" -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@*, mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + mocha@^2.4.5: version "2.5.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" @@ -2897,10 +3543,18 @@ mocha@^3.4.2: mkdirp "0.5.1" supports-color "3.1.2" +mock-fs@^4.1.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.4.2.tgz#09dec5313f97095a450be6aa2ad8ab6738d63d6b" + moment@2.22.1: version "2.22.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad" +mout@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/mout/-/mout-0.11.1.tgz#ba3611df5f0e5b1ffbfd01166b8f02d1f5fa2b99" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -2922,10 +3576,22 @@ mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -nan@2.10.0: +mz@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nan@2.10.0, nan@^2.0.8, nan@^2.3.3: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + nanomatch@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" @@ -2943,6 +3609,10 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + neo-async@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" @@ -3001,11 +3671,18 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -3036,7 +3713,19 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -once@1.x, once@^1.3.0: +oboe@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.3.tgz#2b4865dbd46be81225713f4e9bfe4bcf4f680a4f" + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3167,6 +3856,16 @@ p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" +parse-asn1@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -3176,6 +3875,13 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" +parse-headers@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536" + dependencies: + for-each "^0.3.2" + trim "0.0.1" + parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -3193,6 +3899,10 @@ parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -3223,6 +3933,10 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -3237,10 +3951,24 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +pbkdf2@^3.0.3: + version "3.0.16" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + pegjs@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -3305,6 +4033,17 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + +proxy-addr@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.6.0" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -3313,10 +4052,24 @@ pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +public-encrypt@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +qs@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + qs@~6.5.1: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -3342,6 +4095,36 @@ randomatic@^1.1.3: is-number "^3.0.0" kind-of "^4.0.0" +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +randomhex@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" + +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + read-chunk@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-2.1.0.tgz#6a04c0928005ed9d42e1a6ac5600e19cbc7ff655" @@ -3605,7 +4388,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@^2.2.8, rimraf@^2.6.2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: @@ -3615,6 +4398,13 @@ rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + run-async@^2.0.0, run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -3637,7 +4427,11 @@ rxjs@^5.4.2, rxjs@^5.5.2: dependencies: symbol-observable "1.0.1" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, 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" @@ -3655,10 +4449,72 @@ scoped-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8" +scrypt.js@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada" + dependencies: + scrypt "^6.0.2" + scryptsy "^1.2.1" + +scrypt@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" + dependencies: + nan "^2.0.8" + +scryptsy@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" + dependencies: + pbkdf2 "^3.0.3" + +seek-bzip@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" + dependencies: + commander "~2.8.1" + "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3681,6 +4537,25 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + sha3@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.2.tgz#a66c5098de4c25bc88336ec8b4817d005bca7ba9" @@ -3721,6 +4596,18 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -3908,6 +4795,14 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + stream-to-observable@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10" @@ -3988,10 +4883,22 @@ strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + dependencies: + is-natural-number "^4.0.1" + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + dependencies: + is-hex-prefixed "1.0.0" + supports-color@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" @@ -4018,6 +4925,24 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +swarm-js@0.1.37: + version "0.1.37" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.37.tgz#27d485317a340bbeec40292af783cc10acfa4663" + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + decompress "^4.0.0" + eth-lib "^0.1.26" + fs-extra "^2.1.2" + fs-promise "^2.0.0" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar.gz "^1.0.5" + xhr-request-promise "^0.1.2" + symbol-observable@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" @@ -4030,6 +4955,36 @@ tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" +tar-stream@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.0.tgz#a50efaa7b17760b82c27b3cae4a301a8254a5715" + dependencies: + bl "^1.0.0" + buffer-alloc "^1.1.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.0.0" + to-buffer "^1.1.0" + xtend "^4.0.0" + +tar.gz@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/tar.gz/-/tar.gz-1.0.7.tgz#577ef2c595faaa73452ef0415fed41113212257b" + dependencies: + bluebird "^2.9.34" + commander "^2.8.1" + fstream "^1.0.8" + mout "^0.11.0" + tar "^2.1.1" + +tar@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + temp@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -4045,6 +5000,18 @@ textextensions@2: version "2.2.0" resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286" +thenify-all@^1.0.0, thenify-all@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.0" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + dependencies: + any-promise "^1.0.0" + through2@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" @@ -4066,6 +5033,10 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +to-buffer@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" @@ -4110,6 +5081,10 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + truffle@4.1.8: version "4.1.8" resolved "https://registry.yarnpkg.com/truffle/-/truffle-4.1.8.tgz#b0b9175e0270145999567a3f0a2337c914a23a9c" @@ -4134,10 +5109,23 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-is@~1.6.15, type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + type-name@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/type-name/-/type-name-2.0.2.tgz#efe7d4123d8ac52afff7f40c7e4dec5266008fb4" +typedarray-to-buffer@^3.1.2: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + dependencies: + is-typedarray "^1.0.0" + uglify-js@^2.6: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" @@ -4151,6 +5139,21 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + +unbzip2-stream@^1.0.9: + version "1.2.5" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47" + dependencies: + buffer "^3.0.1" + through "^2.3.6" + +underscore@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" @@ -4164,6 +5167,10 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^0.4.3" +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -4191,6 +5198,10 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" @@ -4201,6 +5212,10 @@ use@^3.1.0: dependencies: kind-of "^6.0.2" +utf8@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.1.tgz#2e01db02f7d8d0944f77104f1609eb0c304cf768" + utf8@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96" @@ -4209,6 +5224,14 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + uuid@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" @@ -4224,6 +5247,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -4262,6 +5289,189 @@ vinyl@^2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" +web3-bzz@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.34.tgz#068d37777ab65e5c60f8ec8b9a50cfe45277929c" + dependencies: + got "7.1.0" + swarm-js "0.1.37" + underscore "1.8.3" + +web3-core-helpers@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz#b168da00d3e19e156bc15ae203203dd4dfee2d03" + dependencies: + underscore "1.8.3" + web3-eth-iban "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-core-method@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.34.tgz#ec163c8a2c490fa02a7ec15559fa7307fc7cc6dd" + dependencies: + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.34" + web3-core-promievent "1.0.0-beta.34" + web3-core-subscriptions "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-core-promievent@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.34.tgz#a4f4fa6784bb293e82c60960ae5b56a94cd03edc" + dependencies: + any-promise "1.3.0" + eventemitter3 "1.1.1" + +web3-core-requestmanager@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.34.tgz#01f8f6cf2ae6b6f0b70c38bae1ef741b5bab215c" + dependencies: + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.34" + web3-providers-http "1.0.0-beta.34" + web3-providers-ipc "1.0.0-beta.34" + web3-providers-ws "1.0.0-beta.34" + +web3-core-subscriptions@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.34.tgz#9fed144033f221c3cf21060302ffdaf5ef2de2de" + dependencies: + eventemitter3 "1.1.1" + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.34" + +web3-core@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.34.tgz#121be8555e9fb00d2c5d05ddd3381d0c9e46987e" + dependencies: + web3-core-helpers "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-core-requestmanager "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-eth-abi@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz#034533e3aa2f7e59ff31793eaea685c0ed5af67a" + dependencies: + bn.js "4.11.6" + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-eth-accounts@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.34.tgz#e09142eeecc797ac3459b75e9b23946d3695f333" + dependencies: + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + scrypt.js "0.2.0" + underscore "1.8.3" + uuid "2.0.1" + web3-core "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-eth-contract@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.34.tgz#9dbb38fae7643a808427a20180470ec7415c91e6" + dependencies: + underscore "1.8.3" + web3-core "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-core-promievent "1.0.0-beta.34" + web3-core-subscriptions "1.0.0-beta.34" + web3-eth-abi "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-eth-iban@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz#9af458605867ccf74ea979aaf326b38ba6a5ba0c" + dependencies: + bn.js "4.11.6" + web3-utils "1.0.0-beta.34" + +web3-eth-personal@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.34.tgz#9afba167342ebde5420bcd5895c3f6c34388f205" + dependencies: + web3-core "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-net "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-eth@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.34.tgz#74086000850c6fe6f535ef49837d6d4bb6113268" + dependencies: + underscore "1.8.3" + web3-core "1.0.0-beta.34" + web3-core-helpers "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-core-subscriptions "1.0.0-beta.34" + web3-eth-abi "1.0.0-beta.34" + web3-eth-accounts "1.0.0-beta.34" + web3-eth-contract "1.0.0-beta.34" + web3-eth-iban "1.0.0-beta.34" + web3-eth-personal "1.0.0-beta.34" + web3-net "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-net@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.34.tgz#427cea2f431881449c8e38d523290f173f9ff63d" + dependencies: + web3-core "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + +web3-providers-http@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.34.tgz#e561b52bbb43766282007d40285bfe3550c27e7a" + dependencies: + web3-core-helpers "1.0.0-beta.34" + xhr2 "0.1.4" + +web3-providers-ipc@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.34.tgz#a1b77f1a306d73649a9c039052e40cb71328d00a" + dependencies: + oboe "2.1.3" + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.34" + +web3-providers-ws@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.34.tgz#7de70f1b83f2de36476772156becfef6e3516eb3" + dependencies: + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.34" + websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" + +web3-shh@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.34.tgz#975061d71eaec42ccee576f7bd8f70f03844afe0" + dependencies: + web3-core "1.0.0-beta.34" + web3-core-method "1.0.0-beta.34" + web3-core-subscriptions "1.0.0-beta.34" + web3-net "1.0.0-beta.34" + +web3-utils@1.0.0-beta.34: + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" + dependencies: + bn.js "4.11.6" + eth-lib "0.1.27" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randomhex "0.1.5" + underscore "1.8.3" + utf8 "2.1.1" + web3@^0.18.4: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" @@ -4272,6 +5482,18 @@ web3@^0.18.4: xhr2 "*" xmlhttprequest "*" +"web3v1@npm:web3@1.0.0-beta.34": + version "1.0.0-beta.34" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" + dependencies: + web3-bzz "1.0.0-beta.34" + web3-core "1.0.0-beta.34" + web3-eth "1.0.0-beta.34" + web3-eth-personal "1.0.0-beta.34" + web3-net "1.0.0-beta.34" + web3-shh "1.0.0-beta.34" + web3-utils "1.0.0-beta.34" + webpack-addons@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/webpack-addons/-/webpack-addons-1.1.5.tgz#2b178dfe873fb6e75e40a819fa5c26e4a9bc837a" @@ -4309,6 +5531,15 @@ webpack-cli@^2.0.9: yeoman-environment "^2.0.0" yeoman-generator "^2.0.4" +"websocket@git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible": + version "1.0.24" + resolved "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c" + dependencies: + debug "^2.2.0" + nan "^2.3.3" + typedarray-to-buffer "^3.1.2" + yaeti "^0.0.6" + which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" @@ -4362,15 +5593,50 @@ write-file-atomic@^1.2.0: imurmurhash "^0.1.4" slide "^1.1.5" -xhr2@*: +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +xhr-request-promise@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz#343c44d1ee7726b8648069682d0f840c83b4261d" + dependencies: + xhr-request "^1.0.1" + +xhr-request@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2@*, xhr2@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" +xhr@^2.0.4, xhr@^2.3.3: + version "2.4.1" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.1.tgz#ba982cced205ae5eec387169ac9dc77ca4853d38" + dependencies: + global "~4.3.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + xmlhttprequest@*: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" -xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -4378,6 +5644,10 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -4446,6 +5716,13 @@ yargs@~3.10.0: decamelize "^1.0.0" window-size "0.1.0" +yauzl@^2.4.2: + version "2.9.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f" + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.0.1" + yeoman-environment@^2.0.0, yeoman-environment@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.0.6.tgz#ae1b21d826b363f3d637f88a7fc9ea7414cb5377" From c920db82e97f995d45fd9e7cbe64f577d247896d Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Sat, 5 May 2018 10:14:25 +0100 Subject: [PATCH 03/26] first pass impl. for delegatedTransfer - #84 --- contracts/TokenAEur.sol | 4 +- contracts/TxDelegator.sol | 83 +++++++++++++++++ contracts/generic/AugmintToken.sol | 19 +++- .../interfaces/AugmintTokenInterface.sol | 7 +- migrations/5_deploy_TxDelegator.js | 10 +++ migrations/6_deploy_TokenAEur.js | 3 +- migrations/98_add_legacyTokens.js | 3 +- test/delegatedTransfer.js | 89 +++++++++++++++++++ test/helpers/testHelpers.js | 7 +- 9 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 contracts/TxDelegator.sol create mode 100644 migrations/5_deploy_TxDelegator.js create mode 100644 test/delegatedTransfer.js diff --git a/contracts/TokenAEur.sol b/contracts/TokenAEur.sol index 409baa70..c790cb8c 100644 --- a/contracts/TokenAEur.sol +++ b/contracts/TokenAEur.sol @@ -5,8 +5,8 @@ import "./generic/AugmintToken.sol"; contract TokenAEur is AugmintToken { - constructor(TransferFeeInterface _feeAccount) - public AugmintToken("Augmint Crypto Euro", "AEUR", "EUR", 2, _feeAccount) + constructor(address _txDelegator, TransferFeeInterface _feeAccount) + public AugmintToken("Augmint Crypto Euro", "AEUR", "EUR", 2, _txDelegator, _feeAccount) {} // solhint-disable-line no-empty-blocks } diff --git a/contracts/TxDelegator.sol b/contracts/TxDelegator.sol new file mode 100644 index 00000000..ed4e4130 --- /dev/null +++ b/contracts/TxDelegator.sol @@ -0,0 +1,83 @@ +/* + WIP, first pass proof of concept. Implementation will change a lot. + + TODO: + - No point to have this as a separate contract unless: + - we make it generic, i.e. any arbitary calldata can be signed + - make it changeable on AugmintToken + - Maybe reorg some parts to interfaces/abstract contract/lib and use it directly on AugmintToken? + - Double check if we don't need to add network id to signed data: + In addition to being implicitly stored in the augmintTokenaddress (ie. deployment address is unique), + the chain id is also explicitly stored in the v parameter (chain_id = (v - 35) / 2). + - test signing with trezor signature: + https://github.com/0xProject/0x-monorepo/blob/095388ffe05ca51e92db87ba81d6e4f29b1ab087/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol + + - EIP712 & ERC191 signature schemes? +*/ +pragma solidity 0.4.23; + +import "./generic/SafeMath.sol"; +import "./interfaces/AugmintTokenInterface.sol"; + +contract TxDelegator { + using SafeMath for uint256; + mapping(bytes32 => bool) public noncesUsed; + + function delegatedTransfer(AugmintTokenInterface augmintToken, address from, address to, uint amount, string narrative, + uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ + uint maxExecutorFee, /* client provided max fee for executing the tx */ + bytes32 nonce, /* random nonce generated by client */ + /* ^^^^ end of signed data ^^^^ */ + bytes signature, + uint requestedExecutorFee /* the executor can decide to request lower fee */ + ) + external { + require(!noncesUsed[nonce], "nonce already used"); + require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); + require(requestedExecutorFee <= maxExecutorFee, "requestedExecutorFee must be <= maxExecutorFee"); + noncesUsed[nonce] = true; + + bytes32 txHash = keccak256(this, augmintToken, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); + txHash = keccak256("\x19Ethereum Signed Message:\n32", txHash); + + address recovered = recover(txHash, signature); + + require(recovered == from, "invalid signature"); + + require(augmintToken.delegatedTransferExecution(from, to, amount, narrative, requestedExecutorFee), + "delegatedTransferExecution failed"); + + } + + /* from: https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol */ + function recover(bytes32 hash, bytes sig) internal pure returns (address) { + bytes32 r; + bytes32 s; + uint8 v; + + //Check the signature length + if (sig.length != 65) { + return (address(0)); + } + + // Divide the signature in r, s and v variables + assembly { // solhint-disable-line no-inline-assembly + r := mload(add(sig, 32)) + s := mload(add(sig, 64)) + v := byte(0, mload(add(sig, 96))) + } + + // Version of signature should be 27 or 28, but 0 and 1 are also possible versions + if (v < 27) { + v += 27; + } + + // If the version is correct return the signer address + if (v != 27 && v != 28) { + return (address(0)); + } else { + return ecrecover(hash, v, r, s); + } + } + +} diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index d5e6d7ef..3ef75a26 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -4,6 +4,8 @@ * Issues/burns tokens TODO: + - reconsider delegatedTransfer and how to structure it + - shall we allow change of txDelegator? - consider generic bytes arg instead of uint for transferAndNotify - consider separate transfer fee params and calculation to separate contract (to feeAccount?) */ @@ -16,7 +18,8 @@ contract AugmintToken is AugmintTokenInterface { event FeeAccountChanged(TransferFeeInterface newFeeAccount); - constructor(string _name, string _symbol, bytes32 _peggedSymbol, uint8 _decimals, TransferFeeInterface _feeAccount) + constructor(string _name, string _symbol, bytes32 _peggedSymbol, uint8 _decimals, address _txDelegator, + TransferFeeInterface _feeAccount) public { require(_feeAccount != address(0), "feeAccount must be set"); require(bytes(_name).length > 0, "name must be set"); @@ -28,14 +31,26 @@ contract AugmintToken is AugmintTokenInterface { decimals = _decimals; feeAccount = _feeAccount; + txDelegator = _txDelegator; } - function transfer(address to, uint256 amount) external returns (bool) { _transfer(msg.sender, to, amount, ""); return true; } + /* Transfers based on an offline signed transfer instruction. + It trusts txDelegator checked the signature of from account and the executorFee */ + function delegatedTransferExecution(address from, address to, uint amount, string narrative, uint executorFee) + external returns(bool) { + require(msg.sender == txDelegator); + + _transfer(from, msg.sender, executorFee, "Delegated execution fee"); + _transfer(from, to, amount, narrative); + + return true; + } + function approve(address _spender, uint256 amount) external returns (bool) { require(_spender != 0x0, "spender must be set"); allowed[msg.sender][_spender] = amount; diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index 9adb69fb..7bea7f47 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -26,10 +26,11 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { mapping(address => mapping (address => uint256)) public allowed; // allowances added with approve() TransferFeeInterface public feeAccount; + address public txDelegator; event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax); event Transfer(address indexed from, address indexed to, uint amount); - event AugmintTransfer(address indexed from, address indexed to, uint amount, string narrative, uint fee); + event AugmintTransfer(address from, address to, uint amount, string narrative, uint fee); event TokenIssued(uint amount); event TokenBurned(uint amount); event Approval(address indexed _owner, address indexed _spender, uint256 _value); @@ -37,6 +38,10 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { function transfer(address to, uint value) external returns (bool); // solhint-disable-line no-simple-event-func-name function transferFrom(address from, address to, uint value) external returns (bool); function approve(address spender, uint value) external returns (bool); + + function delegatedTransferExecution(address from, address to, uint amount, string narrative, uint executorFee) + external returns(bool); + function increaseApproval(address spender, uint addedValue) external returns (bool); function decreaseApproval(address spender, uint subtractedValue) external returns (bool); diff --git a/migrations/5_deploy_TxDelegator.js b/migrations/5_deploy_TxDelegator.js new file mode 100644 index 00000000..974ff0d7 --- /dev/null +++ b/migrations/5_deploy_TxDelegator.js @@ -0,0 +1,10 @@ +const TxDelegator = artifacts.require("./TxDelegator.sol"); +const FeeAccount = artifacts.require("./FeeAccount.sol"); + +module.exports = function(deployer) { + deployer.deploy(TxDelegator); + deployer.then(async () => { + const feeAccount = FeeAccount.at(FeeAccount.address); + await feeAccount.grantPermission(TxDelegator.address, "NoFeeTransferContracts"); + }); +}; diff --git a/migrations/6_deploy_TokenAEur.js b/migrations/6_deploy_TokenAEur.js index 87b92baf..bf01cb16 100644 --- a/migrations/6_deploy_TokenAEur.js +++ b/migrations/6_deploy_TokenAEur.js @@ -1,9 +1,10 @@ const SafeMath = artifacts.require("./SafeMath.sol"); const TokenAEur = artifacts.require("./TokenAEur.sol"); const FeeAccount = artifacts.require("./FeeAccount.sol"); +const TxDelegator = artifacts.require("./TxDelegator.sol"); module.exports = function(deployer) { deployer.link(SafeMath, TokenAEur); - deployer.deploy(TokenAEur, FeeAccount.address); + deployer.deploy(TokenAEur, TxDelegator.address, FeeAccount.address); }; diff --git a/migrations/98_add_legacyTokens.js b/migrations/98_add_legacyTokens.js index 5047dabd..fcc0f6c8 100644 --- a/migrations/98_add_legacyTokens.js +++ b/migrations/98_add_legacyTokens.js @@ -1,12 +1,13 @@ const FeeAccount = artifacts.require("./FeeAccount.sol"); const TokenAEur = artifacts.require("./TokenAEur.sol"); const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); +const TxDelegator = artifacts.require("./TxDelegator.sol"); module.exports = async function(deployer, network, accounts) { deployer.then(async () => { const monetarySupervisor = MonetarySupervisor.at(MonetarySupervisor.address); const feeAccount = FeeAccount.at(FeeAccount.address); - const oldToken = await TokenAEur.new(FeeAccount.address); + const oldToken = await TokenAEur.new(TxDelegator.address, FeeAccount.address); await Promise.all([ oldToken.grantPermission(accounts[0], "MonetarySupervisorContract"), // "hack" for test to issue diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js new file mode 100644 index 00000000..aef5125c --- /dev/null +++ b/test/delegatedTransfer.js @@ -0,0 +1,89 @@ +const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); +const testHelpers = require("./helpers/testHelpers.js"); +const TxDelegator = artifacts.require("TxDelegator.sol"); +const TokenAEur = artifacts.require("TokenAEur.sol"); + +let txDelegator; +let tokenAEur; +let from; + +contract("TxDelegator", accounts => { + before(async () => { + from = accounts[1]; + tokenAEur = tokenTestHelpers.augmintToken; + txDelegator = new global.web3v1.eth.Contract(TxDelegator.abi, TxDelegator.address); + // txDelegator = TxDelegator.at(TxDelegator.address); + }); + + it("should delegatedTransfer function signed", async function() { + await tokenTestHelpers.issueToReserve(1000000000); + await tokenTestHelpers.withdrawFromReserve(from, 500000000); + + // params sent and signed by client + const to = accounts[2]; + const amount = 1000; + const narrative = "here we go"; + const minGasPrice = 1; + const maxExecutorFee = 200; + const nonce = "0x0000000000000000000000000000000000000000000000000000000000000001"; // to be a random hash with proper entrophy + + // executor params + const txSender = accounts[3]; + const actualGasPrice = minGasPrice; + const requestedExecutorFee = maxExecutorFee; + + let txHash; + + if (narrative === "") { + // workaround b/c solidity keccak256 results different txHAsh with empty string than web3 + txHash = global.web3v1.utils.soliditySha3( + TxDelegator.address, + tokenAEur.address, + from, + to, + amount, + minGasPrice, + maxExecutorFee, + nonce + ); + } else { + txHash = global.web3v1.utils.soliditySha3( + TxDelegator.address, + tokenAEur.address, + from, + to, + amount, + narrative, + minGasPrice, + maxExecutorFee, + nonce + ); + } + + const signature = await global.web3v1.eth.sign(txHash, from); + + const tx = await txDelegator.methods + .delegatedTransfer( + tokenAEur.address, + from, + to, + amount, + narrative, + minGasPrice, + maxExecutorFee, + nonce, + signature, + requestedExecutorFee + ) + .send({ from: txSender, gas: 1200000, gasPrice: actualGasPrice }); + testHelpers.logGasUse(this, tx, "delegatedTransfer"); + + // TODO: assert events & balances + }); + + it("should not execute with the same nonce twice"); + + it("should not execute with higher requestedExecutorFee than signed"); + + it("should not execute with lower gasPrice than signed"); +}); diff --git a/test/helpers/testHelpers.js b/test/helpers/testHelpers.js index 9293d8f7..9b4414a4 100644 --- a/test/helpers/testHelpers.js +++ b/test/helpers/testHelpers.js @@ -195,7 +195,12 @@ function revertSnapshot(snapshotId) { function logGasUse(testObj, tx, txName) { if (!gasUseLogDisabled) { - gasUseLog.push([testObj.test.parent.title, testObj.test.fullTitle(), txName || "", tx.receipt.gasUsed]); + gasUseLog.push([ + testObj.test.parent.title, + testObj.test.fullTitle(), + txName || "", + tx.receipt ? tx.receipt.gasUsed : tx.gasUsed /* web3v0 w/ receipt, v1 w/o */ + ]); } } From 186178f9f0972334cec52a909d37a679a377854a Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Sat, 5 May 2018 10:34:39 +0100 Subject: [PATCH 04/26] AugmintToken tests catch up with new TxDelegator --- test/token.js | 4 ++++ test/tokenConversion.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/token.js b/test/token.js index c62c1498..2446b7cd 100644 --- a/test/token.js +++ b/test/token.js @@ -1,6 +1,7 @@ const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); const testHelpers = require("./helpers/testHelpers.js"); const AugmintToken = artifacts.require("./generic/AugmintToken.sol"); +const TxDelegator = artifacts.require("TxDelegator.sol"); let augmintToken; @@ -16,6 +17,7 @@ contract("AugmintToken tests", accounts => { "AEUR", // symbol "EUR", // peggedSymbol 2, // decimals + TxDelegator.address, 0 // feeaccount ) ); @@ -28,6 +30,7 @@ contract("AugmintToken tests", accounts => { "AEUR", // symbol "EUR", // peggedSymbol 2, // decimals + TxDelegator.address, tokenTestHelpers.feeAccount.address ) ); @@ -40,6 +43,7 @@ contract("AugmintToken tests", accounts => { "", // symbol "EUR", // peggedSymbol 2, // decimals + TxDelegator.address, tokenTestHelpers.feeAccount.address ) ); diff --git a/test/tokenConversion.js b/test/tokenConversion.js index 668cb6da..97702263 100644 --- a/test/tokenConversion.js +++ b/test/tokenConversion.js @@ -2,6 +2,7 @@ const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); const testHelpers = require("./helpers/testHelpers.js"); const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); const AugmintToken = artifacts.require("./TokenAEur.sol"); +const TxDelegator = artifacts.require("TxDelegator.sol"); let augmintToken = null; let monetarySupervisor = null; @@ -14,7 +15,7 @@ contract("token conversion tests", accounts => { monetarySupervisor = tokenTestHelpers.monetarySupervisor; [newToken] = await Promise.all([ - AugmintToken.new(tokenTestHelpers.feeAccount.address), + AugmintToken.new(TxDelegator.address, tokenTestHelpers.feeAccount.address), tokenTestHelpers.issueToReserve(10000000) ]); From f546913baa66ae656a70d8985a9a391c363705ac Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 15:46:32 +0100 Subject: [PATCH 05/26] no need to deploy SafeMath it's part of each contract using it --- migrations/2_deploy_Libs.js | 5 ----- migrations/{3_deploy_Rates.js => 2_deploy_Rates.js} | 0 .../{4_deploy_FeeAccount.js => 3_deploy_FeeAccount.js} | 0 ...deploy_AugmintReserves.js => 4_deploy_AugmintReserves.js} | 0 migrations/6_deploy_TokenAEur.js | 3 --- 5 files changed, 8 deletions(-) delete mode 100644 migrations/2_deploy_Libs.js rename migrations/{3_deploy_Rates.js => 2_deploy_Rates.js} (100%) rename migrations/{4_deploy_FeeAccount.js => 3_deploy_FeeAccount.js} (100%) rename migrations/{5_deploy_AugmintReserves.js => 4_deploy_AugmintReserves.js} (100%) diff --git a/migrations/2_deploy_Libs.js b/migrations/2_deploy_Libs.js deleted file mode 100644 index 7c39615f..00000000 --- a/migrations/2_deploy_Libs.js +++ /dev/null @@ -1,5 +0,0 @@ -var SafeMath = artifacts.require("./SafeMath.sol"); - -module.exports = function(deployer) { - deployer.deploy(SafeMath); -}; diff --git a/migrations/3_deploy_Rates.js b/migrations/2_deploy_Rates.js similarity index 100% rename from migrations/3_deploy_Rates.js rename to migrations/2_deploy_Rates.js diff --git a/migrations/4_deploy_FeeAccount.js b/migrations/3_deploy_FeeAccount.js similarity index 100% rename from migrations/4_deploy_FeeAccount.js rename to migrations/3_deploy_FeeAccount.js diff --git a/migrations/5_deploy_AugmintReserves.js b/migrations/4_deploy_AugmintReserves.js similarity index 100% rename from migrations/5_deploy_AugmintReserves.js rename to migrations/4_deploy_AugmintReserves.js diff --git a/migrations/6_deploy_TokenAEur.js b/migrations/6_deploy_TokenAEur.js index bf01cb16..c9ebfd66 100644 --- a/migrations/6_deploy_TokenAEur.js +++ b/migrations/6_deploy_TokenAEur.js @@ -1,10 +1,7 @@ -const SafeMath = artifacts.require("./SafeMath.sol"); const TokenAEur = artifacts.require("./TokenAEur.sol"); const FeeAccount = artifacts.require("./FeeAccount.sol"); const TxDelegator = artifacts.require("./TxDelegator.sol"); module.exports = function(deployer) { - deployer.link(SafeMath, TokenAEur); - deployer.deploy(TokenAEur, TxDelegator.address, FeeAccount.address); }; From 20857c8ad78fe86de8e95dbcc72e6b829e77a7d0 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 16:39:05 +0100 Subject: [PATCH 06/26] order of migration steps - deploy InterestEarnedAcc before TokenAEur --- ...InterestEarnedAccount.js => 6_deploy_InterestEarnedAccount.js} | 0 migrations/{6_deploy_TokenAEur.js => 7_deploy_TokenAEur.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename migrations/{7_deploy_InterestEarnedAccount.js => 6_deploy_InterestEarnedAccount.js} (100%) rename migrations/{6_deploy_TokenAEur.js => 7_deploy_TokenAEur.js} (100%) diff --git a/migrations/7_deploy_InterestEarnedAccount.js b/migrations/6_deploy_InterestEarnedAccount.js similarity index 100% rename from migrations/7_deploy_InterestEarnedAccount.js rename to migrations/6_deploy_InterestEarnedAccount.js diff --git a/migrations/6_deploy_TokenAEur.js b/migrations/7_deploy_TokenAEur.js similarity index 100% rename from migrations/6_deploy_TokenAEur.js rename to migrations/7_deploy_TokenAEur.js From 168e75b100109043b935bfd005857805e2e82649 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 16:46:17 +0100 Subject: [PATCH 07/26] move recover fx to a ECRecovery lib --- contracts/TxDelegator.sol | 35 +-------------- contracts/generic/ECRecovery.sol | 76 ++++++++++++++++++++++++++++++++ test/delegatedTransfer.js | 4 +- 3 files changed, 80 insertions(+), 35 deletions(-) create mode 100644 contracts/generic/ECRecovery.sol diff --git a/contracts/TxDelegator.sol b/contracts/TxDelegator.sol index ed4e4130..fb87e941 100644 --- a/contracts/TxDelegator.sol +++ b/contracts/TxDelegator.sol @@ -17,6 +17,7 @@ pragma solidity 0.4.23; import "./generic/SafeMath.sol"; +import "./generic/ECRecovery.sol"; import "./interfaces/AugmintTokenInterface.sol"; contract TxDelegator { @@ -38,9 +39,8 @@ contract TxDelegator { noncesUsed[nonce] = true; bytes32 txHash = keccak256(this, augmintToken, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); - txHash = keccak256("\x19Ethereum Signed Message:\n32", txHash); - address recovered = recover(txHash, signature); + address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); require(recovered == from, "invalid signature"); @@ -49,35 +49,4 @@ contract TxDelegator { } - /* from: https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol */ - function recover(bytes32 hash, bytes sig) internal pure returns (address) { - bytes32 r; - bytes32 s; - uint8 v; - - //Check the signature length - if (sig.length != 65) { - return (address(0)); - } - - // Divide the signature in r, s and v variables - assembly { // solhint-disable-line no-inline-assembly - r := mload(add(sig, 32)) - s := mload(add(sig, 64)) - v := byte(0, mload(add(sig, 96))) - } - - // Version of signature should be 27 or 28, but 0 and 1 are also possible versions - if (v < 27) { - v += 27; - } - - // If the version is correct return the signer address - if (v != 27 && v != 28) { - return (address(0)); - } else { - return ecrecover(hash, v, r, s); - } - } - } diff --git a/contracts/generic/ECRecovery.sol b/contracts/generic/ECRecovery.sol new file mode 100644 index 00000000..66047715 --- /dev/null +++ b/contracts/generic/ECRecovery.sol @@ -0,0 +1,76 @@ +pragma solidity 0.4.23; + + +/** + * @title Eliptic curve signature operations + * + * @dev Based on https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol + * + * TODO Remove this library once solidity supports passing a signature to ecrecover. + * See https://github.com/ethereum/solidity/issues/864 + * + */ + +library ECRecovery { + + /** + * @dev Recover signer address from a message by using their signature + * @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address. + * @param sig bytes signature, the signature is generated using web3.eth.sign() + */ + function recover(bytes32 hash, bytes sig) + internal + pure + returns (address) + { + bytes32 r; + bytes32 s; + uint8 v; + + // Check the signature length + if (sig.length != 65) { + return (address(0)); + } + + // Divide the signature in r, s and v variables + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + // solium-disable-next-line security/no-inline-assembly + assembly { + r := mload(add(sig, 32)) + s := mload(add(sig, 64)) + v := byte(0, mload(add(sig, 96))) + } + + // Version of signature should be 27 or 28, but 0 and 1 are also possible versions + if (v < 27) { + v += 27; + } + + // If the version is correct return the signer address + if (v != 27 && v != 28) { + return (address(0)); + } else { + // solium-disable-next-line arg-overflow + return ecrecover(hash, v, r, s); + } + } + + /** + * toEthSignedMessageHash + * @dev prefix a bytes32 value with "\x19Ethereum Signed Message:" + * @dev and hash the result + */ + function toEthSignedMessageHash(bytes32 hash) + internal + pure + returns (bytes32) + { + // 32 is the length in bytes of hash, + // enforced by the type signature above + return keccak256( + "\x19Ethereum Signed Message:\n32", + hash + ); + } +} diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js index aef5125c..451825ff 100644 --- a/test/delegatedTransfer.js +++ b/test/delegatedTransfer.js @@ -13,11 +13,11 @@ contract("TxDelegator", accounts => { tokenAEur = tokenTestHelpers.augmintToken; txDelegator = new global.web3v1.eth.Contract(TxDelegator.abi, TxDelegator.address); // txDelegator = TxDelegator.at(TxDelegator.address); + await tokenTestHelpers.issueToReserve(1000000000); + await tokenTestHelpers.withdrawFromReserve(from, 1000000000); }); it("should delegatedTransfer function signed", async function() { - await tokenTestHelpers.issueToReserve(1000000000); - await tokenTestHelpers.withdrawFromReserve(from, 500000000); // params sent and signed by client const to = accounts[2]; From ac15a74257c97fd79c31d41f1519d56933d4418d Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 17:22:38 +0100 Subject: [PATCH 08/26] get rid of sep. TxDelegator, fx in AugmintToken + ECRecovery lib --- contracts/TokenAEur.sol | 4 +- contracts/TxDelegator.sol | 52 ------------------- contracts/generic/AugmintToken.sol | 31 ++++++++--- .../interfaces/AugmintTokenInterface.sol | 12 +++-- migrations/5_deploy_TxDelegator.js | 10 ---- migrations/7_deploy_TokenAEur.js | 3 +- migrations/98_add_legacyTokens.js | 3 +- test/delegatedTransfer.js | 18 +++---- test/token.js | 4 -- test/tokenConversion.js | 3 +- 10 files changed, 43 insertions(+), 97 deletions(-) delete mode 100644 contracts/TxDelegator.sol delete mode 100644 migrations/5_deploy_TxDelegator.js diff --git a/contracts/TokenAEur.sol b/contracts/TokenAEur.sol index c790cb8c..409baa70 100644 --- a/contracts/TokenAEur.sol +++ b/contracts/TokenAEur.sol @@ -5,8 +5,8 @@ import "./generic/AugmintToken.sol"; contract TokenAEur is AugmintToken { - constructor(address _txDelegator, TransferFeeInterface _feeAccount) - public AugmintToken("Augmint Crypto Euro", "AEUR", "EUR", 2, _txDelegator, _feeAccount) + constructor(TransferFeeInterface _feeAccount) + public AugmintToken("Augmint Crypto Euro", "AEUR", "EUR", 2, _feeAccount) {} // solhint-disable-line no-empty-blocks } diff --git a/contracts/TxDelegator.sol b/contracts/TxDelegator.sol deleted file mode 100644 index fb87e941..00000000 --- a/contracts/TxDelegator.sol +++ /dev/null @@ -1,52 +0,0 @@ -/* - WIP, first pass proof of concept. Implementation will change a lot. - - TODO: - - No point to have this as a separate contract unless: - - we make it generic, i.e. any arbitary calldata can be signed - - make it changeable on AugmintToken - - Maybe reorg some parts to interfaces/abstract contract/lib and use it directly on AugmintToken? - - Double check if we don't need to add network id to signed data: - In addition to being implicitly stored in the augmintTokenaddress (ie. deployment address is unique), - the chain id is also explicitly stored in the v parameter (chain_id = (v - 35) / 2). - - test signing with trezor signature: - https://github.com/0xProject/0x-monorepo/blob/095388ffe05ca51e92db87ba81d6e4f29b1ab087/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol - - - EIP712 & ERC191 signature schemes? -*/ -pragma solidity 0.4.23; - -import "./generic/SafeMath.sol"; -import "./generic/ECRecovery.sol"; -import "./interfaces/AugmintTokenInterface.sol"; - -contract TxDelegator { - using SafeMath for uint256; - mapping(bytes32 => bool) public noncesUsed; - - function delegatedTransfer(AugmintTokenInterface augmintToken, address from, address to, uint amount, string narrative, - uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ - uint maxExecutorFee, /* client provided max fee for executing the tx */ - bytes32 nonce, /* random nonce generated by client */ - /* ^^^^ end of signed data ^^^^ */ - bytes signature, - uint requestedExecutorFee /* the executor can decide to request lower fee */ - ) - external { - require(!noncesUsed[nonce], "nonce already used"); - require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); - require(requestedExecutorFee <= maxExecutorFee, "requestedExecutorFee must be <= maxExecutorFee"); - noncesUsed[nonce] = true; - - bytes32 txHash = keccak256(this, augmintToken, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); - - address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); - - require(recovered == from, "invalid signature"); - - require(augmintToken.delegatedTransferExecution(from, to, amount, narrative, requestedExecutorFee), - "delegatedTransferExecution failed"); - - } - -} diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index 3ef75a26..b5745d28 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -11,6 +11,7 @@ */ pragma solidity ^0.4.23; import "../interfaces/AugmintTokenInterface.sol"; +import "./ECRecovery.sol"; import "../interfaces/TransferFeeInterface.sol"; @@ -18,8 +19,7 @@ contract AugmintToken is AugmintTokenInterface { event FeeAccountChanged(TransferFeeInterface newFeeAccount); - constructor(string _name, string _symbol, bytes32 _peggedSymbol, uint8 _decimals, address _txDelegator, - TransferFeeInterface _feeAccount) + constructor(string _name, string _symbol, bytes32 _peggedSymbol, uint8 _decimals, TransferFeeInterface _feeAccount) public { require(_feeAccount != address(0), "feeAccount must be set"); require(bytes(_name).length > 0, "name must be set"); @@ -31,7 +31,6 @@ contract AugmintToken is AugmintTokenInterface { decimals = _decimals; feeAccount = _feeAccount; - txDelegator = _txDelegator; } function transfer(address to, uint256 amount) external returns (bool) { @@ -39,13 +38,29 @@ contract AugmintToken is AugmintTokenInterface { return true; } - /* Transfers based on an offline signed transfer instruction. - It trusts txDelegator checked the signature of from account and the executorFee */ - function delegatedTransferExecution(address from, address to, uint amount, string narrative, uint executorFee) + /* Transfers based on an offline signed transfer instruction. */ + function delegatedTransfer(address from, address to, uint amount, string narrative, + uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ + uint maxExecutorFee, /* client provided max fee for executing the tx */ + bytes32 nonce, /* random nonce generated by client */ + /* ^^^^ end of signed data ^^^^ */ + bytes signature, + uint requestedExecutorFee /* the executor can decide to request lower fee */ + ) external returns(bool) { - require(msg.sender == txDelegator); - _transfer(from, msg.sender, executorFee, "Delegated execution fee"); + require(!noncesUsed[nonce], "nonce already used"); + require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); + require(requestedExecutorFee <= maxExecutorFee, "requestedExecutorFee must be <= maxExecutorFee"); + noncesUsed[nonce] = true; + + bytes32 txHash = keccak256(this, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); + + address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); + + require(recovered == from, "invalid signature"); + + _transfer(from, msg.sender, requestedExecutorFee, "Delegated execution fee"); _transfer(from, to, amount, narrative); return true; diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index 7bea7f47..10d43488 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -26,7 +26,7 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { mapping(address => mapping (address => uint256)) public allowed; // allowances added with approve() TransferFeeInterface public feeAccount; - address public txDelegator; + mapping(bytes32 => bool) public noncesUsed; // record nonces used by delegatedTransfer event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax); event Transfer(address indexed from, address indexed to, uint amount); @@ -39,8 +39,14 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { function transferFrom(address from, address to, uint value) external returns (bool); function approve(address spender, uint value) external returns (bool); - function delegatedTransferExecution(address from, address to, uint amount, string narrative, uint executorFee) - external returns(bool); + function delegatedTransfer(address from, address to, uint amount, string narrative, + uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ + uint maxExecutorFee, /* client provided max fee for executing the tx */ + bytes32 nonce, /* random nonce generated by client */ + /* ^^^^ end of signed data ^^^^ */ + bytes signature, + uint requestedExecutorFee /* the executor can decide to request lower fee */ + ) external returns(bool); function increaseApproval(address spender, uint addedValue) external returns (bool); function decreaseApproval(address spender, uint subtractedValue) external returns (bool); diff --git a/migrations/5_deploy_TxDelegator.js b/migrations/5_deploy_TxDelegator.js deleted file mode 100644 index 974ff0d7..00000000 --- a/migrations/5_deploy_TxDelegator.js +++ /dev/null @@ -1,10 +0,0 @@ -const TxDelegator = artifacts.require("./TxDelegator.sol"); -const FeeAccount = artifacts.require("./FeeAccount.sol"); - -module.exports = function(deployer) { - deployer.deploy(TxDelegator); - deployer.then(async () => { - const feeAccount = FeeAccount.at(FeeAccount.address); - await feeAccount.grantPermission(TxDelegator.address, "NoFeeTransferContracts"); - }); -}; diff --git a/migrations/7_deploy_TokenAEur.js b/migrations/7_deploy_TokenAEur.js index c9ebfd66..725d3802 100644 --- a/migrations/7_deploy_TokenAEur.js +++ b/migrations/7_deploy_TokenAEur.js @@ -1,7 +1,6 @@ const TokenAEur = artifacts.require("./TokenAEur.sol"); const FeeAccount = artifacts.require("./FeeAccount.sol"); -const TxDelegator = artifacts.require("./TxDelegator.sol"); module.exports = function(deployer) { - deployer.deploy(TokenAEur, TxDelegator.address, FeeAccount.address); + deployer.deploy(TokenAEur, FeeAccount.address); }; diff --git a/migrations/98_add_legacyTokens.js b/migrations/98_add_legacyTokens.js index fcc0f6c8..5047dabd 100644 --- a/migrations/98_add_legacyTokens.js +++ b/migrations/98_add_legacyTokens.js @@ -1,13 +1,12 @@ const FeeAccount = artifacts.require("./FeeAccount.sol"); const TokenAEur = artifacts.require("./TokenAEur.sol"); const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); -const TxDelegator = artifacts.require("./TxDelegator.sol"); module.exports = async function(deployer, network, accounts) { deployer.then(async () => { const monetarySupervisor = MonetarySupervisor.at(MonetarySupervisor.address); const feeAccount = FeeAccount.at(FeeAccount.address); - const oldToken = await TokenAEur.new(TxDelegator.address, FeeAccount.address); + const oldToken = await TokenAEur.new(FeeAccount.address); await Promise.all([ oldToken.grantPermission(accounts[0], "MonetarySupervisorContract"), // "hack" for test to issue diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js index 451825ff..468c9deb 100644 --- a/test/delegatedTransfer.js +++ b/test/delegatedTransfer.js @@ -1,24 +1,21 @@ const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); const testHelpers = require("./helpers/testHelpers.js"); -const TxDelegator = artifacts.require("TxDelegator.sol"); const TokenAEur = artifacts.require("TokenAEur.sol"); let txDelegator; let tokenAEur; let from; -contract("TxDelegator", accounts => { +contract("Delegated Transfers", accounts => { before(async () => { from = accounts[1]; - tokenAEur = tokenTestHelpers.augmintToken; - txDelegator = new global.web3v1.eth.Contract(TxDelegator.abi, TxDelegator.address); + tokenAEur = new global.web3v1.eth.Contract(TokenAEur.abi, TokenAEur.address); // txDelegator = TxDelegator.at(TxDelegator.address); await tokenTestHelpers.issueToReserve(1000000000); await tokenTestHelpers.withdrawFromReserve(from, 1000000000); }); - it("should delegatedTransfer function signed", async function() { - + it("should transfer when delegatedTransfer is signed", async function() { // params sent and signed by client const to = accounts[2]; const amount = 1000; @@ -37,8 +34,7 @@ contract("TxDelegator", accounts => { if (narrative === "") { // workaround b/c solidity keccak256 results different txHAsh with empty string than web3 txHash = global.web3v1.utils.soliditySha3( - TxDelegator.address, - tokenAEur.address, + TokenAEur.address, from, to, amount, @@ -48,8 +44,7 @@ contract("TxDelegator", accounts => { ); } else { txHash = global.web3v1.utils.soliditySha3( - TxDelegator.address, - tokenAEur.address, + TokenAEur.address, from, to, amount, @@ -62,9 +57,8 @@ contract("TxDelegator", accounts => { const signature = await global.web3v1.eth.sign(txHash, from); - const tx = await txDelegator.methods + const tx = await tokenAEur.methods .delegatedTransfer( - tokenAEur.address, from, to, amount, diff --git a/test/token.js b/test/token.js index 2446b7cd..c62c1498 100644 --- a/test/token.js +++ b/test/token.js @@ -1,7 +1,6 @@ const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); const testHelpers = require("./helpers/testHelpers.js"); const AugmintToken = artifacts.require("./generic/AugmintToken.sol"); -const TxDelegator = artifacts.require("TxDelegator.sol"); let augmintToken; @@ -17,7 +16,6 @@ contract("AugmintToken tests", accounts => { "AEUR", // symbol "EUR", // peggedSymbol 2, // decimals - TxDelegator.address, 0 // feeaccount ) ); @@ -30,7 +28,6 @@ contract("AugmintToken tests", accounts => { "AEUR", // symbol "EUR", // peggedSymbol 2, // decimals - TxDelegator.address, tokenTestHelpers.feeAccount.address ) ); @@ -43,7 +40,6 @@ contract("AugmintToken tests", accounts => { "", // symbol "EUR", // peggedSymbol 2, // decimals - TxDelegator.address, tokenTestHelpers.feeAccount.address ) ); diff --git a/test/tokenConversion.js b/test/tokenConversion.js index 97702263..668cb6da 100644 --- a/test/tokenConversion.js +++ b/test/tokenConversion.js @@ -2,7 +2,6 @@ const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); const testHelpers = require("./helpers/testHelpers.js"); const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); const AugmintToken = artifacts.require("./TokenAEur.sol"); -const TxDelegator = artifacts.require("TxDelegator.sol"); let augmintToken = null; let monetarySupervisor = null; @@ -15,7 +14,7 @@ contract("token conversion tests", accounts => { monetarySupervisor = tokenTestHelpers.monetarySupervisor; [newToken] = await Promise.all([ - AugmintToken.new(TxDelegator.address, tokenTestHelpers.feeAccount.address), + AugmintToken.new(tokenTestHelpers.feeAccount.address), tokenTestHelpers.issueToReserve(10000000) ]); From 73fa0144c985745e103852cfd80e0488871e826f Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 21:12:21 +0100 Subject: [PATCH 09/26] minor test assertion fix in fee calc for NoFeeTransferContracts --- test/helpers/tokenTestHelpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/tokenTestHelpers.js b/test/helpers/tokenTestHelpers.js index de02c3ef..126a539e 100644 --- a/test/helpers/tokenTestHelpers.js +++ b/test/helpers/tokenTestHelpers.js @@ -194,7 +194,7 @@ async function transferFromTest(testInstance, expTransfer) { async function getTransferFee(transfer) { const [fromAllowed, toAllowed] = await Promise.all([ feeAccount.permissions(transfer.from, "NoFeeTransferContracts"), - feeAccount.permissions(transfer.from, "NoFeeTransferContracts") + feeAccount.permissions(transfer.to, "NoFeeTransferContracts") ]); if (fromAllowed || toAllowed) { return 0; From a2f95827f7cff667a30a6e6b313ac80247d0e481 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 21:12:53 +0100 Subject: [PATCH 10/26] balance tests for delegatedTransfers + nonce test --- test/delegatedTransfer.js | 204 +++++++++++++++++++++++++++----------- 1 file changed, 144 insertions(+), 60 deletions(-) diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js index 468c9deb..98bb6b90 100644 --- a/test/delegatedTransfer.js +++ b/test/delegatedTransfer.js @@ -1,83 +1,167 @@ const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); const testHelpers = require("./helpers/testHelpers.js"); const TokenAEur = artifacts.require("TokenAEur.sol"); +const FeeAccount = artifacts.require("FeeAccount.sol"); + +const DELEGATED_TRANSFER_MAX_GAS = 120000; -let txDelegator; let tokenAEur; let from; +const signDelegatedTransfer = async clientParams => { + let txHash; + + if (clientParams.narrative === "") { + // workaround b/c solidity keccak256 results different txHAsh with empty string than web3 + txHash = global.web3v1.utils.soliditySha3( + clientParams.tokenAEurAddress, + clientParams.from, + clientParams.to, + clientParams.amount, + clientParams.minGasPrice, + clientParams.maxExecutorFee, + clientParams.nonce + ); + } else { + txHash = global.web3v1.utils.soliditySha3( + clientParams.tokenAEurAddress, + clientParams.from, + clientParams.to, + clientParams.amount, + clientParams.narrative, + clientParams.minGasPrice, + clientParams.maxExecutorFee, + clientParams.nonce + ); + } + + const signature = await global.web3v1.eth.sign(txHash, clientParams.from); + + return signature; +}; + +const sendDelegatedTransfer = async (testInstance, clientParams, signature, executorParams) => { + clientParams.fee = await tokenTestHelpers.getTransferFee(clientParams); + clientParams.executorFeeTransferFee = await tokenTestHelpers.getTransferFee({ + from: clientParams.from, + to: clientParams.to, + amount: clientParams.fee + }); + + const balBefore = await tokenTestHelpers.getAllBalances({ + from: clientParams.from, + to: clientParams.to, + executor: executorParams.executorAddress, + feeAccount: FeeAccount.address + }); + + const tx = await tokenAEur.methods + .delegatedTransfer( + clientParams.from, + clientParams.to, + clientParams.amount, + clientParams.narrative, + clientParams.minGasPrice, + clientParams.maxExecutorFee, + clientParams.nonce, + signature, + executorParams.requestedExecutorFee + ) + .send({ from: executorParams.executorAddress, gas: 1200000, gasPrice: executorParams.actualGasPrice }); + testHelpers.logGasUse(testInstance, tx, "delegatedTransfer"); + + // TODO: assert events + + await tokenTestHelpers.assertBalances(balBefore, { + from: { + ace: balBefore.from.ace + .sub(clientParams.amount) + .sub(clientParams.fee) + .sub(clientParams.executorFeeTransferFee) + .sub(executorParams.requestedExecutorFee), + eth: balBefore.from.eth + }, + to: { + ace: balBefore.to.ace.add(clientParams.amount), + eth: balBefore.to.eth + }, + feeAccount: { + ace: balBefore.feeAccount.ace.add(clientParams.fee).add(clientParams.executorFeeTransferFee), + eth: balBefore.feeAccount.eth + }, + executor: { + ace: balBefore.executor.ace.add(executorParams.requestedExecutorFee), + gasFee: testHelpers.GAS_PRICE * DELEGATED_TRANSFER_MAX_GAS + } + }); + + return tx; +}; + contract("Delegated Transfers", accounts => { before(async () => { from = accounts[1]; tokenAEur = new global.web3v1.eth.Contract(TokenAEur.abi, TokenAEur.address); - // txDelegator = TxDelegator.at(TxDelegator.address); - await tokenTestHelpers.issueToReserve(1000000000); - await tokenTestHelpers.withdrawFromReserve(from, 1000000000); + + await tokenTestHelpers.issueToReserve(100000); + await tokenTestHelpers.withdrawFromReserve(from, 100000); }); it("should transfer when delegatedTransfer is signed", async function() { // params sent and signed by client - const to = accounts[2]; - const amount = 1000; - const narrative = "here we go"; - const minGasPrice = 1; - const maxExecutorFee = 200; - const nonce = "0x0000000000000000000000000000000000000000000000000000000000000001"; // to be a random hash with proper entrophy - - // executor params - const txSender = accounts[3]; - const actualGasPrice = minGasPrice; - const requestedExecutorFee = maxExecutorFee; - - let txHash; - - if (narrative === "") { - // workaround b/c solidity keccak256 results different txHAsh with empty string than web3 - txHash = global.web3v1.utils.soliditySha3( - TokenAEur.address, - from, - to, - amount, - minGasPrice, - maxExecutorFee, - nonce - ); - } else { - txHash = global.web3v1.utils.soliditySha3( - TokenAEur.address, - from, - to, - amount, - narrative, - minGasPrice, - maxExecutorFee, - nonce - ); - } + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + to: accounts[2], + amount: 1000, + narrative: "here we go", + minGasPrice: 1, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000001" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee + }; - const signature = await global.web3v1.eth.sign(txHash, from); - - const tx = await tokenAEur.methods - .delegatedTransfer( - from, - to, - amount, - narrative, - minGasPrice, - maxExecutorFee, - nonce, - signature, - requestedExecutorFee - ) - .send({ from: txSender, gas: 1200000, gasPrice: actualGasPrice }); - testHelpers.logGasUse(this, tx, "delegatedTransfer"); - - // TODO: assert events & balances + const signature = await signDelegatedTransfer(clientParams); + + await sendDelegatedTransfer(this, clientParams, signature, executorParams); }); - it("should not execute with the same nonce twice"); + it("should not execute with the same nonce twice", async function() { + // params sent and signed by client + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + to: accounts[2], + amount: 1000, + narrative: "here we go", + minGasPrice: 2, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000002" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee - 1 + }; + + const signature = await signDelegatedTransfer(clientParams); + + await sendDelegatedTransfer(this, clientParams, signature, executorParams); + + await testHelpers.expectThrow(sendDelegatedTransfer(this, clientParams, signature, executorParams)); + }); + + it("should execute with lower requestedExecutorFee than signed"); it("should not execute with higher requestedExecutorFee than signed"); + it("should execute with higher gasPrice than signed"); + it("should not execute with lower gasPrice than signed"); }); From 2cacd0551c02b3bcfcc188dfd44a82616ccde788 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 21:16:44 +0100 Subject: [PATCH 11/26] set AugmintTransfer event to & from indexed (restore original) --- contracts/interfaces/AugmintTokenInterface.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index 10d43488..c49aa9c6 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -30,7 +30,7 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax); event Transfer(address indexed from, address indexed to, uint amount); - event AugmintTransfer(address from, address to, uint amount, string narrative, uint fee); + event AugmintTransfer(address indexed from, address indexed to, uint amount, string narrative, uint fee); event TokenIssued(uint amount); event TokenBurned(uint amount); event Approval(address indexed _owner, address indexed _spender, uint256 _value); From 4bde61b9c626c710de4fbfbe97c52347114db8ef Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 21:30:42 +0100 Subject: [PATCH 12/26] delegatedTransfer return no value (reverts on fail) + narrative change --- contracts/generic/AugmintToken.sol | 5 ++--- contracts/interfaces/AugmintTokenInterface.sol | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index b5745d28..8ef342e5 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -47,7 +47,7 @@ contract AugmintToken is AugmintTokenInterface { bytes signature, uint requestedExecutorFee /* the executor can decide to request lower fee */ ) - external returns(bool) { + external { require(!noncesUsed[nonce], "nonce already used"); require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); @@ -60,10 +60,9 @@ contract AugmintToken is AugmintTokenInterface { require(recovered == from, "invalid signature"); - _transfer(from, msg.sender, requestedExecutorFee, "Delegated execution fee"); + _transfer(from, msg.sender, requestedExecutorFee, "Delegated transfer fee"); _transfer(from, to, amount, narrative); - return true; } function approve(address _spender, uint256 amount) external returns (bool) { diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index c49aa9c6..76ecd12f 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -46,7 +46,7 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { /* ^^^^ end of signed data ^^^^ */ bytes signature, uint requestedExecutorFee /* the executor can decide to request lower fee */ - ) external returns(bool); + ) external; function increaseApproval(address spender, uint addedValue) external returns (bool); function decreaseApproval(address spender, uint subtractedValue) external returns (bool); From 798263acf28cbf316cf3393e14483a800c1ff265 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Tue, 8 May 2018 22:56:34 +0100 Subject: [PATCH 13/26] store delegated tx hashes used instead of nonce --- contracts/generic/AugmintToken.sol | 6 +++--- contracts/interfaces/AugmintTokenInterface.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index 8ef342e5..83747f79 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -48,14 +48,14 @@ contract AugmintToken is AugmintTokenInterface { uint requestedExecutorFee /* the executor can decide to request lower fee */ ) external { - - require(!noncesUsed[nonce], "nonce already used"); require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); require(requestedExecutorFee <= maxExecutorFee, "requestedExecutorFee must be <= maxExecutorFee"); - noncesUsed[nonce] = true; bytes32 txHash = keccak256(this, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); + require(!delegatedTxHashesUsed[txHash], "nonce already used"); + delegatedTxHashesUsed[txHash] = true; + address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); require(recovered == from, "invalid signature"); diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index 76ecd12f..117b8b60 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -26,7 +26,7 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { mapping(address => mapping (address => uint256)) public allowed; // allowances added with approve() TransferFeeInterface public feeAccount; - mapping(bytes32 => bool) public noncesUsed; // record nonces used by delegatedTransfer + mapping(bytes32 => bool) public delegatedTxHashesUsed; // record txHashes used by delegatedTransfer event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax); event Transfer(address indexed from, address indexed to, uint amount); From d9d478133f6f10f00dce7fea8ffcceef95c16537 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Wed, 9 May 2018 00:00:52 +0100 Subject: [PATCH 14/26] units specifed in delegatedTransfer arg names --- contracts/generic/AugmintToken.sol | 13 +++++++------ contracts/interfaces/AugmintTokenInterface.sol | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index 83747f79..6df952a1 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -40,18 +40,19 @@ contract AugmintToken is AugmintTokenInterface { /* Transfers based on an offline signed transfer instruction. */ function delegatedTransfer(address from, address to, uint amount, string narrative, - uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ - uint maxExecutorFee, /* client provided max fee for executing the tx */ + uint minGasPrice, /* client provided minimum gasPrice in wei + which she expects tx to be executed */ + uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ bytes32 nonce, /* random nonce generated by client */ /* ^^^^ end of signed data ^^^^ */ bytes signature, - uint requestedExecutorFee /* the executor can decide to request lower fee */ + uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external { require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); - require(requestedExecutorFee <= maxExecutorFee, "requestedExecutorFee must be <= maxExecutorFee"); + require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); - bytes32 txHash = keccak256(this, from, to, amount, narrative, minGasPrice, maxExecutorFee, nonce); + bytes32 txHash = keccak256(this, from, to, amount, narrative, minGasPrice, maxExecutorFeeInToken, nonce); require(!delegatedTxHashesUsed[txHash], "nonce already used"); delegatedTxHashesUsed[txHash] = true; @@ -60,7 +61,7 @@ contract AugmintToken is AugmintTokenInterface { require(recovered == from, "invalid signature"); - _transfer(from, msg.sender, requestedExecutorFee, "Delegated transfer fee"); + _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated transfer fee"); _transfer(from, to, amount, narrative); } diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index 117b8b60..087a62a1 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -40,12 +40,13 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { function approve(address spender, uint value) external returns (bool); function delegatedTransfer(address from, address to, uint amount, string narrative, - uint minGasPrice, /* client provided gasPrice on which she expects tx to be exec. */ - uint maxExecutorFee, /* client provided max fee for executing the tx */ - bytes32 nonce, /* random nonce generated by client */ - /* ^^^^ end of signed data ^^^^ */ - bytes signature, - uint requestedExecutorFee /* the executor can decide to request lower fee */ + uint minGasPrice, /* client provided minimum gasPrice in wei + which she expects tx to be executed */ + uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ + bytes32 nonce, /* random nonce generated by client */ + /* ^^^^ end of signed data ^^^^ */ + bytes signature, + uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external; function increaseApproval(address spender, uint addedValue) external returns (bool); From 1a7c6248a5e8fd0a8ba8ce120744456fbde17d7a Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Wed, 9 May 2018 00:31:39 +0100 Subject: [PATCH 15/26] 0 transfer fee on executorFee transfer --- contracts/generic/AugmintToken.sol | 11 +++++++---- test/delegatedTransfer.js | 8 +------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index 6df952a1..aa1211f1 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -61,7 +61,7 @@ contract AugmintToken is AugmintTokenInterface { require(recovered == from, "invalid signature"); - _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated transfer fee"); + _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated transfer fee", 0); _transfer(from, to, amount, narrative); } @@ -174,11 +174,14 @@ contract AugmintToken is AugmintTokenInterface { } function _transfer(address from, address to, uint transferAmount, string narrative) private { - require(to != 0x0, "to must be set"); - uint fee = feeAccount.calculateTransferFee(from, to, transferAmount); - uint amountWithFee = transferAmount.add(fee); + _transfer(from, to, transferAmount, narrative, fee); + } + + function _transfer(address from, address to, uint transferAmount, string narrative, uint fee) private { + require(to != 0x0, "to must be set"); + uint amountWithFee = transferAmount.add(fee); // to emit proper reason instead of failing on from.sub() require(balances[from] >= amountWithFee, "balance must be >= amount + transfer fee"); diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js index 98bb6b90..c899e363 100644 --- a/test/delegatedTransfer.js +++ b/test/delegatedTransfer.js @@ -42,11 +42,6 @@ const signDelegatedTransfer = async clientParams => { const sendDelegatedTransfer = async (testInstance, clientParams, signature, executorParams) => { clientParams.fee = await tokenTestHelpers.getTransferFee(clientParams); - clientParams.executorFeeTransferFee = await tokenTestHelpers.getTransferFee({ - from: clientParams.from, - to: clientParams.to, - amount: clientParams.fee - }); const balBefore = await tokenTestHelpers.getAllBalances({ from: clientParams.from, @@ -77,7 +72,6 @@ const sendDelegatedTransfer = async (testInstance, clientParams, signature, exec ace: balBefore.from.ace .sub(clientParams.amount) .sub(clientParams.fee) - .sub(clientParams.executorFeeTransferFee) .sub(executorParams.requestedExecutorFee), eth: balBefore.from.eth }, @@ -86,7 +80,7 @@ const sendDelegatedTransfer = async (testInstance, clientParams, signature, exec eth: balBefore.to.eth }, feeAccount: { - ace: balBefore.feeAccount.ace.add(clientParams.fee).add(clientParams.executorFeeTransferFee), + ace: balBefore.feeAccount.ace.add(clientParams.fee), eth: balBefore.feeAccount.eth }, executor: { From e36caf20c70dbd58bd2cae8a7d0abd535066902d Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Wed, 9 May 2018 12:18:24 +0100 Subject: [PATCH 16/26] finish delegatedTransfer tests --- test/delegatedTransfer.js | 96 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js index c899e363..6ac7d5bd 100644 --- a/test/delegatedTransfer.js +++ b/test/delegatedTransfer.js @@ -102,7 +102,6 @@ contract("Delegated Transfers", accounts => { }); it("should transfer when delegatedTransfer is signed", async function() { - // params sent and signed by client const clientParams = { tokenAEurAddress: TokenAEur.address, from, @@ -126,7 +125,6 @@ contract("Delegated Transfers", accounts => { }); it("should not execute with the same nonce twice", async function() { - // params sent and signed by client const clientParams = { tokenAEurAddress: TokenAEur.address, from, @@ -141,7 +139,7 @@ contract("Delegated Transfers", accounts => { const executorParams = { executorAddress: accounts[3], actualGasPrice: clientParams.minGasPrice, - requestedExecutorFee: clientParams.maxExecutorFee - 1 + requestedExecutorFee: clientParams.maxExecutorFee }; const signature = await signDelegatedTransfer(clientParams); @@ -151,11 +149,95 @@ contract("Delegated Transfers", accounts => { await testHelpers.expectThrow(sendDelegatedTransfer(this, clientParams, signature, executorParams)); }); - it("should execute with lower requestedExecutorFee than signed"); + it("should execute with lower requestedExecutorFee than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + to: accounts[2], + amount: 1000, + narrative: "here we go", + minGasPrice: 1, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000003" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee - 10 + }; - it("should not execute with higher requestedExecutorFee than signed"); + const signature = await signDelegatedTransfer(clientParams); - it("should execute with higher gasPrice than signed"); + await sendDelegatedTransfer(this, clientParams, signature, executorParams); + }); - it("should not execute with lower gasPrice than signed"); + it("should not execute with higher requestedExecutorFee than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + to: accounts[2], + amount: 1000, + narrative: "here we go", + minGasPrice: 2, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000004" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee + 1 + }; + + const signature = await signDelegatedTransfer(clientParams); + + await testHelpers.expectThrow(sendDelegatedTransfer(this, clientParams, signature, executorParams)); + }); + + it("should execute with higher gasPrice than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + to: accounts[2], + amount: 1000, + narrative: "here we go", + minGasPrice: 1, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000005" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice + 1, + requestedExecutorFee: clientParams.maxExecutorFee + }; + + const signature = await signDelegatedTransfer(clientParams); + + await sendDelegatedTransfer(this, clientParams, signature, executorParams); + }); + + it("should not execute with lower gasPrice than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + to: accounts[2], + amount: 1000, + narrative: "here we go", + minGasPrice: 2, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000006" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice - 1, + requestedExecutorFee: clientParams.maxExecutorFee + }; + + const signature = await signDelegatedTransfer(clientParams); + + await testHelpers.expectThrow(sendDelegatedTransfer(this, clientParams, signature, executorParams)); + }); }); From f2ad542d6655e7b2e823f7cc597999789e3e2c63 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Wed, 9 May 2018 13:17:28 +0100 Subject: [PATCH 17/26] delegatedTransferAndNotify + tests --- contracts/generic/AugmintToken.sol | 31 ++- .../interfaces/AugmintTokenInterface.sol | 10 + test/delegatedTransferAndNotify.js | 238 ++++++++++++++++++ 3 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 test/delegatedTransferAndNotify.js diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index aa1211f1..b4671a1c 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -54,7 +54,7 @@ contract AugmintToken is AugmintTokenInterface { bytes32 txHash = keccak256(this, from, to, amount, narrative, minGasPrice, maxExecutorFeeInToken, nonce); - require(!delegatedTxHashesUsed[txHash], "nonce already used"); + require(!delegatedTxHashesUsed[txHash], "txHash already used"); delegatedTxHashesUsed[txHash] = true; address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); @@ -139,6 +139,35 @@ contract AugmintToken is AugmintTokenInterface { target.transferNotification(msg.sender, amount, data); } + /* transferAndNotify based on an instruction signed offline */ + function delegatedTransferAndNotify(address from, TokenReceiver target, uint amount, uint data, + uint minGasPrice, /* client provided minimum gasPrice in wei + which she expects tx to be executed */ + uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ + bytes32 nonce, /* random nonce generated by client */ + /* ^^^^ end of signed data ^^^^ */ + bytes signature, + uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ + ) + external { + require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); + require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); + + bytes32 txHash = keccak256(this, from, target, amount, data, minGasPrice, maxExecutorFeeInToken, nonce); + + require(!delegatedTxHashesUsed[txHash], "txHash already used"); + delegatedTxHashesUsed[txHash] = true; + + address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); + + require(recovered == from, "invalid signature"); + + _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated execution fee", 0); + _transfer(from, target, amount, ""); + target.transferNotification(from, amount, data); + } + + function transferWithNarrative(address to, uint256 amount, string narrative) external { _transfer(msg.sender, to, amount, narrative); } diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index 087a62a1..e629d2fd 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -49,6 +49,16 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external; + function delegatedTransferAndNotify(address from, TokenReceiver target, uint amount, uint data, + uint minGasPrice, /* client provided minimum gasPrice in wei + which she expects tx to be executed */ + uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ + bytes32 nonce, /* random nonce generated by client */ + /* ^^^^ end of signed data ^^^^ */ + bytes signature, + uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ + ) external; + function increaseApproval(address spender, uint addedValue) external returns (bool); function decreaseApproval(address spender, uint subtractedValue) external returns (bool); diff --git a/test/delegatedTransferAndNotify.js b/test/delegatedTransferAndNotify.js new file mode 100644 index 00000000..af3bfb44 --- /dev/null +++ b/test/delegatedTransferAndNotify.js @@ -0,0 +1,238 @@ +const tokenTestHelpers = require("./helpers/tokenTestHelpers.js"); +const testHelpers = require("./helpers/testHelpers.js"); +const TokenAEur = artifacts.require("TokenAEur.sol"); +const FeeAccount = artifacts.require("FeeAccount.sol"); +const Locker = artifacts.require("Locker.sol"); + +const DELEGATED_TRANSFERANDNOTIFY_MAX_GAS = 300000; + +let tokenAEur; +let from; +let lockProductId; +let perTermInterest; + +const signDelegatedTransferAndNotify = async clientParams => { + const txHash = global.web3v1.utils.soliditySha3( + clientParams.tokenAEurAddress, + clientParams.from, + clientParams.target, + clientParams.amount, + clientParams.data, + clientParams.minGasPrice, + clientParams.maxExecutorFee, + clientParams.nonce + ); + + const signature = await global.web3v1.eth.sign(txHash, clientParams.from); + + return signature; +}; + +const sendDelegatedTransferAndNotify = async (testInstance, clientParams, signature, executorParams) => { + clientParams.to = clientParams.target; + clientParams.fee = await tokenTestHelpers.getTransferFee(clientParams); + + const interestEarned = Math.floor(clientParams.amount * perTermInterest / 1000000); + + const balBefore = await tokenTestHelpers.getAllBalances({ + from: clientParams.from, + target: clientParams.target, + executor: executorParams.executorAddress, + feeAccount: FeeAccount.address + }); + + const tx = await tokenAEur.methods + .delegatedTransferAndNotify( + clientParams.from, + clientParams.target, + clientParams.amount, + clientParams.data, + clientParams.minGasPrice, + clientParams.maxExecutorFee, + clientParams.nonce, + signature, + executorParams.requestedExecutorFee + ) + .send({ from: executorParams.executorAddress, gas: 1200000, gasPrice: executorParams.actualGasPrice }); + testHelpers.logGasUse(testInstance, tx, "delegatedTransferAndNotify"); + + // TODO: assert events + + await tokenTestHelpers.assertBalances(balBefore, { + from: { + ace: balBefore.from.ace + .sub(clientParams.amount) + .sub(clientParams.fee) + .sub(executorParams.requestedExecutorFee), + eth: balBefore.from.eth + }, + target: { + ace: balBefore.target.ace.add(clientParams.amount).add(interestEarned), + eth: balBefore.target.eth + }, + feeAccount: { + ace: balBefore.feeAccount.ace.add(clientParams.fee), + eth: balBefore.feeAccount.eth + }, + executor: { + ace: balBefore.executor.ace.add(executorParams.requestedExecutorFee), + gasFee: testHelpers.GAS_PRICE * DELEGATED_TRANSFERANDNOTIFY_MAX_GAS + } + }); + + return tx; +}; + +contract("Delegated transferAndNotify", accounts => { + before(async () => { + from = accounts[1]; + lockProductId = 0; + tokenAEur = new global.web3v1.eth.Contract(TokenAEur.abi, TokenAEur.address); + + const lockerInstance = Locker.at(Locker.address); + const [product] = await Promise.all([lockerInstance.lockProducts(0), tokenTestHelpers.issueToReserve(100000)]); + perTermInterest = product[0].toNumber(); + + await tokenTestHelpers.withdrawFromReserve(from, 100000); + }); + + it("should lock with delegatedTransferAndNotify", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + target: Locker.address, + amount: 1000, + data: lockProductId, + minGasPrice: 1, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000001" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee + }; + + const signature = await signDelegatedTransferAndNotify(clientParams); + + await sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams); + }); + + it("should not execute with the same nonce twice", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + target: Locker.address, + amount: 1000, + data: lockProductId, + minGasPrice: 2, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000002" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee + }; + + const signature = await signDelegatedTransferAndNotify(clientParams); + + await sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams); + + await testHelpers.expectThrow(sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams)); + }); + + it("should execute with lower requestedExecutorFee than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + target: Locker.address, + amount: 1000, + data: lockProductId, + minGasPrice: 1, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000003" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee - 10 + }; + + const signature = await signDelegatedTransferAndNotify(clientParams); + + await sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams); + }); + + it("should not execute with higher requestedExecutorFee than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + target: Locker.address, + amount: 1000, + data: lockProductId, + minGasPrice: 2, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000004" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice, + requestedExecutorFee: clientParams.maxExecutorFee + 1 + }; + + const signature = await signDelegatedTransferAndNotify(clientParams); + + await testHelpers.expectThrow(sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams)); + }); + + it("should execute with higher gasPrice than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + target: Locker.address, + amount: 1000, + data: lockProductId, + minGasPrice: 1, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000005" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice + 1, + requestedExecutorFee: clientParams.maxExecutorFee + }; + + const signature = await signDelegatedTransferAndNotify(clientParams); + + await sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams); + }); + + it("should not execute with lower gasPrice than signed", async function() { + const clientParams = { + tokenAEurAddress: TokenAEur.address, + from, + target: Locker.address, + amount: 1000, + data: lockProductId, + minGasPrice: 2, + maxExecutorFee: 300, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000006" // to be a random hash with proper entrophy + }; + + const executorParams = { + executorAddress: accounts[3], + actualGasPrice: clientParams.minGasPrice - 1, + requestedExecutorFee: clientParams.maxExecutorFee + }; + + const signature = await signDelegatedTransferAndNotify(clientParams); + + await testHelpers.expectThrow(sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams)); + }); +}); From d35e1f7e8316273eb52f61d6cda8e254812f772c Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Wed, 9 May 2018 13:20:18 +0100 Subject: [PATCH 18/26] new tokenAEur ABI & local deployment addresses --- ..._ABI_6b1597192b36a746724929ec9ee7b6b8.json | 795 ++++++++++++++++++ .../999/AugmintReserves_DEPLOYS.json | 15 +- .../deployments/999/Exchange_DEPLOYS.json | 15 +- .../deployments/999/FeeAccount_DEPLOYS.json | 15 +- .../999/InterestEarnedAccount_DEPLOYS.json | 15 +- .../deployments/999/LoanManager_DEPLOYS.json | 15 +- abiniser/deployments/999/Locker_DEPLOYS.json | 15 +- .../999/MonetarySupervisor_DEPLOYS.json | 15 +- abiniser/deployments/999/Rates_DEPLOYS.json | 15 +- .../deployments/999/TokenAEur_DEPLOYS.json | 20 +- 10 files changed, 926 insertions(+), 9 deletions(-) create mode 100644 abiniser/abis/TokenAEur_ABI_6b1597192b36a746724929ec9ee7b6b8.json diff --git a/abiniser/abis/TokenAEur_ABI_6b1597192b36a746724929ec9ee7b6b8.json b/abiniser/abis/TokenAEur_ABI_6b1597192b36a746724929ec9ee7b6b8.json new file mode 100644 index 00000000..3faf42dc --- /dev/null +++ b/abiniser/abis/TokenAEur_ABI_6b1597192b36a746724929ec9ee7b6b8.json @@ -0,0 +1,795 @@ +{ + "contractName": "TokenAEur", + "abiHash": "6b1597192b36a746724929ec9ee7b6b8", + "generatedAt": "2018-05-09T12:19:24.878Z", + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "data", + "type": "uint256" + } + ], + "name": "transferAndNotify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "issueTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "target", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "data", + "type": "uint256" + }, + { + "name": "minGasPrice", + "type": "uint256" + }, + { + "name": "maxExecutorFeeInToken", + "type": "uint256" + }, + { + "name": "nonce", + "type": "bytes32" + }, + { + "name": "signature", + "type": "bytes" + }, + { + "name": "requestedExecutorFeeInToken", + "type": "uint256" + } + ], + "name": "delegatedTransferAndNotify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balances", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "peggedSymbol", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newFeeAccount", + "type": "address" + } + ], + "name": "setFeeAccount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "permissions", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermission", + "type": "bytes32" + } + ], + "name": "revokePermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeAccount", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "narrative", + "type": "string" + } + ], + "name": "transferFromWithNarrative", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermissions", + "type": "bytes32[]" + } + ], + "name": "revokeMultiplePermissions", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "narrative", + "type": "string" + }, + { + "name": "minGasPrice", + "type": "uint256" + }, + { + "name": "maxExecutorFeeInToken", + "type": "uint256" + }, + { + "name": "nonce", + "type": "bytes32" + }, + { + "name": "signature", + "type": "bytes" + }, + { + "name": "requestedExecutorFeeInToken", + "type": "uint256" + } + ], + "name": "delegatedTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermissions", + "type": "bytes32[]" + } + ], + "name": "grantMultiplePermissions", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "delegatedTxHashesUsed", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermission", + "type": "bytes32" + } + ], + "name": "grantPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "narrative", + "type": "string" + } + ], + "name": "transferWithNarrative", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_feeAccount", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "newFeeAccount", + "type": "address" + } + ], + "name": "FeeAccountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "transferFeePt", + "type": "uint256" + }, + { + "indexed": false, + "name": "transferFeeMin", + "type": "uint256" + }, + { + "indexed": false, + "name": "transferFeeMax", + "type": "uint256" + } + ], + "name": "TransferFeesChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "name": "narrative", + "type": "string" + }, + { + "indexed": false, + "name": "fee", + "type": "uint256" + } + ], + "name": "AugmintTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenIssued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "agent", + "type": "address" + }, + { + "indexed": false, + "name": "grantedPermission", + "type": "bytes32" + } + ], + "name": "PermissionGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "agent", + "type": "address" + }, + { + "indexed": false, + "name": "revokedPermission", + "type": "bytes32" + } + ], + "name": "PermissionRevoked", + "type": "event" + } + ] +} \ No newline at end of file diff --git a/abiniser/deployments/999/AugmintReserves_DEPLOYS.json b/abiniser/deployments/999/AugmintReserves_DEPLOYS.json index 1e8fbf3e..f7d1ab6d 100644 --- a/abiniser/deployments/999/AugmintReserves_DEPLOYS.json +++ b/abiniser/deployments/999/AugmintReserves_DEPLOYS.json @@ -20,7 +20,7 @@ } }, "33995f203f6c629e9916d82dd78e875f": { - "latestDeployedAddress": "0x56ad79460db45ac29cb672b1914e30c5ffa4e38e", + "latestDeployedAddress": "0xf4ab8fd7af56fb99e7bf5e5edc0b407da530a4f4", "deployments": { "0x56ad79460db45ac29cb672b1914e30c5ffa4e38e": { "generatedAt": "2018-04-26T01:54:21.104Z", @@ -34,6 +34,19 @@ "deployedBytecodeHash": "b8d26b4b6589916289f69078a09536aa", "sourceHash": "c3fd200d4510ea1b3d7c6c7ae04683b9", "source": "/* Contract to hold Augmint reserves (ETH & Token)\n - ETH as regular ETH balance of the contract\n - ERC20 token reserve (stored as regular Token balance under the contract address)\n\nNB: reserves are held under the contract address, therefore any transaction on the reserve is limited to the\n tx-s defined here (i.e. transfer is not allowed even by the contract owner or MonetaryBoard or MonetarySupervisor)\n\n */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract AugmintReserves is SystemAccount {\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into reserve (from defaulted loan's collateral )\n }\n\n function burn(AugmintTokenInterface augmintToken, uint amount) external restrict(\"MonetarySupervisorContract\") {\n augmintToken.burn(amount);\n }\n\n}\n" + }, + "0xf4ab8fd7af56fb99e7bf5e5edc0b407da530a4f4": { + "generatedAt": "2018-05-09T12:19:24.853Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:10.377Z", + "deployTransactionHash": "0xb340856da28cfea14a94bebe3b91a6a553f8be51a5e090eaacaf9bd4a2860ec7", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "f567a1bdc324dae94eb757065e518ecf", + "deployedBytecodeHash": "589e97f873d78017e591d3435d7b1b04", + "sourceHash": "c3fd200d4510ea1b3d7c6c7ae04683b9", + "source": "/* Contract to hold Augmint reserves (ETH & Token)\n - ETH as regular ETH balance of the contract\n - ERC20 token reserve (stored as regular Token balance under the contract address)\n\nNB: reserves are held under the contract address, therefore any transaction on the reserve is limited to the\n tx-s defined here (i.e. transfer is not allowed even by the contract owner or MonetaryBoard or MonetarySupervisor)\n\n */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract AugmintReserves is SystemAccount {\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into reserve (from defaulted loan's collateral )\n }\n\n function burn(AugmintTokenInterface augmintToken, uint amount) external restrict(\"MonetarySupervisorContract\") {\n augmintToken.burn(amount);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/Exchange_DEPLOYS.json b/abiniser/deployments/999/Exchange_DEPLOYS.json index 52efdd73..6c3a16cf 100644 --- a/abiniser/deployments/999/Exchange_DEPLOYS.json +++ b/abiniser/deployments/999/Exchange_DEPLOYS.json @@ -81,7 +81,7 @@ } }, "3c157a5256a2093da587f166d4dbd537": { - "latestDeployedAddress": "0x0179416e01def5166ce16f0ba74f3506b0ac06d1", + "latestDeployedAddress": "0x367f6272f3c4146f045f19be8774d97fd0966af5", "deployments": { "0x0179416e01def5166ce16f0ba74f3506b0ac06d1": { "generatedAt": "2018-05-01T20:24:25.543Z", @@ -95,6 +95,19 @@ "deployedBytecodeHash": "2c390a9b03f57a87af44432fc6444647", "sourceHash": "6e27bdbdc64d0d9cf52de1abc46c4cf7", "source": "/* Augmint's Internal Exchange\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/exchangeFlow.png\n\n TODO:\n - change to wihtdrawal pattern, see: https://github.com/Augmint/augmint-contracts/issues/17\n - deduct fee\n - consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)\n - use Rates interface?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./Rates.sol\";\n\n\ncontract Exchange is Restricted {\n using SafeMath for uint256;\n\n AugmintTokenInterface public augmintToken;\n Rates public rates;\n\n uint public constant CHUNK_SIZE = 100;\n\n struct Order {\n uint64 index;\n address maker;\n\n // % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million\n // I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity\n uint32 price;\n\n // buy order: amount in wei\n // sell order: token amount\n uint amount;\n }\n\n uint64 public orderCount;\n mapping(uint64 => Order) public buyTokenOrders;\n mapping(uint64 => Order) public sellTokenOrders;\n\n uint64[] private activeBuyOrders;\n uint64[] private activeSellOrders;\n\n /* used to stop executing matchMultiple when running out of gas.\n actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/\n uint32 private constant ORDER_MATCH_WORST_GAS = 100000;\n\n event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);\n\n event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,\n uint64 sellTokenOrderId, uint publishedRate, uint32 price, uint fillRate, uint weiAmount, uint tokenAmount);\n\n event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);\n\n event RatesContractChanged(Rates newRatesContract);\n\n constructor(AugmintTokenInterface _augmintToken, Rates _rates) public {\n augmintToken = _augmintToken;\n rates = _rates;\n }\n\n /* to allow upgrade of Rates contract */\n function setRatesContract(Rates newRatesContract)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n emit RatesContractChanged(newRatesContract);\n }\n\n function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(msg.value > 0, \"msg.value must be > 0\");\n\n orderId = ++orderCount;\n buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);\n activeBuyOrders.push(orderId);\n\n emit NewOrder(orderId, msg.sender, price, 0, msg.value);\n }\n\n /* this function requires previous approval to transfer tokens */\n function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {\n augmintToken.transferFrom(msg.sender, this, tokenAmount);\n return _placeSellTokenOrder(msg.sender, price, tokenAmount);\n }\n\n /* place sell token order called from AugmintToken's transferAndNotify\n Flow:\n 1) user calls token contract's transferAndNotify price passed in data arg\n 2) transferAndNotify transfers tokens to the Exchange contract\n 3) transferAndNotify calls Exchange.transferNotification with lockProductId\n */\n function transferNotification(address maker, uint tokenAmount, uint price) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n _placeSellTokenOrder(maker, uint32(price), tokenAmount);\n }\n\n function cancelBuyTokenOrder(uint64 buyTokenId) external {\n Order storage order = buyTokenOrders[buyTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeBuyOrder(order);\n\n msg.sender.transfer(amount);\n\n emit CancelledOrder(buyTokenId, msg.sender, 0, amount);\n }\n\n function cancelSellTokenOrder(uint64 sellTokenId) external {\n Order storage order = sellTokenOrders[sellTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeSellOrder(order);\n\n augmintToken.transferWithNarrative(msg.sender, amount, \"Sell token order cancelled\");\n\n emit CancelledOrder(sellTokenId, msg.sender, amount, 0);\n }\n\n /* matches any two orders if the sell price >= buy price\n trade price meets in the middle\n reverts if any of the orders have been removed\n */\n function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {\n _fillOrder(buyTokenId, sellTokenId);\n }\n\n /* matches as many orders as possible from the passed orders\n Runs as long as gas is available for the call.\n Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)\n */\n function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {\n uint len = buyTokenIds.length;\n require(len == sellTokenIds.length, \"buyTokenIds and sellTokenIds lengths must be equal\");\n\n for (uint i = 0; i < len && gasleft() > ORDER_MATCH_WORST_GAS; i++) {\n _fillOrder(buyTokenIds[i], sellTokenIds[i]);\n matchCount++;\n }\n }\n\n function getActiveOrderCounts() external view returns(uint buyTokenOrderCount, uint sellTokenOrderCount) {\n return(activeBuyOrders.length, activeSellOrders.length);\n }\n\n // returns CHUNK_SIZE orders starting from offset\n // orders are encoded as [id, maker, price, amount]\n function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {\n uint64 orderId = activeBuyOrders[offset + i];\n Order storage order = buyTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {\n uint64 orderId = activeSellOrders[offset + i];\n Order storage order = sellTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {\n Order storage buy = buyTokenOrders[buyTokenId];\n Order storage sell = sellTokenOrders[sellTokenId];\n\n require(buy.price >= sell.price, \"buy price must be >= sell price\");\n\n // pick maker's price (whoever placed order sooner considered as maker)\n uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;\n\n uint publishedRate;\n (publishedRate, ) = rates.rates(augmintToken.peggedSymbol());\n uint fillRate = publishedRate.mul(price).roundedDiv(1000000);\n\n uint sellWei = sell.amount.mul(1 ether).roundedDiv(fillRate);\n\n uint tradedWei;\n uint tradedTokens;\n if (sellWei <= buy.amount) {\n tradedWei = sellWei;\n tradedTokens = sell.amount;\n } else {\n tradedWei = buy.amount;\n tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);\n }\n\n buy.amount = buy.amount.sub(tradedWei);\n if (buy.amount == 0) {\n _removeBuyOrder(buy);\n }\n\n sell.amount = sell.amount.sub(tradedTokens);\n if (sell.amount == 0) {\n _removeSellOrder(sell);\n }\n\n augmintToken.transferWithNarrative(buy.maker, tradedTokens, \"Buy token order fill\");\n sell.maker.transfer(tradedWei);\n\n emit OrderFill(buy.maker, sell.maker, buyTokenId,\n sellTokenId, publishedRate, price, fillRate, tradedWei, tradedTokens);\n }\n\n function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)\n private returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(tokenAmount > 0, \"tokenAmount must be > 0\");\n\n orderId = ++orderCount;\n sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);\n activeSellOrders.push(orderId);\n\n emit NewOrder(orderId, maker, price, tokenAmount, 0);\n }\n\n function _removeBuyOrder(Order storage order) private {\n _removeOrder(activeBuyOrders, order.index);\n }\n\n function _removeSellOrder(Order storage order) private {\n _removeOrder(activeSellOrders, order.index);\n }\n\n function _removeOrder(uint64[] storage orders, uint64 index) private {\n if (index < orders.length - 1) {\n orders[index] = orders[orders.length - 1];\n }\n orders.length--;\n }\n\n}\n" + }, + "0x367f6272f3c4146f045f19be8774d97fd0966af5": { + "generatedAt": "2018-05-09T12:19:25.093Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.209Z", + "deployTransactionHash": "0x7c89a08ad305d1e37d35c4c74397780c94fb04c6bcde996a2e3c02c972256ca7", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "7f101651e2f05175f4fa67b4cd87d831", + "deployedBytecodeHash": "cbee9a74a02a651fb2c2a64091ddcbe9", + "sourceHash": "6e27bdbdc64d0d9cf52de1abc46c4cf7", + "source": "/* Augmint's Internal Exchange\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/exchangeFlow.png\n\n TODO:\n - change to wihtdrawal pattern, see: https://github.com/Augmint/augmint-contracts/issues/17\n - deduct fee\n - consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)\n - use Rates interface?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./Rates.sol\";\n\n\ncontract Exchange is Restricted {\n using SafeMath for uint256;\n\n AugmintTokenInterface public augmintToken;\n Rates public rates;\n\n uint public constant CHUNK_SIZE = 100;\n\n struct Order {\n uint64 index;\n address maker;\n\n // % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million\n // I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity\n uint32 price;\n\n // buy order: amount in wei\n // sell order: token amount\n uint amount;\n }\n\n uint64 public orderCount;\n mapping(uint64 => Order) public buyTokenOrders;\n mapping(uint64 => Order) public sellTokenOrders;\n\n uint64[] private activeBuyOrders;\n uint64[] private activeSellOrders;\n\n /* used to stop executing matchMultiple when running out of gas.\n actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/\n uint32 private constant ORDER_MATCH_WORST_GAS = 100000;\n\n event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);\n\n event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,\n uint64 sellTokenOrderId, uint publishedRate, uint32 price, uint fillRate, uint weiAmount, uint tokenAmount);\n\n event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);\n\n event RatesContractChanged(Rates newRatesContract);\n\n constructor(AugmintTokenInterface _augmintToken, Rates _rates) public {\n augmintToken = _augmintToken;\n rates = _rates;\n }\n\n /* to allow upgrade of Rates contract */\n function setRatesContract(Rates newRatesContract)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n emit RatesContractChanged(newRatesContract);\n }\n\n function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(msg.value > 0, \"msg.value must be > 0\");\n\n orderId = ++orderCount;\n buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);\n activeBuyOrders.push(orderId);\n\n emit NewOrder(orderId, msg.sender, price, 0, msg.value);\n }\n\n /* this function requires previous approval to transfer tokens */\n function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {\n augmintToken.transferFrom(msg.sender, this, tokenAmount);\n return _placeSellTokenOrder(msg.sender, price, tokenAmount);\n }\n\n /* place sell token order called from AugmintToken's transferAndNotify\n Flow:\n 1) user calls token contract's transferAndNotify price passed in data arg\n 2) transferAndNotify transfers tokens to the Exchange contract\n 3) transferAndNotify calls Exchange.transferNotification with lockProductId\n */\n function transferNotification(address maker, uint tokenAmount, uint price) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n _placeSellTokenOrder(maker, uint32(price), tokenAmount);\n }\n\n function cancelBuyTokenOrder(uint64 buyTokenId) external {\n Order storage order = buyTokenOrders[buyTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeBuyOrder(order);\n\n msg.sender.transfer(amount);\n\n emit CancelledOrder(buyTokenId, msg.sender, 0, amount);\n }\n\n function cancelSellTokenOrder(uint64 sellTokenId) external {\n Order storage order = sellTokenOrders[sellTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeSellOrder(order);\n\n augmintToken.transferWithNarrative(msg.sender, amount, \"Sell token order cancelled\");\n\n emit CancelledOrder(sellTokenId, msg.sender, amount, 0);\n }\n\n /* matches any two orders if the sell price >= buy price\n trade price meets in the middle\n reverts if any of the orders have been removed\n */\n function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {\n _fillOrder(buyTokenId, sellTokenId);\n }\n\n /* matches as many orders as possible from the passed orders\n Runs as long as gas is available for the call.\n Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)\n */\n function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {\n uint len = buyTokenIds.length;\n require(len == sellTokenIds.length, \"buyTokenIds and sellTokenIds lengths must be equal\");\n\n for (uint i = 0; i < len && gasleft() > ORDER_MATCH_WORST_GAS; i++) {\n _fillOrder(buyTokenIds[i], sellTokenIds[i]);\n matchCount++;\n }\n }\n\n function getActiveOrderCounts() external view returns(uint buyTokenOrderCount, uint sellTokenOrderCount) {\n return(activeBuyOrders.length, activeSellOrders.length);\n }\n\n // returns CHUNK_SIZE orders starting from offset\n // orders are encoded as [id, maker, price, amount]\n function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {\n uint64 orderId = activeBuyOrders[offset + i];\n Order storage order = buyTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {\n uint64 orderId = activeSellOrders[offset + i];\n Order storage order = sellTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {\n Order storage buy = buyTokenOrders[buyTokenId];\n Order storage sell = sellTokenOrders[sellTokenId];\n\n require(buy.price >= sell.price, \"buy price must be >= sell price\");\n\n // pick maker's price (whoever placed order sooner considered as maker)\n uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;\n\n uint publishedRate;\n (publishedRate, ) = rates.rates(augmintToken.peggedSymbol());\n uint fillRate = publishedRate.mul(price).roundedDiv(1000000);\n\n uint sellWei = sell.amount.mul(1 ether).roundedDiv(fillRate);\n\n uint tradedWei;\n uint tradedTokens;\n if (sellWei <= buy.amount) {\n tradedWei = sellWei;\n tradedTokens = sell.amount;\n } else {\n tradedWei = buy.amount;\n tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);\n }\n\n buy.amount = buy.amount.sub(tradedWei);\n if (buy.amount == 0) {\n _removeBuyOrder(buy);\n }\n\n sell.amount = sell.amount.sub(tradedTokens);\n if (sell.amount == 0) {\n _removeSellOrder(sell);\n }\n\n augmintToken.transferWithNarrative(buy.maker, tradedTokens, \"Buy token order fill\");\n sell.maker.transfer(tradedWei);\n\n emit OrderFill(buy.maker, sell.maker, buyTokenId,\n sellTokenId, publishedRate, price, fillRate, tradedWei, tradedTokens);\n }\n\n function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)\n private returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(tokenAmount > 0, \"tokenAmount must be > 0\");\n\n orderId = ++orderCount;\n sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);\n activeSellOrders.push(orderId);\n\n emit NewOrder(orderId, maker, price, tokenAmount, 0);\n }\n\n function _removeBuyOrder(Order storage order) private {\n _removeOrder(activeBuyOrders, order.index);\n }\n\n function _removeSellOrder(Order storage order) private {\n _removeOrder(activeSellOrders, order.index);\n }\n\n function _removeOrder(uint64[] storage orders, uint64 index) private {\n if (index < orders.length - 1) {\n orders[index] = orders[orders.length - 1];\n }\n orders.length--;\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/FeeAccount_DEPLOYS.json b/abiniser/deployments/999/FeeAccount_DEPLOYS.json index 17c94381..60657969 100644 --- a/abiniser/deployments/999/FeeAccount_DEPLOYS.json +++ b/abiniser/deployments/999/FeeAccount_DEPLOYS.json @@ -20,7 +20,7 @@ } }, "dd4594a936e439aa46ed5b06cb69eafa": { - "latestDeployedAddress": "0xb0a2a8e846b66c7384f52635cecef5280f766c8b", + "latestDeployedAddress": "0xbed57eb0b4232da0cddd3c9c27490fc0759e0a01", "deployments": { "0xb0a2a8e846b66c7384f52635cecef5280f766c8b": { "generatedAt": "2018-04-26T01:54:21.110Z", @@ -34,6 +34,19 @@ "deployedBytecodeHash": "d64b9e9eed98c75afb9cbf0d9a709fd5", "sourceHash": "fe888fa25a5c364abea5c0856f1ebd5b", "source": "/* Contract to collect fees from system\n TODO: calculateExchangeFee + Exchange params and setters\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/TransferFeeInterface.sol\";\n\n\ncontract FeeAccount is SystemAccount, TransferFeeInterface {\n\n using SafeMath for uint256;\n\n struct TransferFee {\n uint pt; // in parts per million (ppm) , ie. 2,000 = 0.2%\n uint min; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n uint max; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n }\n\n TransferFee public transferFee;\n\n event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax);\n\n constructor(uint transferFeePt, uint transferFeeMin, uint transferFeeMax) public {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into feeAccount (defaulting fee in ETH )\n }\n\n function setTransferFees(uint transferFeePt, uint transferFeeMin, uint transferFeeMax)\n external restrict(\"MonetaryBoard\") {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n emit TransferFeesChanged(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function calculateTransferFee(address from, address to, uint amount) external view returns (uint256 fee) {\n if (!permissions[from][\"NoFeeTransferContracts\"] && !permissions[to][\"NoFeeTransferContracts\"]) {\n fee = amount.mul(transferFee.pt).div(1000000);\n if (fee > transferFee.max) {\n fee = transferFee.max;\n } else if (fee < transferFee.min) {\n fee = transferFee.min;\n }\n }\n return fee;\n }\n\n function calculateExchangeFee(uint weiAmount) external view returns (uint256 weiFee) {\n /* TODO: to be implemented and use in Exchange.sol. always revert for now */\n require(weiAmount != weiAmount, \"not yet implemented\");\n weiFee = transferFee.max; // to silence compiler warnings until it's implemented\n }\n\n}\n" + }, + "0xbed57eb0b4232da0cddd3c9c27490fc0759e0a01": { + "generatedAt": "2018-05-09T12:19:24.863Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.182Z", + "deployTransactionHash": "0x7b0979e47c736474f40783c225a9dacd926d2f452725274493d5c7b122a6024d", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "1aee8ce6c1ed891053a114cc53e90661", + "deployedBytecodeHash": "9449685df0e18e918086c1eb18a3cb90", + "sourceHash": "fe888fa25a5c364abea5c0856f1ebd5b", + "source": "/* Contract to collect fees from system\n TODO: calculateExchangeFee + Exchange params and setters\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/TransferFeeInterface.sol\";\n\n\ncontract FeeAccount is SystemAccount, TransferFeeInterface {\n\n using SafeMath for uint256;\n\n struct TransferFee {\n uint pt; // in parts per million (ppm) , ie. 2,000 = 0.2%\n uint min; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n uint max; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n }\n\n TransferFee public transferFee;\n\n event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax);\n\n constructor(uint transferFeePt, uint transferFeeMin, uint transferFeeMax) public {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into feeAccount (defaulting fee in ETH )\n }\n\n function setTransferFees(uint transferFeePt, uint transferFeeMin, uint transferFeeMax)\n external restrict(\"MonetaryBoard\") {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n emit TransferFeesChanged(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function calculateTransferFee(address from, address to, uint amount) external view returns (uint256 fee) {\n if (!permissions[from][\"NoFeeTransferContracts\"] && !permissions[to][\"NoFeeTransferContracts\"]) {\n fee = amount.mul(transferFee.pt).div(1000000);\n if (fee > transferFee.max) {\n fee = transferFee.max;\n } else if (fee < transferFee.min) {\n fee = transferFee.min;\n }\n }\n return fee;\n }\n\n function calculateExchangeFee(uint weiAmount) external view returns (uint256 weiFee) {\n /* TODO: to be implemented and use in Exchange.sol. always revert for now */\n require(weiAmount != weiAmount, \"not yet implemented\");\n weiFee = transferFee.max; // to silence compiler warnings until it's implemented\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json b/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json index 27dad4c7..cd2da383 100644 --- a/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json +++ b/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json @@ -32,7 +32,7 @@ } }, "72a73972d565bb24463e7368fd263af4": { - "latestDeployedAddress": "0xbbecff5db2f9cccc936895121802fc15053344c6", + "latestDeployedAddress": "0x1b9441428f9e682bab4f9cc70fdf50adcc3411f4", "deployments": { "0xbbecff5db2f9cccc936895121802fc15053344c6": { "generatedAt": "2018-04-26T01:54:21.116Z", @@ -46,6 +46,19 @@ "deployedBytecodeHash": "d1c4729a444bfc4abb71e5af98bb6ed2", "sourceHash": "080bdccd163b74b49fb908b443c45199", "source": "/* Contract to hold earned interest from loans repaid\n premiums for locks are being accrued (i.e. transferred) to Locker */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract InterestEarnedAccount is SystemAccount {\n\n function transferInterest(AugmintTokenInterface augmintToken, address locker, uint interestAmount)\n external restrict(\"MonetarySupervisorContract\") {\n augmintToken.transfer(locker, interestAmount);\n }\n\n}\n" + }, + "0x1b9441428f9e682bab4f9cc70fdf50adcc3411f4": { + "generatedAt": "2018-05-09T12:19:24.870Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:14.518Z", + "deployTransactionHash": "0x4a2ddab9798f94b774cf51564da108298989ae38eea8945303d43d885e4a7382", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "5fcc9d704336df748a50d2813334e9bf", + "deployedBytecodeHash": "345c5e752b0283e593d29d6193059fdd", + "sourceHash": "080bdccd163b74b49fb908b443c45199", + "source": "/* Contract to hold earned interest from loans repaid\n premiums for locks are being accrued (i.e. transferred) to Locker */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract InterestEarnedAccount is SystemAccount {\n\n function transferInterest(AugmintTokenInterface augmintToken, address locker, uint interestAmount)\n external restrict(\"MonetarySupervisorContract\") {\n augmintToken.transfer(locker, interestAmount);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/LoanManager_DEPLOYS.json b/abiniser/deployments/999/LoanManager_DEPLOYS.json index c78284d7..ec7a7ce9 100644 --- a/abiniser/deployments/999/LoanManager_DEPLOYS.json +++ b/abiniser/deployments/999/LoanManager_DEPLOYS.json @@ -37,7 +37,7 @@ } }, "291572b8d2ffe95dca1733ebc1472e08": { - "latestDeployedAddress": "0x6d3c4ce65b4b1267804e011120f3359d44a3a1a3", + "latestDeployedAddress": "0x8280d8de1424a92f69a4055e5666d2e1e6950a33", "deployments": { "0x9b17fd903246da0b0304e0e984389dabd1ae6a8c": { "generatedAt": "2018-04-26T14:50:56.186Z", @@ -64,6 +64,19 @@ "deployedBytecodeHash": "efd0e7f623cf1fab5dbce721e75be938", "sourceHash": "9d5f96db98b6d336c18b8c6df5c7cd92", "source": "/*\n Contract to manage Augmint token loan contracts backed by ETH\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/loanFlow.png\n\n TODO:\n - create MonetarySupervisor interface and use it instead?\n - make data arg generic bytes?\n - make collect() run as long as gas provided allows\n*/\npragma solidity ^0.4.23;\n\nimport \"./Rates.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\n\n\ncontract LoanManager is Restricted {\n using SafeMath for uint256;\n\n uint16 public constant CHUNK_SIZE = 100;\n\n enum LoanState { Open, Repaid, Defaulted, Collected } // NB: Defaulted state is not stored, only getters calculate\n\n struct LoanProduct {\n uint minDisbursedAmount; // 0: with decimals set in AugmintToken.decimals\n uint32 term; // 1\n uint32 discountRate; // 2: discountRate in parts per million , ie. 10,000 = 1%\n uint32 collateralRatio; // 3: loan token amount / colleteral pegged ccy value\n // in parts per million , ie. 10,000 = 1%\n uint32 defaultingFeePt; // 4: % of collateral in parts per million , ie. 50,000 = 5%\n bool isActive; // 5\n }\n\n /* NB: we don't need to store loan parameters because loan products can't be altered (only disabled/enabled) */\n struct LoanData {\n uint collateralAmount; // 0\n uint repaymentAmount; // 1\n address borrower; // 2\n uint32 productId; // 3\n LoanState state; // 4\n uint40 maturity; // 5\n }\n\n LoanProduct[] public products;\n\n LoanData[] public loans;\n mapping(address => uint[]) public accountLoans; // owner account address => array of loan Ids\n\n Rates public rates; // instance of ETH/pegged currency rate provider contract\n AugmintTokenInterface public augmintToken; // instance of token contract\n MonetarySupervisor public monetarySupervisor;\n\n event NewLoan(uint32 productId, uint loanId, address indexed borrower, uint collateralAmount, uint loanAmount,\n uint repaymentAmount, uint40 maturity);\n\n event LoanProductActiveStateChanged(uint32 productId, bool newState);\n\n event LoanProductAdded(uint32 productId);\n\n event LoanRepayed(uint loanId, address borrower);\n\n event LoanCollected(uint loanId, address indexed borrower, uint collectedCollateral,\n uint releasedCollateral, uint defaultingFee);\n\n event SystemContractsChanged(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor);\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor, Rates _rates)\n public {\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n rates = _rates;\n }\n\n function addLoanProduct(uint32 term, uint32 discountRate, uint32 collateralRatio, uint minDisbursedAmount,\n uint32 defaultingFeePt, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newProductId = products.push(\n LoanProduct(minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, isActive)\n ) - 1;\n\n uint32 newProductId = uint32(_newProductId);\n require(newProductId == _newProductId, \"productId overflow\");\n\n emit LoanProductAdded(newProductId);\n }\n\n function setLoanProductActiveState(uint32 productId, bool newState)\n external restrict (\"MonetaryBoard\") {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n products[productId].isActive = false;\n emit LoanProductActiveStateChanged(productId, newState);\n }\n\n function newEthBackedLoan(uint32 productId) external payable {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n LoanProduct storage product = products[productId];\n require(product.isActive, \"product must be in active state\"); // valid product\n\n\n // calculate loan values based on ETH sent in with Tx\n uint tokenValue = rates.convertFromWei(augmintToken.peggedSymbol(), msg.value);\n uint repaymentAmount = tokenValue.mul(product.collateralRatio).div(1000000);\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, repaymentAmount);\n\n require(loanAmount >= product.minDisbursedAmount, \"loanAmount must be >= minDisbursedAmount\");\n\n uint expiration = now.add(product.term);\n uint40 maturity = uint40(expiration);\n require(maturity == expiration, \"maturity overflow\");\n\n // Create new loan\n uint loanId = loans.push(LoanData(msg.value, repaymentAmount, msg.sender,\n productId, LoanState.Open, maturity)) - 1;\n\n // Store ref to new loan\n accountLoans[msg.sender].push(loanId);\n\n // Issue tokens and send to borrower\n monetarySupervisor.issueLoan(msg.sender, loanAmount);\n\n emit NewLoan(productId, loanId, msg.sender, msg.value, loanAmount, repaymentAmount, maturity);\n }\n\n /* repay loan, called from AugmintToken's transferAndNotify\n Flow for repaying loan:\n 1) user calls token contract's transferAndNotify loanId passed in data arg\n 2) transferAndNotify transfers tokens to the Lender contract\n 3) transferAndNotify calls Lender.transferNotification with lockProductId\n */\n // from arg is not used as we allow anyone to repay a loan:\n function transferNotification(address, uint repaymentAmount, uint loanId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n\n _repayLoan(loanId, repaymentAmount);\n }\n\n function collect(uint[] loanIds) external {\n /* when there are a lots of loans to be collected then\n the client need to call it in batches to make sure tx won't exceed block gas limit.\n Anyone can call it - can't cause harm as it only allows to collect loans which they are defaulted\n TODO: optimise defaulting fee calculations\n */\n uint totalLoanAmountCollected;\n uint totalCollateralToCollect;\n uint totalDefaultingFee;\n for (uint i = 0; i < loanIds.length; i++) {\n require(i < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanIds[i]];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(now >= loan.maturity, \"current time must be later than maturity\");\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, loan.repaymentAmount);\n\n totalLoanAmountCollected = totalLoanAmountCollected.add(loanAmount);\n\n loan.state = LoanState.Collected;\n\n // send ETH collateral to augmintToken reserve\n uint defaultingFeeInToken = loan.repaymentAmount.mul(product.defaultingFeePt).div(1000000);\n uint defaultingFee = rates.convertToWei(augmintToken.peggedSymbol(), defaultingFeeInToken);\n uint targetCollection = rates.convertToWei(augmintToken.peggedSymbol(),\n loan.repaymentAmount).add(defaultingFee);\n\n uint releasedCollateral;\n if (targetCollection < loan.collateralAmount) {\n releasedCollateral = loan.collateralAmount.sub(targetCollection);\n loan.borrower.transfer(releasedCollateral);\n }\n uint collateralToCollect = loan.collateralAmount.sub(releasedCollateral);\n if (defaultingFee >= collateralToCollect) {\n defaultingFee = collateralToCollect;\n collateralToCollect = 0;\n } else {\n collateralToCollect = collateralToCollect.sub(defaultingFee);\n }\n totalDefaultingFee = totalDefaultingFee.add(defaultingFee);\n\n totalCollateralToCollect = totalCollateralToCollect.add(collateralToCollect);\n\n emit LoanCollected(loanIds[i], loan.borrower, collateralToCollect.add(defaultingFee), releasedCollateral, defaultingFee);\n }\n\n if (totalCollateralToCollect > 0) {\n address(monetarySupervisor.augmintReserves()).transfer(totalCollateralToCollect);\n }\n\n if (totalDefaultingFee > 0){\n address(augmintToken.feeAccount()).transfer(totalDefaultingFee);\n }\n\n monetarySupervisor.loanCollectionNotification(totalLoanAmountCollected);// update KPIs\n\n }\n\n /* to allow upgrade of Rates and MonetarySupervisor contracts */\n function setSystemContracts(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n monetarySupervisor = newMonetarySupervisor;\n emit SystemContractsChanged(newRatesContract, newMonetarySupervisor);\n }\n\n function getProductCount() external view returns (uint ct) {\n return products.length;\n }\n\n // returns CHUNK_SIZE loan products starting from some offset:\n // [ productId, minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, maxLoanAmount, isActive ]\n function getProducts(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= products.length) { break; }\n\n LoanProduct storage product = products[offset + i];\n\n response[i] = [offset + i, product.minDisbursedAmount, product.term, product.discountRate,\n product.collateralRatio, product.defaultingFeePt,\n monetarySupervisor.getMaxLoanAmount(product.minDisbursedAmount), product.isActive ? 1 : 0 ];\n }\n }\n\n function getLoanCount() external view returns (uint ct) {\n return loans.length;\n }\n\n /* returns CHUNK_SIZE loans starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoans(uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loans.length) { break; }\n\n response[i] = getLoanTuple(offset + i);\n }\n }\n\n function getLoanCountForAddress(address borrower) external view returns (uint) {\n return accountLoans[borrower].length;\n }\n\n /* returns CHUNK_SIZE loans of a given account, starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoansForAddress(address borrower, uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n uint[] storage loansForAddress = accountLoans[borrower];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loansForAddress.length) { break; }\n\n response[i] = getLoanTuple(loansForAddress[offset + i]);\n }\n }\n\n function getLoanTuple(uint loanId) public view returns (uint[10] result) {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n uint disbursementTime = loan.maturity - product.term;\n\n LoanState loanState =\n loan.state == LoanState.Open && now >= loan.maturity ? LoanState.Defaulted : loan.state;\n\n result = [loanId, loan.collateralAmount, loan.repaymentAmount, uint(loan.borrower),\n loan.productId, uint(loanState), loan.maturity, disbursementTime, loanAmount, interestAmount];\n }\n\n function calculateLoanValues(LoanProduct storage product, uint repaymentAmount)\n internal view returns (uint loanAmount, uint interestAmount) {\n // calculate loan values based on repayment amount\n loanAmount = repaymentAmount.mul(product.discountRate).div(1000000);\n interestAmount = loanAmount > repaymentAmount ? 0 : repaymentAmount.sub(loanAmount);\n }\n\n /* internal function, assuming repayment amount already transfered */\n function _repayLoan(uint loanId, uint repaymentAmount) internal {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(repaymentAmount == loan.repaymentAmount, \"repaymentAmount must be equal to tokens sent\");\n require(now <= loan.maturity, \"current time must be earlier than maturity\");\n\n LoanProduct storage product = products[loan.productId];\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n\n loans[loanId].state = LoanState.Repaid;\n\n if (interestAmount > 0) {\n augmintToken.transfer(monetarySupervisor.interestEarnedAccount(), interestAmount);\n augmintToken.burn(loanAmount);\n } else {\n // negative or zero interest (i.e. discountRate >= 0)\n augmintToken.burn(repaymentAmount);\n }\n\n monetarySupervisor.loanRepaymentNotification(loanAmount); // update KPIs\n\n loan.borrower.transfer(loan.collateralAmount); // send back ETH collateral\n\n emit LoanRepayed(loanId, loan.borrower);\n }\n\n}\n" + }, + "0x8280d8de1424a92f69a4055e5666d2e1e6950a33": { + "generatedAt": "2018-05-09T12:19:24.981Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.220Z", + "deployTransactionHash": "0xd8b39ba1a6e17f23d0f5e9be5fc157fed229459f1a0cd817db55455c0823e9d5", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "e96ef4ce2aae9198d215ce6ca82b89c2", + "deployedBytecodeHash": "1635532ec88fcc2442b41e10ea8ae2d1", + "sourceHash": "9d5f96db98b6d336c18b8c6df5c7cd92", + "source": "/*\n Contract to manage Augmint token loan contracts backed by ETH\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/loanFlow.png\n\n TODO:\n - create MonetarySupervisor interface and use it instead?\n - make data arg generic bytes?\n - make collect() run as long as gas provided allows\n*/\npragma solidity ^0.4.23;\n\nimport \"./Rates.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\n\n\ncontract LoanManager is Restricted {\n using SafeMath for uint256;\n\n uint16 public constant CHUNK_SIZE = 100;\n\n enum LoanState { Open, Repaid, Defaulted, Collected } // NB: Defaulted state is not stored, only getters calculate\n\n struct LoanProduct {\n uint minDisbursedAmount; // 0: with decimals set in AugmintToken.decimals\n uint32 term; // 1\n uint32 discountRate; // 2: discountRate in parts per million , ie. 10,000 = 1%\n uint32 collateralRatio; // 3: loan token amount / colleteral pegged ccy value\n // in parts per million , ie. 10,000 = 1%\n uint32 defaultingFeePt; // 4: % of collateral in parts per million , ie. 50,000 = 5%\n bool isActive; // 5\n }\n\n /* NB: we don't need to store loan parameters because loan products can't be altered (only disabled/enabled) */\n struct LoanData {\n uint collateralAmount; // 0\n uint repaymentAmount; // 1\n address borrower; // 2\n uint32 productId; // 3\n LoanState state; // 4\n uint40 maturity; // 5\n }\n\n LoanProduct[] public products;\n\n LoanData[] public loans;\n mapping(address => uint[]) public accountLoans; // owner account address => array of loan Ids\n\n Rates public rates; // instance of ETH/pegged currency rate provider contract\n AugmintTokenInterface public augmintToken; // instance of token contract\n MonetarySupervisor public monetarySupervisor;\n\n event NewLoan(uint32 productId, uint loanId, address indexed borrower, uint collateralAmount, uint loanAmount,\n uint repaymentAmount, uint40 maturity);\n\n event LoanProductActiveStateChanged(uint32 productId, bool newState);\n\n event LoanProductAdded(uint32 productId);\n\n event LoanRepayed(uint loanId, address borrower);\n\n event LoanCollected(uint loanId, address indexed borrower, uint collectedCollateral,\n uint releasedCollateral, uint defaultingFee);\n\n event SystemContractsChanged(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor);\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor, Rates _rates)\n public {\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n rates = _rates;\n }\n\n function addLoanProduct(uint32 term, uint32 discountRate, uint32 collateralRatio, uint minDisbursedAmount,\n uint32 defaultingFeePt, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newProductId = products.push(\n LoanProduct(minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, isActive)\n ) - 1;\n\n uint32 newProductId = uint32(_newProductId);\n require(newProductId == _newProductId, \"productId overflow\");\n\n emit LoanProductAdded(newProductId);\n }\n\n function setLoanProductActiveState(uint32 productId, bool newState)\n external restrict (\"MonetaryBoard\") {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n products[productId].isActive = false;\n emit LoanProductActiveStateChanged(productId, newState);\n }\n\n function newEthBackedLoan(uint32 productId) external payable {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n LoanProduct storage product = products[productId];\n require(product.isActive, \"product must be in active state\"); // valid product\n\n\n // calculate loan values based on ETH sent in with Tx\n uint tokenValue = rates.convertFromWei(augmintToken.peggedSymbol(), msg.value);\n uint repaymentAmount = tokenValue.mul(product.collateralRatio).div(1000000);\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, repaymentAmount);\n\n require(loanAmount >= product.minDisbursedAmount, \"loanAmount must be >= minDisbursedAmount\");\n\n uint expiration = now.add(product.term);\n uint40 maturity = uint40(expiration);\n require(maturity == expiration, \"maturity overflow\");\n\n // Create new loan\n uint loanId = loans.push(LoanData(msg.value, repaymentAmount, msg.sender,\n productId, LoanState.Open, maturity)) - 1;\n\n // Store ref to new loan\n accountLoans[msg.sender].push(loanId);\n\n // Issue tokens and send to borrower\n monetarySupervisor.issueLoan(msg.sender, loanAmount);\n\n emit NewLoan(productId, loanId, msg.sender, msg.value, loanAmount, repaymentAmount, maturity);\n }\n\n /* repay loan, called from AugmintToken's transferAndNotify\n Flow for repaying loan:\n 1) user calls token contract's transferAndNotify loanId passed in data arg\n 2) transferAndNotify transfers tokens to the Lender contract\n 3) transferAndNotify calls Lender.transferNotification with lockProductId\n */\n // from arg is not used as we allow anyone to repay a loan:\n function transferNotification(address, uint repaymentAmount, uint loanId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n\n _repayLoan(loanId, repaymentAmount);\n }\n\n function collect(uint[] loanIds) external {\n /* when there are a lots of loans to be collected then\n the client need to call it in batches to make sure tx won't exceed block gas limit.\n Anyone can call it - can't cause harm as it only allows to collect loans which they are defaulted\n TODO: optimise defaulting fee calculations\n */\n uint totalLoanAmountCollected;\n uint totalCollateralToCollect;\n uint totalDefaultingFee;\n for (uint i = 0; i < loanIds.length; i++) {\n require(i < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanIds[i]];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(now >= loan.maturity, \"current time must be later than maturity\");\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, loan.repaymentAmount);\n\n totalLoanAmountCollected = totalLoanAmountCollected.add(loanAmount);\n\n loan.state = LoanState.Collected;\n\n // send ETH collateral to augmintToken reserve\n uint defaultingFeeInToken = loan.repaymentAmount.mul(product.defaultingFeePt).div(1000000);\n uint defaultingFee = rates.convertToWei(augmintToken.peggedSymbol(), defaultingFeeInToken);\n uint targetCollection = rates.convertToWei(augmintToken.peggedSymbol(),\n loan.repaymentAmount).add(defaultingFee);\n\n uint releasedCollateral;\n if (targetCollection < loan.collateralAmount) {\n releasedCollateral = loan.collateralAmount.sub(targetCollection);\n loan.borrower.transfer(releasedCollateral);\n }\n uint collateralToCollect = loan.collateralAmount.sub(releasedCollateral);\n if (defaultingFee >= collateralToCollect) {\n defaultingFee = collateralToCollect;\n collateralToCollect = 0;\n } else {\n collateralToCollect = collateralToCollect.sub(defaultingFee);\n }\n totalDefaultingFee = totalDefaultingFee.add(defaultingFee);\n\n totalCollateralToCollect = totalCollateralToCollect.add(collateralToCollect);\n\n emit LoanCollected(loanIds[i], loan.borrower, collateralToCollect.add(defaultingFee), releasedCollateral, defaultingFee);\n }\n\n if (totalCollateralToCollect > 0) {\n address(monetarySupervisor.augmintReserves()).transfer(totalCollateralToCollect);\n }\n\n if (totalDefaultingFee > 0){\n address(augmintToken.feeAccount()).transfer(totalDefaultingFee);\n }\n\n monetarySupervisor.loanCollectionNotification(totalLoanAmountCollected);// update KPIs\n\n }\n\n /* to allow upgrade of Rates and MonetarySupervisor contracts */\n function setSystemContracts(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n monetarySupervisor = newMonetarySupervisor;\n emit SystemContractsChanged(newRatesContract, newMonetarySupervisor);\n }\n\n function getProductCount() external view returns (uint ct) {\n return products.length;\n }\n\n // returns CHUNK_SIZE loan products starting from some offset:\n // [ productId, minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, maxLoanAmount, isActive ]\n function getProducts(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= products.length) { break; }\n\n LoanProduct storage product = products[offset + i];\n\n response[i] = [offset + i, product.minDisbursedAmount, product.term, product.discountRate,\n product.collateralRatio, product.defaultingFeePt,\n monetarySupervisor.getMaxLoanAmount(product.minDisbursedAmount), product.isActive ? 1 : 0 ];\n }\n }\n\n function getLoanCount() external view returns (uint ct) {\n return loans.length;\n }\n\n /* returns CHUNK_SIZE loans starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoans(uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loans.length) { break; }\n\n response[i] = getLoanTuple(offset + i);\n }\n }\n\n function getLoanCountForAddress(address borrower) external view returns (uint) {\n return accountLoans[borrower].length;\n }\n\n /* returns CHUNK_SIZE loans of a given account, starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoansForAddress(address borrower, uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n uint[] storage loansForAddress = accountLoans[borrower];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loansForAddress.length) { break; }\n\n response[i] = getLoanTuple(loansForAddress[offset + i]);\n }\n }\n\n function getLoanTuple(uint loanId) public view returns (uint[10] result) {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n uint disbursementTime = loan.maturity - product.term;\n\n LoanState loanState =\n loan.state == LoanState.Open && now >= loan.maturity ? LoanState.Defaulted : loan.state;\n\n result = [loanId, loan.collateralAmount, loan.repaymentAmount, uint(loan.borrower),\n loan.productId, uint(loanState), loan.maturity, disbursementTime, loanAmount, interestAmount];\n }\n\n function calculateLoanValues(LoanProduct storage product, uint repaymentAmount)\n internal view returns (uint loanAmount, uint interestAmount) {\n // calculate loan values based on repayment amount\n loanAmount = repaymentAmount.mul(product.discountRate).div(1000000);\n interestAmount = loanAmount > repaymentAmount ? 0 : repaymentAmount.sub(loanAmount);\n }\n\n /* internal function, assuming repayment amount already transfered */\n function _repayLoan(uint loanId, uint repaymentAmount) internal {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(repaymentAmount == loan.repaymentAmount, \"repaymentAmount must be equal to tokens sent\");\n require(now <= loan.maturity, \"current time must be earlier than maturity\");\n\n LoanProduct storage product = products[loan.productId];\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n\n loans[loanId].state = LoanState.Repaid;\n\n if (interestAmount > 0) {\n augmintToken.transfer(monetarySupervisor.interestEarnedAccount(), interestAmount);\n augmintToken.burn(loanAmount);\n } else {\n // negative or zero interest (i.e. discountRate >= 0)\n augmintToken.burn(repaymentAmount);\n }\n\n monetarySupervisor.loanRepaymentNotification(loanAmount); // update KPIs\n\n loan.borrower.transfer(loan.collateralAmount); // send back ETH collateral\n\n emit LoanRepayed(loanId, loan.borrower);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/Locker_DEPLOYS.json b/abiniser/deployments/999/Locker_DEPLOYS.json index 01468db2..41ed7391 100644 --- a/abiniser/deployments/999/Locker_DEPLOYS.json +++ b/abiniser/deployments/999/Locker_DEPLOYS.json @@ -37,7 +37,7 @@ } }, "66e3e89133d9bbd91baac5552f21f7e1": { - "latestDeployedAddress": "0x664cf487408a31c812fc3e8a875d26bf26b604d8", + "latestDeployedAddress": "0x3444841a0e3417fd486aebc096ef3e67b75d7939", "deployments": { "0xe3f2872407243412e4753a77f75eda2269f02da2": { "generatedAt": "2018-04-26T14:50:56.210Z", @@ -64,6 +64,19 @@ "deployedBytecodeHash": "c0a9aa9f3a0bb633f930f11515b1d0b6", "sourceHash": "7ffe14f90465530802dc1f5762e0217f", "source": "/* contract for tracking locked funds\n\n requirements\n -> lock funds\n -> unlock funds\n -> index locks by address\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/lockFlow.png\n\n TODO / think about:\n -> self-destruct function?\n\n*/\n\npragma solidity ^0.4.23;\n\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\n\n\ncontract Locker is Restricted, TokenReceiver {\n\n using SafeMath for uint256;\n\n uint public constant CHUNK_SIZE = 100;\n\n event NewLockProduct(uint32 indexed lockProductId, uint32 perTermInterest, uint32 durationInSecs,\n uint32 minimumLockAmount, bool isActive);\n\n event LockProductActiveChange(uint32 indexed lockProductId, bool newActiveState);\n\n // NB: amountLocked includes the original amount, plus interest\n event NewLock(address indexed lockOwner, uint lockId, uint amountLocked, uint interestEarned,\n uint40 lockedUntil, uint32 perTermInterest, uint32 durationInSecs);\n\n event LockReleased(address indexed lockOwner, uint lockId);\n\n event MonetarySupervisorChanged(MonetarySupervisor newMonetarySupervisor);\n\n struct LockProduct {\n // perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n uint32 perTermInterest;\n uint32 durationInSecs;\n uint32 minimumLockAmount;\n bool isActive;\n }\n\n /* NB: we don't need to store lock parameters because lockProducts can't be altered (only disabled/enabled) */\n struct Lock {\n uint amountLocked;\n address owner;\n uint32 productId;\n uint40 lockedUntil;\n bool isActive;\n }\n\n AugmintTokenInterface public augmintToken;\n MonetarySupervisor public monetarySupervisor;\n\n LockProduct[] public lockProducts;\n\n Lock[] public locks;\n\n // lock ids for an account\n mapping(address => uint[]) public accountLocks;\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor) public {\n\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n\n }\n\n function addLockProduct(uint32 perTermInterest, uint32 durationInSecs, uint32 minimumLockAmount, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newLockProductId = lockProducts.push(\n LockProduct(perTermInterest, durationInSecs, minimumLockAmount, isActive)) - 1;\n uint32 newLockProductId = uint32(_newLockProductId);\n require(newLockProductId == _newLockProductId, \"lockProduct overflow\");\n emit NewLockProduct(newLockProductId, perTermInterest, durationInSecs, minimumLockAmount, isActive);\n\n }\n\n function setLockProductActiveState(uint32 lockProductId, bool isActive) external restrict(\"MonetaryBoard\") {\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n\n lockProducts[lockProductId].isActive = isActive;\n emit LockProductActiveChange(lockProductId, isActive);\n\n }\n\n /* lock funds, called from AugmintToken's transferAndNotify\n Flow for locking tokens:\n 1) user calls token contract's transferAndNotify lockProductId passed in data arg\n 2) transferAndNotify transfers tokens to the Lock contract\n 3) transferAndNotify calls Lock.transferNotification with lockProductId\n */\n function transferNotification(address from, uint256 amountToLock, uint _lockProductId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n uint32 lockProductId = uint32(_lockProductId);\n require(lockProductId == _lockProductId, \"lockProductId overflow\");\n /* TODO: make data arg generic bytes\n uint productId;\n assembly { // solhint-disable-line no-inline-assembly\n productId := mload(data)\n } */\n _createLock(lockProductId, from, amountToLock);\n }\n\n function releaseFunds(uint lockId) external {\n // next line would revert but require to emit reason:\n require(lockId < locks.length, \"invalid lockId\");\n Lock storage lock = locks[lockId];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n require(lock.isActive, \"lock must be in active state\");\n require(now >= lock.lockedUntil, \"current time must be later than lockedUntil\");\n\n lock.isActive = false;\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n monetarySupervisor.releaseFundsNotification(lock.amountLocked); // to maintain totalLockAmount\n augmintToken.transferWithNarrative(lock.owner, lock.amountLocked.add(interestEarned),\n \"Funds released from lock\");\n\n emit LockReleased(lock.owner, lockId);\n }\n\n function setMonetarySupervisor(MonetarySupervisor newMonetarySupervisor) external restrict(\"MonetaryBoard\") {\n monetarySupervisor = newMonetarySupervisor;\n emit MonetarySupervisorChanged(newMonetarySupervisor);\n }\n\n function getLockProductCount() external view returns (uint) {\n\n return lockProducts.length;\n\n }\n\n // returns 20 lock products starting from some offset\n // lock products are encoded as [ perTermInterest, durationInSecs, minimumLockAmount, maxLockAmount, isActive ]\n function getLockProducts(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= lockProducts.length) { break; }\n\n LockProduct storage lockProduct = lockProducts[offset + i];\n\n response[i] = [ lockProduct.perTermInterest, lockProduct.durationInSecs, lockProduct.minimumLockAmount,\n monetarySupervisor.getMaxLockAmount(lockProduct.minimumLockAmount, lockProduct.perTermInterest),\n lockProduct.isActive ? 1 : 0 ];\n }\n }\n\n function getLockCount() external view returns (uint) {\n return locks.length;\n }\n\n function getLockCountForAddress(address lockOwner) external view returns (uint) {\n return accountLocks[lockOwner].length;\n }\n\n // returns CHUNK_SIZE locks starting from some offset\n // lock products are encoded as\n // [lockId, owner, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n // NB: perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n function getLocks(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locks.length) { break; }\n\n Lock storage lock = locks[offset + i];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [uint(offset + i), uint(lock.owner), lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0];\n }\n }\n\n // returns CHUNK_SIZE locks of a given account, starting from some offset\n // lock products are encoded as\n // [lockId, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n function getLocksForAddress(address lockOwner, uint offset) external view returns (uint[7][CHUNK_SIZE] response) {\n\n uint[] storage locksForAddress = accountLocks[lockOwner];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locksForAddress.length) { break; }\n\n Lock storage lock = locks[locksForAddress[offset + i]];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [ locksForAddress[offset + i], lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0 ];\n }\n }\n\n function calculateInterest(uint32 perTermInterest, uint amountToLock) public pure returns (uint interestEarned) {\n interestEarned = amountToLock.mul(perTermInterest).div(1000000);\n }\n\n // Internal function. assumes amountToLock is already transferred to this Lock contract\n function _createLock(uint32 lockProductId, address lockOwner, uint amountToLock) internal returns(uint lockId) {\n LockProduct storage lockProduct = lockProducts[lockProductId];\n require(lockProduct.isActive, \"lockProduct must be in active state\");\n require(amountToLock >= lockProduct.minimumLockAmount, \"amountToLock must be >= minimumLockAmount\");\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, amountToLock);\n uint expiration = now.add(lockProduct.durationInSecs);\n uint40 lockedUntil = uint40(expiration);\n require(lockedUntil == expiration, \"lockedUntil overflow\");\n\n lockId = locks.push(Lock(amountToLock, lockOwner, lockProductId, lockedUntil, true)) - 1;\n accountLocks[lockOwner].push(lockId);\n\n monetarySupervisor.requestInterest(amountToLock, interestEarned); // update KPIs & transfer interest here\n\n emit NewLock(lockOwner, lockId, amountToLock, interestEarned, lockedUntil, lockProduct.perTermInterest,\n lockProduct.durationInSecs);\n }\n\n}\n" + }, + "0x3444841a0e3417fd486aebc096ef3e67b75d7939": { + "generatedAt": "2018-05-09T12:19:25.074Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.193Z", + "deployTransactionHash": "0xf2d8f45148bd8fb86cd3105d204f2583fe4e92655b5528d184e3d2f64eab058c", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "9b5e7b3da1f0e565bcb907a0d79c0e98", + "deployedBytecodeHash": "4e1e5f0bc52471787c3b57559a69a7e9", + "sourceHash": "7ffe14f90465530802dc1f5762e0217f", + "source": "/* contract for tracking locked funds\n\n requirements\n -> lock funds\n -> unlock funds\n -> index locks by address\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/lockFlow.png\n\n TODO / think about:\n -> self-destruct function?\n\n*/\n\npragma solidity ^0.4.23;\n\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\n\n\ncontract Locker is Restricted, TokenReceiver {\n\n using SafeMath for uint256;\n\n uint public constant CHUNK_SIZE = 100;\n\n event NewLockProduct(uint32 indexed lockProductId, uint32 perTermInterest, uint32 durationInSecs,\n uint32 minimumLockAmount, bool isActive);\n\n event LockProductActiveChange(uint32 indexed lockProductId, bool newActiveState);\n\n // NB: amountLocked includes the original amount, plus interest\n event NewLock(address indexed lockOwner, uint lockId, uint amountLocked, uint interestEarned,\n uint40 lockedUntil, uint32 perTermInterest, uint32 durationInSecs);\n\n event LockReleased(address indexed lockOwner, uint lockId);\n\n event MonetarySupervisorChanged(MonetarySupervisor newMonetarySupervisor);\n\n struct LockProduct {\n // perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n uint32 perTermInterest;\n uint32 durationInSecs;\n uint32 minimumLockAmount;\n bool isActive;\n }\n\n /* NB: we don't need to store lock parameters because lockProducts can't be altered (only disabled/enabled) */\n struct Lock {\n uint amountLocked;\n address owner;\n uint32 productId;\n uint40 lockedUntil;\n bool isActive;\n }\n\n AugmintTokenInterface public augmintToken;\n MonetarySupervisor public monetarySupervisor;\n\n LockProduct[] public lockProducts;\n\n Lock[] public locks;\n\n // lock ids for an account\n mapping(address => uint[]) public accountLocks;\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor) public {\n\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n\n }\n\n function addLockProduct(uint32 perTermInterest, uint32 durationInSecs, uint32 minimumLockAmount, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newLockProductId = lockProducts.push(\n LockProduct(perTermInterest, durationInSecs, minimumLockAmount, isActive)) - 1;\n uint32 newLockProductId = uint32(_newLockProductId);\n require(newLockProductId == _newLockProductId, \"lockProduct overflow\");\n emit NewLockProduct(newLockProductId, perTermInterest, durationInSecs, minimumLockAmount, isActive);\n\n }\n\n function setLockProductActiveState(uint32 lockProductId, bool isActive) external restrict(\"MonetaryBoard\") {\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n\n lockProducts[lockProductId].isActive = isActive;\n emit LockProductActiveChange(lockProductId, isActive);\n\n }\n\n /* lock funds, called from AugmintToken's transferAndNotify\n Flow for locking tokens:\n 1) user calls token contract's transferAndNotify lockProductId passed in data arg\n 2) transferAndNotify transfers tokens to the Lock contract\n 3) transferAndNotify calls Lock.transferNotification with lockProductId\n */\n function transferNotification(address from, uint256 amountToLock, uint _lockProductId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n uint32 lockProductId = uint32(_lockProductId);\n require(lockProductId == _lockProductId, \"lockProductId overflow\");\n /* TODO: make data arg generic bytes\n uint productId;\n assembly { // solhint-disable-line no-inline-assembly\n productId := mload(data)\n } */\n _createLock(lockProductId, from, amountToLock);\n }\n\n function releaseFunds(uint lockId) external {\n // next line would revert but require to emit reason:\n require(lockId < locks.length, \"invalid lockId\");\n Lock storage lock = locks[lockId];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n require(lock.isActive, \"lock must be in active state\");\n require(now >= lock.lockedUntil, \"current time must be later than lockedUntil\");\n\n lock.isActive = false;\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n monetarySupervisor.releaseFundsNotification(lock.amountLocked); // to maintain totalLockAmount\n augmintToken.transferWithNarrative(lock.owner, lock.amountLocked.add(interestEarned),\n \"Funds released from lock\");\n\n emit LockReleased(lock.owner, lockId);\n }\n\n function setMonetarySupervisor(MonetarySupervisor newMonetarySupervisor) external restrict(\"MonetaryBoard\") {\n monetarySupervisor = newMonetarySupervisor;\n emit MonetarySupervisorChanged(newMonetarySupervisor);\n }\n\n function getLockProductCount() external view returns (uint) {\n\n return lockProducts.length;\n\n }\n\n // returns 20 lock products starting from some offset\n // lock products are encoded as [ perTermInterest, durationInSecs, minimumLockAmount, maxLockAmount, isActive ]\n function getLockProducts(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= lockProducts.length) { break; }\n\n LockProduct storage lockProduct = lockProducts[offset + i];\n\n response[i] = [ lockProduct.perTermInterest, lockProduct.durationInSecs, lockProduct.minimumLockAmount,\n monetarySupervisor.getMaxLockAmount(lockProduct.minimumLockAmount, lockProduct.perTermInterest),\n lockProduct.isActive ? 1 : 0 ];\n }\n }\n\n function getLockCount() external view returns (uint) {\n return locks.length;\n }\n\n function getLockCountForAddress(address lockOwner) external view returns (uint) {\n return accountLocks[lockOwner].length;\n }\n\n // returns CHUNK_SIZE locks starting from some offset\n // lock products are encoded as\n // [lockId, owner, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n // NB: perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n function getLocks(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locks.length) { break; }\n\n Lock storage lock = locks[offset + i];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [uint(offset + i), uint(lock.owner), lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0];\n }\n }\n\n // returns CHUNK_SIZE locks of a given account, starting from some offset\n // lock products are encoded as\n // [lockId, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n function getLocksForAddress(address lockOwner, uint offset) external view returns (uint[7][CHUNK_SIZE] response) {\n\n uint[] storage locksForAddress = accountLocks[lockOwner];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locksForAddress.length) { break; }\n\n Lock storage lock = locks[locksForAddress[offset + i]];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [ locksForAddress[offset + i], lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0 ];\n }\n }\n\n function calculateInterest(uint32 perTermInterest, uint amountToLock) public pure returns (uint interestEarned) {\n interestEarned = amountToLock.mul(perTermInterest).div(1000000);\n }\n\n // Internal function. assumes amountToLock is already transferred to this Lock contract\n function _createLock(uint32 lockProductId, address lockOwner, uint amountToLock) internal returns(uint lockId) {\n LockProduct storage lockProduct = lockProducts[lockProductId];\n require(lockProduct.isActive, \"lockProduct must be in active state\");\n require(amountToLock >= lockProduct.minimumLockAmount, \"amountToLock must be >= minimumLockAmount\");\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, amountToLock);\n uint expiration = now.add(lockProduct.durationInSecs);\n uint40 lockedUntil = uint40(expiration);\n require(lockedUntil == expiration, \"lockedUntil overflow\");\n\n lockId = locks.push(Lock(amountToLock, lockOwner, lockProductId, lockedUntil, true)) - 1;\n accountLocks[lockOwner].push(lockId);\n\n monetarySupervisor.requestInterest(amountToLock, interestEarned); // update KPIs & transfer interest here\n\n emit NewLock(lockOwner, lockId, amountToLock, interestEarned, lockedUntil, lockProduct.perTermInterest,\n lockProduct.durationInSecs);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json b/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json index 79a57ad6..c1594721 100644 --- a/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json +++ b/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json @@ -32,7 +32,7 @@ } }, "a552ee1f90ae83cb91d07311ae8eab1e": { - "latestDeployedAddress": "0x88166fdc3c3e6768c375488fa45c90f1b904a767", + "latestDeployedAddress": "0x80d62c62138ed74699008c6f041b589c51619145", "deployments": { "0xbd68d7e5984c5000c432777e38c51554d9de1eaf": { "generatedAt": "2018-04-26T14:50:56.159Z", @@ -59,6 +59,19 @@ "deployedBytecodeHash": "afc07e949d0fc0522760e21001e6442e", "sourceHash": "cbd67b35e74bbbc9ef5a78c3180eb002", "source": "/* MonetarySupervisor\n - maintains system wide KPIs (eg totalLockAmount, totalLoanAmount)\n - holds system wide parameters/limits\n - enforces system wide limits\n - burns and issues to AugmintReserves\n - Send funds from reserve to exchange when intervening (not implemented yet)\n - Converts older versions of AugmintTokens in 1:1 to new\n\n TODO:\n - Mcreate and use MonetarySupervisorInterface?\n - create and use InterestEarnedAccount interface ?\n\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\nimport \"./InterestEarnedAccount.sol\";\nimport \"./AugmintReserves.sol\";\n\n\ncontract MonetarySupervisor is Restricted, TokenReceiver { // solhint-disable-line no-empty-blocks\n using SafeMath for uint256;\n\n uint public constant PERCENT_100 = 1000000;\n\n AugmintTokenInterface public augmintToken;\n InterestEarnedAccount public interestEarnedAccount;\n AugmintReserves public augmintReserves;\n\n uint public issuedByMonetaryBoard; // supply issued manually by monetary board\n\n uint public totalLoanAmount; // total amount of all loans without interest, in token\n uint public totalLockedAmount; // total amount of all locks without premium, in token\n\n /**********\n Parameters to ensure totalLoanAmount or totalLockedAmount difference is within limits and system also works\n when total loan or lock amounts are low.\n for test calculations: https://docs.google.com/spreadsheets/d/1MeWYPYZRIm1n9lzpvbq8kLfQg1hhvk5oJY6NrR401S0\n **********/\n struct LtdParams {\n uint lockDifferenceLimit; /* only allow a new lock if Loan To Deposit ratio would stay above\n (1 - lockDifferenceLimit) with new lock. Stored as parts per million */\n uint loanDifferenceLimit; /* only allow a new loan if Loan To Deposit ratio would stay above\n (1 + loanDifferenceLimit) with new loan. Stored as parts per million */\n /* allowedDifferenceAmount param is to ensure the system is not \"freezing\" when totalLoanAmount or\n totalLockAmount is low.\n It allows a new loan or lock (up to an amount to reach this difference) even if LTD will go below / above\n lockDifferenceLimit / loanDifferenceLimit with the new lock/loan */\n uint allowedDifferenceAmount;\n }\n\n LtdParams public ltdParams;\n\n /* Previously deployed AugmintTokens which are accepted for conversion (see transferNotification() )\n NB: it's not iterable so old version addresses needs to be added for UI manually after each deploy */\n mapping(address => bool) public acceptedLegacyAugmintTokens;\n\n event LtdParamsChanged(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount);\n\n event AcceptedLegacyAugmintTokenChanged(address augmintTokenAddress, bool newAcceptedState);\n\n event LegacyTokenConverted(address oldTokenAddress, address account, uint amount);\n\n event KPIsAdjusted(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment);\n\n event SystemContractsChanged(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves);\n\n constructor(AugmintTokenInterface _augmintToken, AugmintReserves _augmintReserves,\n InterestEarnedAccount _interestEarnedAccount,\n uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount) public {\n augmintToken = _augmintToken;\n augmintReserves = _augmintReserves;\n interestEarnedAccount = _interestEarnedAccount;\n\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n function issueToReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.add(amount);\n augmintToken.issueTo(augmintReserves, amount);\n }\n\n function burnFromReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.sub(amount);\n augmintReserves.burn(augmintToken, amount);\n }\n\n /* Locker requesting interest when locking funds. Enforcing LTD to stay within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function requestInterest(uint amountToLock, uint interestAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n require(amountToLock <= getMaxLockAmountAllowedByLtd(), \"amountToLock must be <= maxLockAmountAllowedByLtd\");\n\n totalLockedAmount = totalLockedAmount.add(amountToLock);\n // next line would revert but require to emit reason:\n require(augmintToken.balanceOf(address(interestEarnedAccount)) >= interestAmount,\n \"interestEarnedAccount balance must be >= interestAmount\");\n interestEarnedAccount.transferInterest(augmintToken, msg.sender, interestAmount); // transfer interest to Locker\n }\n\n // Locker notifying when releasing funds to update KPIs\n function releaseFundsNotification(uint lockedAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n totalLockedAmount = totalLockedAmount.sub(lockedAmount);\n }\n\n /* Issue loan if LTD stays within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function issueLoan(address borrower, uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n require(loanAmount <= getMaxLoanAmountAllowedByLtd(), \"loanAmount must be <= maxLoanAmountAllowedByLtd\");\n totalLoanAmount = totalLoanAmount.add(loanAmount);\n augmintToken.issueTo(borrower, loanAmount);\n }\n\n function loanRepaymentNotification(uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(loanAmount);\n }\n\n // NB: this is called by Lender contract with the sum of all loans collected in batch\n function loanCollectionNotification(uint totalLoanAmountCollected) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(totalLoanAmountCollected);\n }\n\n function setAcceptedLegacyAugmintToken(address legacyAugmintTokenAddress, bool newAcceptedState)\n external restrict(\"MonetaryBoard\") {\n acceptedLegacyAugmintTokens[legacyAugmintTokenAddress] = newAcceptedState;\n emit AcceptedLegacyAugmintTokenChanged(legacyAugmintTokenAddress, newAcceptedState);\n }\n\n function setLtdParams(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount)\n external restrict(\"MonetaryBoard\") {\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n\n emit LtdParamsChanged(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n /* function to migrate old totalLoanAmount and totalLockedAmount from old monetarySupervisor contract\n when it's upgraded.\n Set new monetarySupervisor contract in all locker and loanManager contracts before executing this */\n function adjustKPIs(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment)\n external restrict(\"MonetaryBoard\") {\n totalLoanAmount = totalLoanAmount.add(totalLoanAmountAdjustment);\n totalLockedAmount = totalLockedAmount.add(totalLockedAmountAdjustment);\n\n emit KPIsAdjusted(totalLoanAmountAdjustment, totalLockedAmountAdjustment);\n }\n\n /* to allow upgrades of InterestEarnedAccount and AugmintReserves contracts. */\n function setSystemContracts(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves)\n external restrict(\"MonetaryBoard\") {\n interestEarnedAccount = newInterestEarnedAccount;\n augmintReserves = newAugmintReserves;\n emit SystemContractsChanged(newInterestEarnedAccount, newAugmintReserves);\n }\n\n /* User can request to convert their tokens from older AugmintToken versions in 1:1\n transferNotification is called from AugmintToken's transferAndNotify\n Flow for converting old tokens:\n 1) user calls old token contract's transferAndNotify with the amount to convert,\n addressing the new MonetarySupervisor Contract\n 2) transferAndNotify transfers user's old tokens to the current MonetarySupervisor contract's address\n 3) transferAndNotify calls MonetarySupervisor.transferNotification\n 4) MonetarySupervisor checks if old AugmintToken is permitted\n 5) MonetarySupervisor issues new tokens to user's account in current AugmintToken\n 6) MonetarySupervisor burns old tokens from own balance\n */\n function transferNotification(address from, uint amount, uint /* data, not used */ ) external {\n AugmintTokenInterface legacyToken = AugmintTokenInterface(msg.sender);\n require(acceptedLegacyAugmintTokens[legacyToken], \"msg.sender must be allowed in acceptedLegacyAugmintTokens\");\n\n legacyToken.burn(amount);\n augmintToken.issueTo(from, amount);\n emit LegacyTokenConverted(msg.sender, from, amount);\n }\n\n function getLoanToDepositRatio() external view returns (uint loanToDepositRatio) {\n loanToDepositRatio = totalLockedAmount == 0 ? 0 : totalLockedAmount.mul(PERCENT_100).div(totalLoanAmount);\n }\n\n /* Helper function for UI.\n Returns max lock amount based on minLockAmount, interestPt, using LTD params & interestEarnedAccount balance */\n function getMaxLockAmount(uint minLockAmount, uint interestPt) external view returns (uint maxLock) {\n uint allowedByEarning = augmintToken.balanceOf(address(interestEarnedAccount)).mul(PERCENT_100).div(interestPt);\n uint allowedByLtd = getMaxLockAmountAllowedByLtd();\n maxLock = allowedByEarning < allowedByLtd ? allowedByEarning : allowedByLtd;\n maxLock = maxLock < minLockAmount ? 0 : maxLock;\n }\n\n /* Helper function for UI.\n Returns max loan amount based on minLoanAmont using LTD params */\n function getMaxLoanAmount(uint minLoanAmount) external view returns (uint maxLoan) {\n uint allowedByLtd = getMaxLoanAmountAllowedByLtd();\n maxLoan = allowedByLtd < minLoanAmount ? 0 : allowedByLtd;\n }\n\n /* returns maximum lockable token amount allowed by LTD params. */\n function getMaxLockAmountAllowedByLtd() public view returns(uint maxLockByLtd) {\n uint allowedByLtdDifferencePt = totalLoanAmount.mul(PERCENT_100).div(PERCENT_100\n .sub(ltdParams.lockDifferenceLimit));\n allowedByLtdDifferencePt = totalLockedAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLockedAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLockedAmount >= totalLoanAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLoanAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLockedAmount);\n\n maxLockByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n /* returns maximum borrowable token amount allowed by LTD params */\n function getMaxLoanAmountAllowedByLtd() public view returns(uint maxLoanByLtd) {\n uint allowedByLtdDifferencePt = totalLockedAmount.mul(ltdParams.loanDifferenceLimit.add(PERCENT_100))\n .div(PERCENT_100);\n allowedByLtdDifferencePt = totalLoanAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLoanAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLoanAmount >= totalLockedAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLockedAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLoanAmount);\n\n maxLoanByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n}\n" + }, + "0x80d62c62138ed74699008c6f041b589c51619145": { + "generatedAt": "2018-05-09T12:19:24.897Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.178Z", + "deployTransactionHash": "0x6cad685d7b05755ae58e792936e933d0710c1a03d7a9b70d6724853b74c45b22", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "5d834220507e83a131733731236263e3", + "deployedBytecodeHash": "b077fe6a352003e4f465a281bd3a503f", + "sourceHash": "cbd67b35e74bbbc9ef5a78c3180eb002", + "source": "/* MonetarySupervisor\n - maintains system wide KPIs (eg totalLockAmount, totalLoanAmount)\n - holds system wide parameters/limits\n - enforces system wide limits\n - burns and issues to AugmintReserves\n - Send funds from reserve to exchange when intervening (not implemented yet)\n - Converts older versions of AugmintTokens in 1:1 to new\n\n TODO:\n - Mcreate and use MonetarySupervisorInterface?\n - create and use InterestEarnedAccount interface ?\n\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\nimport \"./InterestEarnedAccount.sol\";\nimport \"./AugmintReserves.sol\";\n\n\ncontract MonetarySupervisor is Restricted, TokenReceiver { // solhint-disable-line no-empty-blocks\n using SafeMath for uint256;\n\n uint public constant PERCENT_100 = 1000000;\n\n AugmintTokenInterface public augmintToken;\n InterestEarnedAccount public interestEarnedAccount;\n AugmintReserves public augmintReserves;\n\n uint public issuedByMonetaryBoard; // supply issued manually by monetary board\n\n uint public totalLoanAmount; // total amount of all loans without interest, in token\n uint public totalLockedAmount; // total amount of all locks without premium, in token\n\n /**********\n Parameters to ensure totalLoanAmount or totalLockedAmount difference is within limits and system also works\n when total loan or lock amounts are low.\n for test calculations: https://docs.google.com/spreadsheets/d/1MeWYPYZRIm1n9lzpvbq8kLfQg1hhvk5oJY6NrR401S0\n **********/\n struct LtdParams {\n uint lockDifferenceLimit; /* only allow a new lock if Loan To Deposit ratio would stay above\n (1 - lockDifferenceLimit) with new lock. Stored as parts per million */\n uint loanDifferenceLimit; /* only allow a new loan if Loan To Deposit ratio would stay above\n (1 + loanDifferenceLimit) with new loan. Stored as parts per million */\n /* allowedDifferenceAmount param is to ensure the system is not \"freezing\" when totalLoanAmount or\n totalLockAmount is low.\n It allows a new loan or lock (up to an amount to reach this difference) even if LTD will go below / above\n lockDifferenceLimit / loanDifferenceLimit with the new lock/loan */\n uint allowedDifferenceAmount;\n }\n\n LtdParams public ltdParams;\n\n /* Previously deployed AugmintTokens which are accepted for conversion (see transferNotification() )\n NB: it's not iterable so old version addresses needs to be added for UI manually after each deploy */\n mapping(address => bool) public acceptedLegacyAugmintTokens;\n\n event LtdParamsChanged(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount);\n\n event AcceptedLegacyAugmintTokenChanged(address augmintTokenAddress, bool newAcceptedState);\n\n event LegacyTokenConverted(address oldTokenAddress, address account, uint amount);\n\n event KPIsAdjusted(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment);\n\n event SystemContractsChanged(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves);\n\n constructor(AugmintTokenInterface _augmintToken, AugmintReserves _augmintReserves,\n InterestEarnedAccount _interestEarnedAccount,\n uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount) public {\n augmintToken = _augmintToken;\n augmintReserves = _augmintReserves;\n interestEarnedAccount = _interestEarnedAccount;\n\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n function issueToReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.add(amount);\n augmintToken.issueTo(augmintReserves, amount);\n }\n\n function burnFromReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.sub(amount);\n augmintReserves.burn(augmintToken, amount);\n }\n\n /* Locker requesting interest when locking funds. Enforcing LTD to stay within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function requestInterest(uint amountToLock, uint interestAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n require(amountToLock <= getMaxLockAmountAllowedByLtd(), \"amountToLock must be <= maxLockAmountAllowedByLtd\");\n\n totalLockedAmount = totalLockedAmount.add(amountToLock);\n // next line would revert but require to emit reason:\n require(augmintToken.balanceOf(address(interestEarnedAccount)) >= interestAmount,\n \"interestEarnedAccount balance must be >= interestAmount\");\n interestEarnedAccount.transferInterest(augmintToken, msg.sender, interestAmount); // transfer interest to Locker\n }\n\n // Locker notifying when releasing funds to update KPIs\n function releaseFundsNotification(uint lockedAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n totalLockedAmount = totalLockedAmount.sub(lockedAmount);\n }\n\n /* Issue loan if LTD stays within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function issueLoan(address borrower, uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n require(loanAmount <= getMaxLoanAmountAllowedByLtd(), \"loanAmount must be <= maxLoanAmountAllowedByLtd\");\n totalLoanAmount = totalLoanAmount.add(loanAmount);\n augmintToken.issueTo(borrower, loanAmount);\n }\n\n function loanRepaymentNotification(uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(loanAmount);\n }\n\n // NB: this is called by Lender contract with the sum of all loans collected in batch\n function loanCollectionNotification(uint totalLoanAmountCollected) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(totalLoanAmountCollected);\n }\n\n function setAcceptedLegacyAugmintToken(address legacyAugmintTokenAddress, bool newAcceptedState)\n external restrict(\"MonetaryBoard\") {\n acceptedLegacyAugmintTokens[legacyAugmintTokenAddress] = newAcceptedState;\n emit AcceptedLegacyAugmintTokenChanged(legacyAugmintTokenAddress, newAcceptedState);\n }\n\n function setLtdParams(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount)\n external restrict(\"MonetaryBoard\") {\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n\n emit LtdParamsChanged(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n /* function to migrate old totalLoanAmount and totalLockedAmount from old monetarySupervisor contract\n when it's upgraded.\n Set new monetarySupervisor contract in all locker and loanManager contracts before executing this */\n function adjustKPIs(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment)\n external restrict(\"MonetaryBoard\") {\n totalLoanAmount = totalLoanAmount.add(totalLoanAmountAdjustment);\n totalLockedAmount = totalLockedAmount.add(totalLockedAmountAdjustment);\n\n emit KPIsAdjusted(totalLoanAmountAdjustment, totalLockedAmountAdjustment);\n }\n\n /* to allow upgrades of InterestEarnedAccount and AugmintReserves contracts. */\n function setSystemContracts(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves)\n external restrict(\"MonetaryBoard\") {\n interestEarnedAccount = newInterestEarnedAccount;\n augmintReserves = newAugmintReserves;\n emit SystemContractsChanged(newInterestEarnedAccount, newAugmintReserves);\n }\n\n /* User can request to convert their tokens from older AugmintToken versions in 1:1\n transferNotification is called from AugmintToken's transferAndNotify\n Flow for converting old tokens:\n 1) user calls old token contract's transferAndNotify with the amount to convert,\n addressing the new MonetarySupervisor Contract\n 2) transferAndNotify transfers user's old tokens to the current MonetarySupervisor contract's address\n 3) transferAndNotify calls MonetarySupervisor.transferNotification\n 4) MonetarySupervisor checks if old AugmintToken is permitted\n 5) MonetarySupervisor issues new tokens to user's account in current AugmintToken\n 6) MonetarySupervisor burns old tokens from own balance\n */\n function transferNotification(address from, uint amount, uint /* data, not used */ ) external {\n AugmintTokenInterface legacyToken = AugmintTokenInterface(msg.sender);\n require(acceptedLegacyAugmintTokens[legacyToken], \"msg.sender must be allowed in acceptedLegacyAugmintTokens\");\n\n legacyToken.burn(amount);\n augmintToken.issueTo(from, amount);\n emit LegacyTokenConverted(msg.sender, from, amount);\n }\n\n function getLoanToDepositRatio() external view returns (uint loanToDepositRatio) {\n loanToDepositRatio = totalLockedAmount == 0 ? 0 : totalLockedAmount.mul(PERCENT_100).div(totalLoanAmount);\n }\n\n /* Helper function for UI.\n Returns max lock amount based on minLockAmount, interestPt, using LTD params & interestEarnedAccount balance */\n function getMaxLockAmount(uint minLockAmount, uint interestPt) external view returns (uint maxLock) {\n uint allowedByEarning = augmintToken.balanceOf(address(interestEarnedAccount)).mul(PERCENT_100).div(interestPt);\n uint allowedByLtd = getMaxLockAmountAllowedByLtd();\n maxLock = allowedByEarning < allowedByLtd ? allowedByEarning : allowedByLtd;\n maxLock = maxLock < minLockAmount ? 0 : maxLock;\n }\n\n /* Helper function for UI.\n Returns max loan amount based on minLoanAmont using LTD params */\n function getMaxLoanAmount(uint minLoanAmount) external view returns (uint maxLoan) {\n uint allowedByLtd = getMaxLoanAmountAllowedByLtd();\n maxLoan = allowedByLtd < minLoanAmount ? 0 : allowedByLtd;\n }\n\n /* returns maximum lockable token amount allowed by LTD params. */\n function getMaxLockAmountAllowedByLtd() public view returns(uint maxLockByLtd) {\n uint allowedByLtdDifferencePt = totalLoanAmount.mul(PERCENT_100).div(PERCENT_100\n .sub(ltdParams.lockDifferenceLimit));\n allowedByLtdDifferencePt = totalLockedAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLockedAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLockedAmount >= totalLoanAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLoanAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLockedAmount);\n\n maxLockByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n /* returns maximum borrowable token amount allowed by LTD params */\n function getMaxLoanAmountAllowedByLtd() public view returns(uint maxLoanByLtd) {\n uint allowedByLtdDifferencePt = totalLockedAmount.mul(ltdParams.loanDifferenceLimit.add(PERCENT_100))\n .div(PERCENT_100);\n allowedByLtdDifferencePt = totalLoanAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLoanAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLoanAmount >= totalLockedAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLockedAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLoanAmount);\n\n maxLoanByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/Rates_DEPLOYS.json b/abiniser/deployments/999/Rates_DEPLOYS.json index a97a9bd2..27f9dfbf 100644 --- a/abiniser/deployments/999/Rates_DEPLOYS.json +++ b/abiniser/deployments/999/Rates_DEPLOYS.json @@ -20,7 +20,7 @@ } }, "cc8bc64cd780f047eca819e6cd3b8af9": { - "latestDeployedAddress": "0x8b639dc72f3e640c0d6bc19497fbc7b5160d0463", + "latestDeployedAddress": "0xd3ef19679c2dbbf3b8e2077c61b88f5e9c6178eb", "deployments": { "0x8b639dc72f3e640c0d6bc19497fbc7b5160d0463": { "generatedAt": "2018-04-25T12:31:28.978Z", @@ -34,6 +34,19 @@ "deployedBytecodeHash": "36714b840505a1bedac90a82a36e514c", "sourceHash": "e9dda67ca678fd3a4700e8970b50acd3", "source": "/*\n Generic symbol / WEI rates contract.\n only callable by trusted price oracles.\n Being regularly called by a price oracle\n TODO: trustless/decentrilezed price Oracle\n TODO: shall we use blockNumber instead of now for lastUpdated?\n TODO: consider if we need storing rates with variable decimals instead of fixed 4\n TODO: could we emit 1 RateChanged event from setMultipleRates (symbols and newrates arrays)?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\n\n\ncontract Rates is Restricted {\n using SafeMath for uint256;\n\n struct RateInfo {\n uint rate; // how much 1 WEI worth 1 unit , i.e. symbol/ETH rate\n // 0 rate means no rate info available\n uint lastUpdated;\n }\n\n // mapping currency symbol => rate. all rates are stored with 4 decimals. i.e. ETH/EUR = 989.12 then rate = 989,1200\n mapping(bytes32 => RateInfo) public rates;\n\n event RateChanged(bytes32 symbol, uint newRate);\n\n function setRate(bytes32 symbol, uint newRate) external restrict(\"setRate\") {\n rates[symbol] = RateInfo(newRate, now);\n emit RateChanged(symbol, newRate);\n }\n\n function setMultipleRates(bytes32[] symbols, uint[] newRates) external restrict(\"setRate\") {\n require(symbols.length == newRates.length, \"symobls and newRates lengths must be equal\");\n for (uint256 i = 0; i < symbols.length; i++) {\n rates[symbols[i]] = RateInfo(newRates[i], now);\n emit RateChanged(symbols[i], newRates[i]);\n }\n }\n\n function convertFromWei(bytes32 bSymbol, uint weiValue) external view returns(uint value) {\n require(rates[bSymbol].rate > 0, \"rates[bSymbol] must be > 0\");\n return weiValue.mul(rates[bSymbol].rate).roundedDiv(1000000000000000000);\n }\n\n function convertToWei(bytes32 bSymbol, uint value) external view returns(uint weiValue) {\n // next line would revert with div by zero but require to emit reason\n require(rates[bSymbol].rate > 0, \"rates[bSymbol] must be > 0\");\n /* TODO: can we make this not loosing max scale? */\n return value.mul(1000000000000000000).roundedDiv(rates[bSymbol].rate);\n }\n\n}\n" + }, + "0xd3ef19679c2dbbf3b8e2077c61b88f5e9c6178eb": { + "generatedAt": "2018-05-09T12:19:24.837Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:15.658Z", + "deployTransactionHash": "0xf54d0cd33fc046bdca1066c84c2c1af0a3064d14b41d820a952d6739e015de6b", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "7b3cb84bdbfbe54d0c253744ccc86bd9", + "deployedBytecodeHash": "36714b840505a1bedac90a82a36e514c", + "sourceHash": "e9dda67ca678fd3a4700e8970b50acd3", + "source": "/*\n Generic symbol / WEI rates contract.\n only callable by trusted price oracles.\n Being regularly called by a price oracle\n TODO: trustless/decentrilezed price Oracle\n TODO: shall we use blockNumber instead of now for lastUpdated?\n TODO: consider if we need storing rates with variable decimals instead of fixed 4\n TODO: could we emit 1 RateChanged event from setMultipleRates (symbols and newrates arrays)?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\n\n\ncontract Rates is Restricted {\n using SafeMath for uint256;\n\n struct RateInfo {\n uint rate; // how much 1 WEI worth 1 unit , i.e. symbol/ETH rate\n // 0 rate means no rate info available\n uint lastUpdated;\n }\n\n // mapping currency symbol => rate. all rates are stored with 4 decimals. i.e. ETH/EUR = 989.12 then rate = 989,1200\n mapping(bytes32 => RateInfo) public rates;\n\n event RateChanged(bytes32 symbol, uint newRate);\n\n function setRate(bytes32 symbol, uint newRate) external restrict(\"setRate\") {\n rates[symbol] = RateInfo(newRate, now);\n emit RateChanged(symbol, newRate);\n }\n\n function setMultipleRates(bytes32[] symbols, uint[] newRates) external restrict(\"setRate\") {\n require(symbols.length == newRates.length, \"symobls and newRates lengths must be equal\");\n for (uint256 i = 0; i < symbols.length; i++) {\n rates[symbols[i]] = RateInfo(newRates[i], now);\n emit RateChanged(symbols[i], newRates[i]);\n }\n }\n\n function convertFromWei(bytes32 bSymbol, uint weiValue) external view returns(uint value) {\n require(rates[bSymbol].rate > 0, \"rates[bSymbol] must be > 0\");\n return weiValue.mul(rates[bSymbol].rate).roundedDiv(1000000000000000000);\n }\n\n function convertToWei(bytes32 bSymbol, uint value) external view returns(uint weiValue) {\n // next line would revert with div by zero but require to emit reason\n require(rates[bSymbol].rate > 0, \"rates[bSymbol] must be > 0\");\n /* TODO: can we make this not loosing max scale? */\n return value.mul(1000000000000000000).roundedDiv(rates[bSymbol].rate);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/999/TokenAEur_DEPLOYS.json b/abiniser/deployments/999/TokenAEur_DEPLOYS.json index 0478d99a..74c9874b 100644 --- a/abiniser/deployments/999/TokenAEur_DEPLOYS.json +++ b/abiniser/deployments/999/TokenAEur_DEPLOYS.json @@ -1,6 +1,6 @@ { "contractName": "TokenAEur", - "latestAbiHash": "d7dd02520f2d92b2ca237f066cf2488d", + "latestAbiHash": "6b1597192b36a746724929ec9ee7b6b8", "deployedAbis": { "27721a2c77dc40da7639abd46791c3d7": { "latestDeployedAddress": "0x1b9441428f9e682bab4f9cc70fdf50adcc3411f4", @@ -53,6 +53,24 @@ "source": "/* Augmint Crypto Euro token (ACE) implementation */\npragma solidity ^0.4.23;\nimport \"./interfaces/TransferFeeInterface.sol\";\nimport \"./generic/AugmintToken.sol\";\n\n\ncontract TokenAEur is AugmintToken {\n constructor(TransferFeeInterface _feeAccount)\n public AugmintToken(\"Augmint Crypto Euro\", \"AEUR\", \"EUR\", 2, _feeAccount)\n {} // solhint-disable-line no-empty-blocks\n\n}\n" } } + }, + "6b1597192b36a746724929ec9ee7b6b8": { + "latestDeployedAddress": "0x1a93886326b0220731f25926d56074f7f0b074ce", + "deployments": { + "0x1a93886326b0220731f25926d56074f7f0b074ce": { + "generatedAt": "2018-05-09T12:19:24.881Z", + "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.167Z", + "deployTransactionHash": "0x43cafa531d88afc9f1e6c7ed2b61b446b4881da1ee87d4c52a99f582bae59cb2", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "f2b7e271f06e88e3b6b6bc423ff97850", + "deployedBytecodeHash": "0b4acdad4c4f7dc3b8cb55fe3479a743", + "sourceHash": "61dded7836f4431e621382340f32670c", + "source": "/* Augmint Crypto Euro token (ACE) implementation */\npragma solidity ^0.4.23;\nimport \"./interfaces/TransferFeeInterface.sol\";\nimport \"./generic/AugmintToken.sol\";\n\n\ncontract TokenAEur is AugmintToken {\n constructor(TransferFeeInterface _feeAccount)\n public AugmintToken(\"Augmint Crypto Euro\", \"AEUR\", \"EUR\", 2, _feeAccount)\n {} // solhint-disable-line no-empty-blocks\n\n}\n" + } + } } } } \ No newline at end of file From 2669e08dbd4be962dddb0f1b00a39c446d5e7c73 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Wed, 9 May 2018 18:05:42 +0100 Subject: [PATCH 19/26] deploy a set of legacy contracts for local testing new augminTokens --- migrations/97_add_legacyExchange.js | 32 ------------ migrations/98_add_legacyContracts.js | 75 ++++++++++++++++++++++++++++ migrations/98_add_legacyTokens.js | 22 -------- 3 files changed, 75 insertions(+), 54 deletions(-) delete mode 100644 migrations/97_add_legacyExchange.js create mode 100644 migrations/98_add_legacyContracts.js delete mode 100644 migrations/98_add_legacyTokens.js diff --git a/migrations/97_add_legacyExchange.js b/migrations/97_add_legacyExchange.js deleted file mode 100644 index fc9cf961..00000000 --- a/migrations/97_add_legacyExchange.js +++ /dev/null @@ -1,32 +0,0 @@ -/* add a legacy exchange contract with some orders for manual testing */ -const TokenAEur = artifacts.require("./TokenAEur.sol"); -const Exchange = artifacts.require("./Exchange.sol"); -const LoanManager = artifacts.require("./LoanManager.sol"); -const Rates = artifacts.require("./Rates.sol"); -const FeeAccount = artifacts.require("./FeeAccount.sol"); - -module.exports = async function(deployer, network, accounts) { - deployer.then(async () => { - const feeAccount = FeeAccount.at(FeeAccount.address); - const tokenAEur = TokenAEur.at(TokenAEur.address); - const loanManager = LoanManager.at(LoanManager.address); - const oldExchange = await Exchange.new(TokenAEur.address, Rates.address); - - await Promise.all([ - feeAccount.grantPermission(oldExchange.address, "NoFeeTransferContracts"), - oldExchange.grantPermission(accounts[0], "MonetaryBoard"), - loanManager.newEthBackedLoan(0, { value: web3.toWei(0.06568) }) // = 31 A-EUR - ]); - - await Promise.all([ - tokenAEur.transferAndNotify(oldExchange.address, 2000, 1010000), - tokenAEur.transferAndNotify(oldExchange.address, 1100, 980000), - oldExchange.placeBuyTokenOrder(990000, { value: web3.toWei(0.01) }), - oldExchange.placeBuyTokenOrder(1020000, { value: web3.toWei(0.011) }) - ]); - - console.log( - "On local ganache - deployed a mock legacy Exchange contract for manual testing at " + oldExchange.address - ); - }); -}; diff --git a/migrations/98_add_legacyContracts.js b/migrations/98_add_legacyContracts.js new file mode 100644 index 00000000..39c6e22e --- /dev/null +++ b/migrations/98_add_legacyContracts.js @@ -0,0 +1,75 @@ +/* deploy a legacy augmintToken, Locker, LoanManager & Exchange contracts + with test loans , locks & orders for local manual testing */ +const FeeAccount = artifacts.require("./FeeAccount.sol"); +const TokenAEur = artifacts.require("./TokenAEur.sol"); +const Rates = artifacts.require("./Rates.sol"); +const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); +const Locker = artifacts.require("./Locker.sol"); +const LoanManager = artifacts.require("./LoanManager.sol"); +const Exchange = artifacts.require("./Exchange.sol"); + +module.exports = async function(deployer, network, accounts) { + deployer.then(async () => { + const monetarySupervisor = MonetarySupervisor.at(MonetarySupervisor.address); + const feeAccount = FeeAccount.at(FeeAccount.address); + + const oldToken = await TokenAEur.new(FeeAccount.address); + + const [oldLocker, oldLoanManager, oldExchange] = await Promise.all([ + Locker.new(oldToken.address, MonetarySupervisor.address), + LoanManager.new(oldToken.address, MonetarySupervisor.address, Rates.address), + Exchange.new(oldToken.address, Rates.address) + ]); + + await Promise.all([ + oldToken.grantPermission(MonetarySupervisor.address, "MonetarySupervisorContract"), + + monetarySupervisor.setAcceptedLegacyAugmintToken(oldToken.address, true), + + oldToken.grantPermission(accounts[0], "MonetarySupervisorContract"), // "hack" for test to issue + + /* Locker permissions & products */ + oldLocker.grantPermission(accounts[0], "MonetaryBoard"), + monetarySupervisor.grantPermission(oldLocker.address, "LockerContracts"), + feeAccount.grantPermission(oldLocker.address, "NoFeeTransferContracts"), + oldLocker.addLockProduct(80001, 31536000, 1000, true), // 365 days, 8% p.a. + oldLocker.addLockProduct(1, 60, 1000, true), // 1 minute for testing, ~69.15% p.a. + + /* LoanManager permissions & products */ + oldLoanManager.grantPermission(accounts[0], "MonetaryBoard"), + monetarySupervisor.grantPermission(oldLoanManager.address, "LoanManagerContracts"), + feeAccount.grantPermission(oldLoanManager.address, "NoFeeTransferContracts"), + oldLoanManager.addLoanProduct(1, 999999, 990000, 1000, 50000, true), // defaults in 1 secs for testing ? p.a. + oldLoanManager.addLoanProduct(3600, 999989, 980000, 1000, 50000, true), // due in 1hr for testing repayments ? p.a. + oldLoanManager.addLoanProduct(31536000, 860000, 550000, 1000, 50000, true), // 365d, 14% p.a. + + /* Exchange permissions */ + feeAccount.grantPermission(oldExchange.address, "NoFeeTransferContracts"), + oldExchange.grantPermission(accounts[0], "MonetaryBoard") + ]); + + await oldToken.issueTo(accounts[0], 20000); // issue some to account 0 + + await Promise.all([ + oldToken.transferAndNotify(oldLocker.address, 1500, 0), + oldToken.transferAndNotify(oldLocker.address, 1600, 1), + + oldLoanManager.newEthBackedLoan(0, { value: web3.toWei(0.1) }), + oldLoanManager.newEthBackedLoan(1, { value: web3.toWei(0.11) }), + oldLoanManager.newEthBackedLoan(2, { value: web3.toWei(0.12) }), + + oldToken.transferAndNotify(oldExchange.address, 2000, 1010000), + oldToken.transferAndNotify(oldExchange.address, 1100, 980000), + oldExchange.placeBuyTokenOrder(990000, { value: web3.toWei(0.01) }), + oldExchange.placeBuyTokenOrder(1020000, { value: web3.toWei(0.011) }) + ]); + + console.log( + ` *** On local ganache - deployed a set of legacy mock contracts for manual testing: + TokenAEur: ${oldToken.address} + Locker: ${oldLocker.address} + LoanManager: ${oldLoanManager.address} + Exchange: ${oldExchange.address}` + ); + }); +}; diff --git a/migrations/98_add_legacyTokens.js b/migrations/98_add_legacyTokens.js deleted file mode 100644 index 5047dabd..00000000 --- a/migrations/98_add_legacyTokens.js +++ /dev/null @@ -1,22 +0,0 @@ -const FeeAccount = artifacts.require("./FeeAccount.sol"); -const TokenAEur = artifacts.require("./TokenAEur.sol"); -const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); - -module.exports = async function(deployer, network, accounts) { - deployer.then(async () => { - const monetarySupervisor = MonetarySupervisor.at(MonetarySupervisor.address); - const feeAccount = FeeAccount.at(FeeAccount.address); - const oldToken = await TokenAEur.new(FeeAccount.address); - - await Promise.all([ - oldToken.grantPermission(accounts[0], "MonetarySupervisorContract"), // "hack" for test to issue - feeAccount.grantPermission(monetarySupervisor.address, "NoFeeTransferContracts"), - monetarySupervisor.setAcceptedLegacyAugmintToken(oldToken.address, true) - ]); - - await oldToken.issueTo(accounts[0], 20000); // issue some to account 0 - console.log( - "On local ganache - deployed a mock legacy token contract for manual testing at " + oldToken.address - ); - }); -}; From d08f8ea86d21e46509bbf35d38abd6d2c43c6092 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Thu, 10 May 2018 07:16:56 -0400 Subject: [PATCH 20/26] adjust test values to changed migration data --- test/loanCollection.js | 2 +- test/loans.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/loanCollection.js b/test/loanCollection.js index 6ea7a142..544c689f 100644 --- a/test/loanCollection.js +++ b/test/loanCollection.js @@ -68,7 +68,7 @@ contract("Loans collection tests", accounts => { this, products.defaultingNoLeftOver, accounts[1], - global.web3v1.utils.toWei("1") + global.web3v1.utils.toWei("0.2") ); await Promise.all([rates.setRate("EUR", 99000), testHelpers.waitForTimeStamp(loan.maturity)]); diff --git a/test/loans.js b/test/loans.js index 9bae12df..00bc8891 100644 --- a/test/loans.js +++ b/test/loans.js @@ -11,7 +11,7 @@ let loanManager = null; let monetarySupervisor = null; let rates = null; -const ltdParams = { lockDifferenceLimit: 300000, loanDifferenceLimit: 200000, allowedDifferenceAmount: 100000 }; +const ltdParams = { lockDifferenceLimit: 300000, loanDifferenceLimit: 200000, allowedDifferenceAmount: 1000000 }; let products = {}; From 9d6de9ae681ce668d40d20f7d02df9bd698970aa Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 11 May 2018 17:04:13 -0400 Subject: [PATCH 21/26] got rid of minGasPrice from delegatedTransfer* fxs --- contracts/generic/AugmintToken.sol | 11 +--- .../interfaces/AugmintTokenInterface.sol | 4 -- test/delegatedTransfer.js | 61 +------------------ test/delegatedTransferAndNotify.js | 58 +----------------- 4 files changed, 5 insertions(+), 129 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index b4671a1c..6acb21be 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -40,8 +40,6 @@ contract AugmintToken is AugmintTokenInterface { /* Transfers based on an offline signed transfer instruction. */ function delegatedTransfer(address from, address to, uint amount, string narrative, - uint minGasPrice, /* client provided minimum gasPrice in wei - which she expects tx to be executed */ uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ bytes32 nonce, /* random nonce generated by client */ /* ^^^^ end of signed data ^^^^ */ @@ -49,10 +47,9 @@ contract AugmintToken is AugmintTokenInterface { uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external { - require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); - bytes32 txHash = keccak256(this, from, to, amount, narrative, minGasPrice, maxExecutorFeeInToken, nonce); + bytes32 txHash = keccak256(this, from, to, amount, narrative, maxExecutorFeeInToken, nonce); require(!delegatedTxHashesUsed[txHash], "txHash already used"); delegatedTxHashesUsed[txHash] = true; @@ -63,7 +60,6 @@ contract AugmintToken is AugmintTokenInterface { _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated transfer fee", 0); _transfer(from, to, amount, narrative); - } function approve(address _spender, uint256 amount) external returns (bool) { @@ -141,8 +137,6 @@ contract AugmintToken is AugmintTokenInterface { /* transferAndNotify based on an instruction signed offline */ function delegatedTransferAndNotify(address from, TokenReceiver target, uint amount, uint data, - uint minGasPrice, /* client provided minimum gasPrice in wei - which she expects tx to be executed */ uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ bytes32 nonce, /* random nonce generated by client */ /* ^^^^ end of signed data ^^^^ */ @@ -150,10 +144,9 @@ contract AugmintToken is AugmintTokenInterface { uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external { - require(tx.gasprice >= minGasPrice, "tx.gasprice must be >= minGasPrice"); require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); - bytes32 txHash = keccak256(this, from, target, amount, data, minGasPrice, maxExecutorFeeInToken, nonce); + bytes32 txHash = keccak256(this, from, target, amount, data, maxExecutorFeeInToken, nonce); require(!delegatedTxHashesUsed[txHash], "txHash already used"); delegatedTxHashesUsed[txHash] = true; diff --git a/contracts/interfaces/AugmintTokenInterface.sol b/contracts/interfaces/AugmintTokenInterface.sol index e629d2fd..062063cd 100644 --- a/contracts/interfaces/AugmintTokenInterface.sol +++ b/contracts/interfaces/AugmintTokenInterface.sol @@ -40,8 +40,6 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { function approve(address spender, uint value) external returns (bool); function delegatedTransfer(address from, address to, uint amount, string narrative, - uint minGasPrice, /* client provided minimum gasPrice in wei - which she expects tx to be executed */ uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ bytes32 nonce, /* random nonce generated by client */ /* ^^^^ end of signed data ^^^^ */ @@ -50,8 +48,6 @@ contract AugmintTokenInterface is Restricted, ERC20Interface { ) external; function delegatedTransferAndNotify(address from, TokenReceiver target, uint amount, uint data, - uint minGasPrice, /* client provided minimum gasPrice in wei - which she expects tx to be executed */ uint maxExecutorFeeInToken, /* client provided max fee for executing the tx */ bytes32 nonce, /* random nonce generated by client */ /* ^^^^ end of signed data ^^^^ */ diff --git a/test/delegatedTransfer.js b/test/delegatedTransfer.js index 6ac7d5bd..0d4573d6 100644 --- a/test/delegatedTransfer.js +++ b/test/delegatedTransfer.js @@ -3,7 +3,7 @@ const testHelpers = require("./helpers/testHelpers.js"); const TokenAEur = artifacts.require("TokenAEur.sol"); const FeeAccount = artifacts.require("FeeAccount.sol"); -const DELEGATED_TRANSFER_MAX_GAS = 120000; +const DELEGATED_TRANSFER_MAX_GAS = 150000; let tokenAEur; let from; @@ -18,7 +18,6 @@ const signDelegatedTransfer = async clientParams => { clientParams.from, clientParams.to, clientParams.amount, - clientParams.minGasPrice, clientParams.maxExecutorFee, clientParams.nonce ); @@ -29,7 +28,6 @@ const signDelegatedTransfer = async clientParams => { clientParams.to, clientParams.amount, clientParams.narrative, - clientParams.minGasPrice, clientParams.maxExecutorFee, clientParams.nonce ); @@ -56,13 +54,12 @@ const sendDelegatedTransfer = async (testInstance, clientParams, signature, exec clientParams.to, clientParams.amount, clientParams.narrative, - clientParams.minGasPrice, clientParams.maxExecutorFee, clientParams.nonce, signature, executorParams.requestedExecutorFee ) - .send({ from: executorParams.executorAddress, gas: 1200000, gasPrice: executorParams.actualGasPrice }); + .send({ from: executorParams.executorAddress, gas: 1200000 }); testHelpers.logGasUse(testInstance, tx, "delegatedTransfer"); // TODO: assert events @@ -108,14 +105,12 @@ contract("Delegated Transfers", accounts => { to: accounts[2], amount: 1000, narrative: "here we go", - minGasPrice: 1, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000001" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee }; @@ -131,14 +126,12 @@ contract("Delegated Transfers", accounts => { to: accounts[2], amount: 1000, narrative: "here we go", - minGasPrice: 2, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000002" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee }; @@ -156,14 +149,12 @@ contract("Delegated Transfers", accounts => { to: accounts[2], amount: 1000, narrative: "here we go", - minGasPrice: 1, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000003" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee - 10 }; @@ -179,14 +170,12 @@ contract("Delegated Transfers", accounts => { to: accounts[2], amount: 1000, narrative: "here we go", - minGasPrice: 2, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000004" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee + 1 }; @@ -194,50 +183,4 @@ contract("Delegated Transfers", accounts => { await testHelpers.expectThrow(sendDelegatedTransfer(this, clientParams, signature, executorParams)); }); - - it("should execute with higher gasPrice than signed", async function() { - const clientParams = { - tokenAEurAddress: TokenAEur.address, - from, - to: accounts[2], - amount: 1000, - narrative: "here we go", - minGasPrice: 1, - maxExecutorFee: 300, - nonce: "0x0000000000000000000000000000000000000000000000000000000000000005" // to be a random hash with proper entrophy - }; - - const executorParams = { - executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice + 1, - requestedExecutorFee: clientParams.maxExecutorFee - }; - - const signature = await signDelegatedTransfer(clientParams); - - await sendDelegatedTransfer(this, clientParams, signature, executorParams); - }); - - it("should not execute with lower gasPrice than signed", async function() { - const clientParams = { - tokenAEurAddress: TokenAEur.address, - from, - to: accounts[2], - amount: 1000, - narrative: "here we go", - minGasPrice: 2, - maxExecutorFee: 300, - nonce: "0x0000000000000000000000000000000000000000000000000000000000000006" // to be a random hash with proper entrophy - }; - - const executorParams = { - executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice - 1, - requestedExecutorFee: clientParams.maxExecutorFee - }; - - const signature = await signDelegatedTransfer(clientParams); - - await testHelpers.expectThrow(sendDelegatedTransfer(this, clientParams, signature, executorParams)); - }); }); diff --git a/test/delegatedTransferAndNotify.js b/test/delegatedTransferAndNotify.js index af3bfb44..422a02c4 100644 --- a/test/delegatedTransferAndNotify.js +++ b/test/delegatedTransferAndNotify.js @@ -18,7 +18,6 @@ const signDelegatedTransferAndNotify = async clientParams => { clientParams.target, clientParams.amount, clientParams.data, - clientParams.minGasPrice, clientParams.maxExecutorFee, clientParams.nonce ); @@ -47,13 +46,12 @@ const sendDelegatedTransferAndNotify = async (testInstance, clientParams, signat clientParams.target, clientParams.amount, clientParams.data, - clientParams.minGasPrice, clientParams.maxExecutorFee, clientParams.nonce, signature, executorParams.requestedExecutorFee ) - .send({ from: executorParams.executorAddress, gas: 1200000, gasPrice: executorParams.actualGasPrice }); + .send({ from: executorParams.executorAddress, gas: 1200000 }); testHelpers.logGasUse(testInstance, tx, "delegatedTransferAndNotify"); // TODO: assert events @@ -103,14 +101,12 @@ contract("Delegated transferAndNotify", accounts => { target: Locker.address, amount: 1000, data: lockProductId, - minGasPrice: 1, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000001" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee }; @@ -126,14 +122,12 @@ contract("Delegated transferAndNotify", accounts => { target: Locker.address, amount: 1000, data: lockProductId, - minGasPrice: 2, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000002" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee }; @@ -151,14 +145,12 @@ contract("Delegated transferAndNotify", accounts => { target: Locker.address, amount: 1000, data: lockProductId, - minGasPrice: 1, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000003" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee - 10 }; @@ -174,14 +166,12 @@ contract("Delegated transferAndNotify", accounts => { target: Locker.address, amount: 1000, data: lockProductId, - minGasPrice: 2, maxExecutorFee: 300, nonce: "0x0000000000000000000000000000000000000000000000000000000000000004" // to be a random hash with proper entrophy }; const executorParams = { executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice, requestedExecutorFee: clientParams.maxExecutorFee + 1 }; @@ -189,50 +179,4 @@ contract("Delegated transferAndNotify", accounts => { await testHelpers.expectThrow(sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams)); }); - - it("should execute with higher gasPrice than signed", async function() { - const clientParams = { - tokenAEurAddress: TokenAEur.address, - from, - target: Locker.address, - amount: 1000, - data: lockProductId, - minGasPrice: 1, - maxExecutorFee: 300, - nonce: "0x0000000000000000000000000000000000000000000000000000000000000005" // to be a random hash with proper entrophy - }; - - const executorParams = { - executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice + 1, - requestedExecutorFee: clientParams.maxExecutorFee - }; - - const signature = await signDelegatedTransferAndNotify(clientParams); - - await sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams); - }); - - it("should not execute with lower gasPrice than signed", async function() { - const clientParams = { - tokenAEurAddress: TokenAEur.address, - from, - target: Locker.address, - amount: 1000, - data: lockProductId, - minGasPrice: 2, - maxExecutorFee: 300, - nonce: "0x0000000000000000000000000000000000000000000000000000000000000006" // to be a random hash with proper entrophy - }; - - const executorParams = { - executorAddress: accounts[3], - actualGasPrice: clientParams.minGasPrice - 1, - requestedExecutorFee: clientParams.maxExecutorFee - }; - - const signature = await signDelegatedTransferAndNotify(clientParams); - - await testHelpers.expectThrow(sendDelegatedTransferAndNotify(this, clientParams, signature, executorParams)); - }); }); From 58634d61f8027473470c5745706b1bb75f7bbbfa Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 11 May 2018 17:18:27 -0400 Subject: [PATCH 22/26] delegatedTransfer* fx common parts into private fx --- contracts/generic/AugmintToken.sol | 34 +++++++++++++----------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index 6acb21be..8298bb27 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -47,18 +47,10 @@ contract AugmintToken is AugmintTokenInterface { uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external { - require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); - bytes32 txHash = keccak256(this, from, to, amount, narrative, maxExecutorFeeInToken, nonce); - require(!delegatedTxHashesUsed[txHash], "txHash already used"); - delegatedTxHashesUsed[txHash] = true; - - address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); + _checkHashAndTransferExecutorFee(txHash, signature, from, maxExecutorFeeInToken, requestedExecutorFeeInToken); - require(recovered == from, "invalid signature"); - - _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated transfer fee", 0); _transfer(from, to, amount, narrative); } @@ -144,18 +136,10 @@ contract AugmintToken is AugmintTokenInterface { uint requestedExecutorFeeInToken /* the executor can decide to request lower fee */ ) external { - require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); - bytes32 txHash = keccak256(this, from, target, amount, data, maxExecutorFeeInToken, nonce); - require(!delegatedTxHashesUsed[txHash], "txHash already used"); - delegatedTxHashesUsed[txHash] = true; - - address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); - - require(recovered == from, "invalid signature"); + _checkHashAndTransferExecutorFee(txHash, signature, from, maxExecutorFeeInToken, requestedExecutorFeeInToken); - _transfer(from, msg.sender, requestedExecutorFeeInToken, "Delegated execution fee", 0); _transfer(from, target, amount, ""); target.transferNotification(from, amount, data); } @@ -177,7 +161,19 @@ contract AugmintToken is AugmintTokenInterface { return allowed[_owner][_spender]; } - function _increaseApproval(address _approver, address _spender, uint _addedValue) internal returns (bool) { + function _checkHashAndTransferExecutorFee(bytes32 txHash, bytes signature, address signer, + uint maxExecutorFeeInToken, uint requestedExecutorFeeInToken) private { + require(requestedExecutorFeeInToken <= maxExecutorFeeInToken, "requestedExecutorFee must be <= maxExecutorFee"); + require(!delegatedTxHashesUsed[txHash], "txHash already used"); + delegatedTxHashesUsed[txHash] = true; + + address recovered = ECRecovery.recover(ECRecovery.toEthSignedMessageHash(txHash), signature); + require(recovered == signer, "invalid signature"); + + _transfer(signer, msg.sender, requestedExecutorFeeInToken, "Delegated transfer fee", 0); + } + + function _increaseApproval(address _approver, address _spender, uint _addedValue) private returns (bool) { allowed[_approver][_spender] = allowed[_approver][_spender].add(_addedValue); emit Approval(_approver, _spender, allowed[_approver][_spender]); } From 574350da35e75edfbbb06e687947805c27dc6a87 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 11 May 2018 17:19:26 -0400 Subject: [PATCH 23/26] emit ERC20 transfer for fee part of transfer too. --- contracts/generic/AugmintToken.sol | 1 + test/helpers/testHelpers.js | 106 +++++++++++++++++------------ test/helpers/tokenTestHelpers.js | 14 +++- 3 files changed, 75 insertions(+), 46 deletions(-) diff --git a/contracts/generic/AugmintToken.sol b/contracts/generic/AugmintToken.sol index 8298bb27..45ea966a 100644 --- a/contracts/generic/AugmintToken.sol +++ b/contracts/generic/AugmintToken.sol @@ -205,6 +205,7 @@ contract AugmintToken is AugmintTokenInterface { if (fee > 0) { balances[feeAccount] = balances[feeAccount].add(fee); + emit Transfer(from, feeAccount, fee); } balances[from] = balances[from].sub(amountWithFee); diff --git a/test/helpers/testHelpers.js b/test/helpers/testHelpers.js index 9b4414a4..7c910a3d 100644 --- a/test/helpers/testHelpers.js +++ b/test/helpers/testHelpers.js @@ -86,63 +86,79 @@ async function getGasCost(gas) { return gasPrice.mul(gas); } -async function assertEvent(contractInstance, eventName, expectedArgs) { +async function assertEvent(contractInstance, eventName, _expectedArgs) { + let expectedArgsArray; + if (!Array.isArray(_expectedArgs)) { + expectedArgsArray = [_expectedArgs]; + } else { + expectedArgsArray = _expectedArgs; + } const events = await getEvents(contractInstance, eventName); - assert(events.length === 1, `Expected ${eventName} event wasn't received from ${contractInstance.address}`); // how to get contract name? - const event = events[0]; + assert( + events.length === expectedArgsArray.length, + `Expected ${expectedArgsArray.length} ${eventName} events from ${contractInstance.address} but received ${ + events.length + }` + ); // how to get contract name? + + const ret = {}; // we return values from event (useful when custom validator passed for an id) - assert(event.event === eventName, `Expected ${eventName} event but got ${event.event}`); + for (let i = 0; i < events.length; i++) { + const event = events[i]; + const expectedArgs = expectedArgsArray[i]; - const eventArgs = event.args; + assert(event.event === eventName, `Expected ${eventName} event but got ${event.event}`); - const expectedArgNames = Object.keys(expectedArgs); - const receivedArgNames = Object.keys(eventArgs); + const eventArgs = event.args; - assert( - expectedArgNames.length === receivedArgNames.length, - `Expected ${eventName} event to have ${expectedArgNames.length} arguments, but it had ${ - receivedArgNames.length - }` - ); + const expectedArgNames = Object.keys(expectedArgs); + const receivedArgNames = Object.keys(eventArgs); - const ret = {}; // we return values from event (useful when custom validator passed for an id) - expectedArgNames.forEach(argName => { assert( - typeof event.args[argName] !== "undefined", - `${argName} expected in ${eventName} event but it's not found` + expectedArgNames.length === receivedArgNames.length, + `Expected ${eventName} event to have ${expectedArgNames.length} arguments, but it had ${ + receivedArgNames.length + }` ); - const expectedValue = expectedArgs[argName]; - let value; - switch (typeof expectedValue) { - case "function": - value = expectedValue(event.args[argName]); - break; - case "number": - value = - typeof event.args[argName].toNumber === "function" - ? event.args[argName].toNumber() - : event.args[argName]; - break; - case "string": - value = - typeof event.args[argName].toString === "function" - ? event.args[argName].toString() - : event.args[argName]; - break; - default: - value = event.args[argName]; - } - - if (typeof expectedValue !== "function") { + expectedArgNames.forEach(argName => { assert( - value === expectedValue, - `Event ${eventName} has ${argName} arg with a value of ${value} but expected ${expectedValue}` + typeof event.args[argName] !== "undefined", + `${argName} expected in ${eventName} event but it's not found` ); - } - ret[argName] = value; - }); + + const expectedValue = expectedArgs[argName]; + let value; + switch (typeof expectedValue) { + case "function": + value = expectedValue(event.args[argName]); + break; + case "number": + value = + typeof event.args[argName].toNumber === "function" + ? event.args[argName].toNumber() + : event.args[argName]; + break; + case "string": + value = + typeof event.args[argName].toString === "function" + ? event.args[argName].toString() + : event.args[argName]; + break; + default: + value = event.args[argName]; + } + + if (typeof expectedValue !== "function") { + assert( + value === expectedValue, + `Event ${eventName} has ${argName} arg with a value of ${value} but expected ${expectedValue}` + ); + } + ret[argName] = value; + }); + } return ret; } diff --git a/test/helpers/tokenTestHelpers.js b/test/helpers/tokenTestHelpers.js index 126a539e..73f340d6 100644 --- a/test/helpers/tokenTestHelpers.js +++ b/test/helpers/tokenTestHelpers.js @@ -281,11 +281,23 @@ async function transferEventAsserts(expTransfer) { narrative: expTransfer.narrative }); - await testHelpers.assertEvent(augmintToken, "Transfer", { + const expTransferEvents = []; + + if (expTransfer.fee > 0) { + expTransferEvents.push({ + from: expTransfer.from, + to: feeAccount.address, + amount: expTransfer.fee.toString() + }); + } + + expTransferEvents.push({ from: expTransfer.from, to: expTransfer.to, amount: expTransfer.amount.toString() }); + + await testHelpers.assertEvent(augmintToken, "Transfer", expTransferEvents); } async function approveEventAsserts(expApprove) { From 72412233325316ee3280d6a64de1f07f8c573519 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 11 May 2018 20:27:46 -0400 Subject: [PATCH 24/26] update ganache abi & deploy info --- ..._ABI_4b49e7e6d1a9a2de81a4d2d088acbc04.json | 787 ++++++++++++++++++ .../999/AugmintReserves_DEPLOYS.json | 10 +- .../deployments/999/Exchange_DEPLOYS.json | 10 +- .../deployments/999/FeeAccount_DEPLOYS.json | 10 +- .../999/InterestEarnedAccount_DEPLOYS.json | 10 +- .../deployments/999/LoanManager_DEPLOYS.json | 10 +- abiniser/deployments/999/Locker_DEPLOYS.json | 10 +- .../999/MonetarySupervisor_DEPLOYS.json | 10 +- .../deployments/999/TokenAEur_DEPLOYS.json | 20 +- 9 files changed, 841 insertions(+), 36 deletions(-) create mode 100644 abiniser/abis/TokenAEur_ABI_4b49e7e6d1a9a2de81a4d2d088acbc04.json diff --git a/abiniser/abis/TokenAEur_ABI_4b49e7e6d1a9a2de81a4d2d088acbc04.json b/abiniser/abis/TokenAEur_ABI_4b49e7e6d1a9a2de81a4d2d088acbc04.json new file mode 100644 index 00000000..0d89825a --- /dev/null +++ b/abiniser/abis/TokenAEur_ABI_4b49e7e6d1a9a2de81a4d2d088acbc04.json @@ -0,0 +1,787 @@ +{ + "contractName": "TokenAEur", + "abiHash": "4b49e7e6d1a9a2de81a4d2d088acbc04", + "generatedAt": "2018-05-12T00:22:15.160Z", + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "data", + "type": "uint256" + } + ], + "name": "transferAndNotify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "issueTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balances", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "peggedSymbol", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newFeeAccount", + "type": "address" + } + ], + "name": "setFeeAccount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "permissions", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermission", + "type": "bytes32" + } + ], + "name": "revokePermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeAccount", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "narrative", + "type": "string" + } + ], + "name": "transferFromWithNarrative", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermissions", + "type": "bytes32[]" + } + ], + "name": "revokeMultiplePermissions", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermissions", + "type": "bytes32[]" + } + ], + "name": "grantMultiplePermissions", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "narrative", + "type": "string" + }, + { + "name": "maxExecutorFeeInToken", + "type": "uint256" + }, + { + "name": "nonce", + "type": "bytes32" + }, + { + "name": "signature", + "type": "bytes" + }, + { + "name": "requestedExecutorFeeInToken", + "type": "uint256" + } + ], + "name": "delegatedTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "target", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "data", + "type": "uint256" + }, + { + "name": "maxExecutorFeeInToken", + "type": "uint256" + }, + { + "name": "nonce", + "type": "bytes32" + }, + { + "name": "signature", + "type": "bytes" + }, + { + "name": "requestedExecutorFeeInToken", + "type": "uint256" + } + ], + "name": "delegatedTransferAndNotify", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "delegatedTxHashesUsed", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "agent", + "type": "address" + }, + { + "name": "requiredPermission", + "type": "bytes32" + } + ], + "name": "grantPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "narrative", + "type": "string" + } + ], + "name": "transferWithNarrative", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_feeAccount", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "newFeeAccount", + "type": "address" + } + ], + "name": "FeeAccountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "transferFeePt", + "type": "uint256" + }, + { + "indexed": false, + "name": "transferFeeMin", + "type": "uint256" + }, + { + "indexed": false, + "name": "transferFeeMax", + "type": "uint256" + } + ], + "name": "TransferFeesChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "name": "narrative", + "type": "string" + }, + { + "indexed": false, + "name": "fee", + "type": "uint256" + } + ], + "name": "AugmintTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenIssued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "agent", + "type": "address" + }, + { + "indexed": false, + "name": "grantedPermission", + "type": "bytes32" + } + ], + "name": "PermissionGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "agent", + "type": "address" + }, + { + "indexed": false, + "name": "revokedPermission", + "type": "bytes32" + } + ], + "name": "PermissionRevoked", + "type": "event" + } + ] +} \ No newline at end of file diff --git a/abiniser/deployments/999/AugmintReserves_DEPLOYS.json b/abiniser/deployments/999/AugmintReserves_DEPLOYS.json index f7d1ab6d..76a4f02a 100644 --- a/abiniser/deployments/999/AugmintReserves_DEPLOYS.json +++ b/abiniser/deployments/999/AugmintReserves_DEPLOYS.json @@ -36,15 +36,15 @@ "source": "/* Contract to hold Augmint reserves (ETH & Token)\n - ETH as regular ETH balance of the contract\n - ERC20 token reserve (stored as regular Token balance under the contract address)\n\nNB: reserves are held under the contract address, therefore any transaction on the reserve is limited to the\n tx-s defined here (i.e. transfer is not allowed even by the contract owner or MonetaryBoard or MonetarySupervisor)\n\n */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract AugmintReserves is SystemAccount {\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into reserve (from defaulted loan's collateral )\n }\n\n function burn(AugmintTokenInterface augmintToken, uint amount) external restrict(\"MonetarySupervisorContract\") {\n augmintToken.burn(amount);\n }\n\n}\n" }, "0xf4ab8fd7af56fb99e7bf5e5edc0b407da530a4f4": { - "generatedAt": "2018-05-09T12:19:24.853Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:10.377Z", - "deployTransactionHash": "0xb340856da28cfea14a94bebe3b91a6a553f8be51a5e090eaacaf9bd4a2860ec7", + "generatedAt": "2018-05-12T00:22:15.136Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:05.945Z", + "deployTransactionHash": "0xfd91f3b00fe09f5c641919e0de3dac8645d5e460fa53f4151458fe3fa540d864", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "f567a1bdc324dae94eb757065e518ecf", - "deployedBytecodeHash": "589e97f873d78017e591d3435d7b1b04", + "bytecodeHash": "380f5b0d9fdbaed55c151dfd0f5f78c4", + "deployedBytecodeHash": "a707a4a1296cb1140d4610935ad58f8c", "sourceHash": "c3fd200d4510ea1b3d7c6c7ae04683b9", "source": "/* Contract to hold Augmint reserves (ETH & Token)\n - ETH as regular ETH balance of the contract\n - ERC20 token reserve (stored as regular Token balance under the contract address)\n\nNB: reserves are held under the contract address, therefore any transaction on the reserve is limited to the\n tx-s defined here (i.e. transfer is not allowed even by the contract owner or MonetaryBoard or MonetarySupervisor)\n\n */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract AugmintReserves is SystemAccount {\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into reserve (from defaulted loan's collateral )\n }\n\n function burn(AugmintTokenInterface augmintToken, uint amount) external restrict(\"MonetarySupervisorContract\") {\n augmintToken.burn(amount);\n }\n\n}\n" } diff --git a/abiniser/deployments/999/Exchange_DEPLOYS.json b/abiniser/deployments/999/Exchange_DEPLOYS.json index 6c3a16cf..edf24c27 100644 --- a/abiniser/deployments/999/Exchange_DEPLOYS.json +++ b/abiniser/deployments/999/Exchange_DEPLOYS.json @@ -97,15 +97,15 @@ "source": "/* Augmint's Internal Exchange\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/exchangeFlow.png\n\n TODO:\n - change to wihtdrawal pattern, see: https://github.com/Augmint/augmint-contracts/issues/17\n - deduct fee\n - consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)\n - use Rates interface?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./Rates.sol\";\n\n\ncontract Exchange is Restricted {\n using SafeMath for uint256;\n\n AugmintTokenInterface public augmintToken;\n Rates public rates;\n\n uint public constant CHUNK_SIZE = 100;\n\n struct Order {\n uint64 index;\n address maker;\n\n // % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million\n // I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity\n uint32 price;\n\n // buy order: amount in wei\n // sell order: token amount\n uint amount;\n }\n\n uint64 public orderCount;\n mapping(uint64 => Order) public buyTokenOrders;\n mapping(uint64 => Order) public sellTokenOrders;\n\n uint64[] private activeBuyOrders;\n uint64[] private activeSellOrders;\n\n /* used to stop executing matchMultiple when running out of gas.\n actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/\n uint32 private constant ORDER_MATCH_WORST_GAS = 100000;\n\n event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);\n\n event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,\n uint64 sellTokenOrderId, uint publishedRate, uint32 price, uint fillRate, uint weiAmount, uint tokenAmount);\n\n event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);\n\n event RatesContractChanged(Rates newRatesContract);\n\n constructor(AugmintTokenInterface _augmintToken, Rates _rates) public {\n augmintToken = _augmintToken;\n rates = _rates;\n }\n\n /* to allow upgrade of Rates contract */\n function setRatesContract(Rates newRatesContract)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n emit RatesContractChanged(newRatesContract);\n }\n\n function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(msg.value > 0, \"msg.value must be > 0\");\n\n orderId = ++orderCount;\n buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);\n activeBuyOrders.push(orderId);\n\n emit NewOrder(orderId, msg.sender, price, 0, msg.value);\n }\n\n /* this function requires previous approval to transfer tokens */\n function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {\n augmintToken.transferFrom(msg.sender, this, tokenAmount);\n return _placeSellTokenOrder(msg.sender, price, tokenAmount);\n }\n\n /* place sell token order called from AugmintToken's transferAndNotify\n Flow:\n 1) user calls token contract's transferAndNotify price passed in data arg\n 2) transferAndNotify transfers tokens to the Exchange contract\n 3) transferAndNotify calls Exchange.transferNotification with lockProductId\n */\n function transferNotification(address maker, uint tokenAmount, uint price) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n _placeSellTokenOrder(maker, uint32(price), tokenAmount);\n }\n\n function cancelBuyTokenOrder(uint64 buyTokenId) external {\n Order storage order = buyTokenOrders[buyTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeBuyOrder(order);\n\n msg.sender.transfer(amount);\n\n emit CancelledOrder(buyTokenId, msg.sender, 0, amount);\n }\n\n function cancelSellTokenOrder(uint64 sellTokenId) external {\n Order storage order = sellTokenOrders[sellTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeSellOrder(order);\n\n augmintToken.transferWithNarrative(msg.sender, amount, \"Sell token order cancelled\");\n\n emit CancelledOrder(sellTokenId, msg.sender, amount, 0);\n }\n\n /* matches any two orders if the sell price >= buy price\n trade price meets in the middle\n reverts if any of the orders have been removed\n */\n function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {\n _fillOrder(buyTokenId, sellTokenId);\n }\n\n /* matches as many orders as possible from the passed orders\n Runs as long as gas is available for the call.\n Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)\n */\n function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {\n uint len = buyTokenIds.length;\n require(len == sellTokenIds.length, \"buyTokenIds and sellTokenIds lengths must be equal\");\n\n for (uint i = 0; i < len && gasleft() > ORDER_MATCH_WORST_GAS; i++) {\n _fillOrder(buyTokenIds[i], sellTokenIds[i]);\n matchCount++;\n }\n }\n\n function getActiveOrderCounts() external view returns(uint buyTokenOrderCount, uint sellTokenOrderCount) {\n return(activeBuyOrders.length, activeSellOrders.length);\n }\n\n // returns CHUNK_SIZE orders starting from offset\n // orders are encoded as [id, maker, price, amount]\n function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {\n uint64 orderId = activeBuyOrders[offset + i];\n Order storage order = buyTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {\n uint64 orderId = activeSellOrders[offset + i];\n Order storage order = sellTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {\n Order storage buy = buyTokenOrders[buyTokenId];\n Order storage sell = sellTokenOrders[sellTokenId];\n\n require(buy.price >= sell.price, \"buy price must be >= sell price\");\n\n // pick maker's price (whoever placed order sooner considered as maker)\n uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;\n\n uint publishedRate;\n (publishedRate, ) = rates.rates(augmintToken.peggedSymbol());\n uint fillRate = publishedRate.mul(price).roundedDiv(1000000);\n\n uint sellWei = sell.amount.mul(1 ether).roundedDiv(fillRate);\n\n uint tradedWei;\n uint tradedTokens;\n if (sellWei <= buy.amount) {\n tradedWei = sellWei;\n tradedTokens = sell.amount;\n } else {\n tradedWei = buy.amount;\n tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);\n }\n\n buy.amount = buy.amount.sub(tradedWei);\n if (buy.amount == 0) {\n _removeBuyOrder(buy);\n }\n\n sell.amount = sell.amount.sub(tradedTokens);\n if (sell.amount == 0) {\n _removeSellOrder(sell);\n }\n\n augmintToken.transferWithNarrative(buy.maker, tradedTokens, \"Buy token order fill\");\n sell.maker.transfer(tradedWei);\n\n emit OrderFill(buy.maker, sell.maker, buyTokenId,\n sellTokenId, publishedRate, price, fillRate, tradedWei, tradedTokens);\n }\n\n function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)\n private returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(tokenAmount > 0, \"tokenAmount must be > 0\");\n\n orderId = ++orderCount;\n sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);\n activeSellOrders.push(orderId);\n\n emit NewOrder(orderId, maker, price, tokenAmount, 0);\n }\n\n function _removeBuyOrder(Order storage order) private {\n _removeOrder(activeBuyOrders, order.index);\n }\n\n function _removeSellOrder(Order storage order) private {\n _removeOrder(activeSellOrders, order.index);\n }\n\n function _removeOrder(uint64[] storage orders, uint64 index) private {\n if (index < orders.length - 1) {\n orders[index] = orders[orders.length - 1];\n }\n orders.length--;\n }\n\n}\n" }, "0x367f6272f3c4146f045f19be8774d97fd0966af5": { - "generatedAt": "2018-05-09T12:19:25.093Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.209Z", - "deployTransactionHash": "0x7c89a08ad305d1e37d35c4c74397780c94fb04c6bcde996a2e3c02c972256ca7", + "generatedAt": "2018-05-12T00:22:15.270Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:12.453Z", + "deployTransactionHash": "0x5f0778b2c07d8392ba59c905b51eb0f528d89948108b6f9adcc1aca28982f35d", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "7f101651e2f05175f4fa67b4cd87d831", - "deployedBytecodeHash": "cbee9a74a02a651fb2c2a64091ddcbe9", + "bytecodeHash": "0c3aae2a1e26a2cc4f1800e62fa873ee", + "deployedBytecodeHash": "4c6ad15993389d8d7326c731b2b92d63", "sourceHash": "6e27bdbdc64d0d9cf52de1abc46c4cf7", "source": "/* Augmint's Internal Exchange\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/exchangeFlow.png\n\n TODO:\n - change to wihtdrawal pattern, see: https://github.com/Augmint/augmint-contracts/issues/17\n - deduct fee\n - consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)\n - use Rates interface?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./Rates.sol\";\n\n\ncontract Exchange is Restricted {\n using SafeMath for uint256;\n\n AugmintTokenInterface public augmintToken;\n Rates public rates;\n\n uint public constant CHUNK_SIZE = 100;\n\n struct Order {\n uint64 index;\n address maker;\n\n // % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million\n // I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity\n uint32 price;\n\n // buy order: amount in wei\n // sell order: token amount\n uint amount;\n }\n\n uint64 public orderCount;\n mapping(uint64 => Order) public buyTokenOrders;\n mapping(uint64 => Order) public sellTokenOrders;\n\n uint64[] private activeBuyOrders;\n uint64[] private activeSellOrders;\n\n /* used to stop executing matchMultiple when running out of gas.\n actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/\n uint32 private constant ORDER_MATCH_WORST_GAS = 100000;\n\n event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);\n\n event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,\n uint64 sellTokenOrderId, uint publishedRate, uint32 price, uint fillRate, uint weiAmount, uint tokenAmount);\n\n event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);\n\n event RatesContractChanged(Rates newRatesContract);\n\n constructor(AugmintTokenInterface _augmintToken, Rates _rates) public {\n augmintToken = _augmintToken;\n rates = _rates;\n }\n\n /* to allow upgrade of Rates contract */\n function setRatesContract(Rates newRatesContract)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n emit RatesContractChanged(newRatesContract);\n }\n\n function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(msg.value > 0, \"msg.value must be > 0\");\n\n orderId = ++orderCount;\n buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);\n activeBuyOrders.push(orderId);\n\n emit NewOrder(orderId, msg.sender, price, 0, msg.value);\n }\n\n /* this function requires previous approval to transfer tokens */\n function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {\n augmintToken.transferFrom(msg.sender, this, tokenAmount);\n return _placeSellTokenOrder(msg.sender, price, tokenAmount);\n }\n\n /* place sell token order called from AugmintToken's transferAndNotify\n Flow:\n 1) user calls token contract's transferAndNotify price passed in data arg\n 2) transferAndNotify transfers tokens to the Exchange contract\n 3) transferAndNotify calls Exchange.transferNotification with lockProductId\n */\n function transferNotification(address maker, uint tokenAmount, uint price) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n _placeSellTokenOrder(maker, uint32(price), tokenAmount);\n }\n\n function cancelBuyTokenOrder(uint64 buyTokenId) external {\n Order storage order = buyTokenOrders[buyTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeBuyOrder(order);\n\n msg.sender.transfer(amount);\n\n emit CancelledOrder(buyTokenId, msg.sender, 0, amount);\n }\n\n function cancelSellTokenOrder(uint64 sellTokenId) external {\n Order storage order = sellTokenOrders[sellTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeSellOrder(order);\n\n augmintToken.transferWithNarrative(msg.sender, amount, \"Sell token order cancelled\");\n\n emit CancelledOrder(sellTokenId, msg.sender, amount, 0);\n }\n\n /* matches any two orders if the sell price >= buy price\n trade price meets in the middle\n reverts if any of the orders have been removed\n */\n function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {\n _fillOrder(buyTokenId, sellTokenId);\n }\n\n /* matches as many orders as possible from the passed orders\n Runs as long as gas is available for the call.\n Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)\n */\n function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {\n uint len = buyTokenIds.length;\n require(len == sellTokenIds.length, \"buyTokenIds and sellTokenIds lengths must be equal\");\n\n for (uint i = 0; i < len && gasleft() > ORDER_MATCH_WORST_GAS; i++) {\n _fillOrder(buyTokenIds[i], sellTokenIds[i]);\n matchCount++;\n }\n }\n\n function getActiveOrderCounts() external view returns(uint buyTokenOrderCount, uint sellTokenOrderCount) {\n return(activeBuyOrders.length, activeSellOrders.length);\n }\n\n // returns CHUNK_SIZE orders starting from offset\n // orders are encoded as [id, maker, price, amount]\n function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {\n uint64 orderId = activeBuyOrders[offset + i];\n Order storage order = buyTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {\n uint64 orderId = activeSellOrders[offset + i];\n Order storage order = sellTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {\n Order storage buy = buyTokenOrders[buyTokenId];\n Order storage sell = sellTokenOrders[sellTokenId];\n\n require(buy.price >= sell.price, \"buy price must be >= sell price\");\n\n // pick maker's price (whoever placed order sooner considered as maker)\n uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;\n\n uint publishedRate;\n (publishedRate, ) = rates.rates(augmintToken.peggedSymbol());\n uint fillRate = publishedRate.mul(price).roundedDiv(1000000);\n\n uint sellWei = sell.amount.mul(1 ether).roundedDiv(fillRate);\n\n uint tradedWei;\n uint tradedTokens;\n if (sellWei <= buy.amount) {\n tradedWei = sellWei;\n tradedTokens = sell.amount;\n } else {\n tradedWei = buy.amount;\n tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);\n }\n\n buy.amount = buy.amount.sub(tradedWei);\n if (buy.amount == 0) {\n _removeBuyOrder(buy);\n }\n\n sell.amount = sell.amount.sub(tradedTokens);\n if (sell.amount == 0) {\n _removeSellOrder(sell);\n }\n\n augmintToken.transferWithNarrative(buy.maker, tradedTokens, \"Buy token order fill\");\n sell.maker.transfer(tradedWei);\n\n emit OrderFill(buy.maker, sell.maker, buyTokenId,\n sellTokenId, publishedRate, price, fillRate, tradedWei, tradedTokens);\n }\n\n function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)\n private returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(tokenAmount > 0, \"tokenAmount must be > 0\");\n\n orderId = ++orderCount;\n sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);\n activeSellOrders.push(orderId);\n\n emit NewOrder(orderId, maker, price, tokenAmount, 0);\n }\n\n function _removeBuyOrder(Order storage order) private {\n _removeOrder(activeBuyOrders, order.index);\n }\n\n function _removeSellOrder(Order storage order) private {\n _removeOrder(activeSellOrders, order.index);\n }\n\n function _removeOrder(uint64[] storage orders, uint64 index) private {\n if (index < orders.length - 1) {\n orders[index] = orders[orders.length - 1];\n }\n orders.length--;\n }\n\n}\n" } diff --git a/abiniser/deployments/999/FeeAccount_DEPLOYS.json b/abiniser/deployments/999/FeeAccount_DEPLOYS.json index 60657969..e832bca1 100644 --- a/abiniser/deployments/999/FeeAccount_DEPLOYS.json +++ b/abiniser/deployments/999/FeeAccount_DEPLOYS.json @@ -36,15 +36,15 @@ "source": "/* Contract to collect fees from system\n TODO: calculateExchangeFee + Exchange params and setters\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/TransferFeeInterface.sol\";\n\n\ncontract FeeAccount is SystemAccount, TransferFeeInterface {\n\n using SafeMath for uint256;\n\n struct TransferFee {\n uint pt; // in parts per million (ppm) , ie. 2,000 = 0.2%\n uint min; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n uint max; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n }\n\n TransferFee public transferFee;\n\n event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax);\n\n constructor(uint transferFeePt, uint transferFeeMin, uint transferFeeMax) public {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into feeAccount (defaulting fee in ETH )\n }\n\n function setTransferFees(uint transferFeePt, uint transferFeeMin, uint transferFeeMax)\n external restrict(\"MonetaryBoard\") {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n emit TransferFeesChanged(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function calculateTransferFee(address from, address to, uint amount) external view returns (uint256 fee) {\n if (!permissions[from][\"NoFeeTransferContracts\"] && !permissions[to][\"NoFeeTransferContracts\"]) {\n fee = amount.mul(transferFee.pt).div(1000000);\n if (fee > transferFee.max) {\n fee = transferFee.max;\n } else if (fee < transferFee.min) {\n fee = transferFee.min;\n }\n }\n return fee;\n }\n\n function calculateExchangeFee(uint weiAmount) external view returns (uint256 weiFee) {\n /* TODO: to be implemented and use in Exchange.sol. always revert for now */\n require(weiAmount != weiAmount, \"not yet implemented\");\n weiFee = transferFee.max; // to silence compiler warnings until it's implemented\n }\n\n}\n" }, "0xbed57eb0b4232da0cddd3c9c27490fc0759e0a01": { - "generatedAt": "2018-05-09T12:19:24.863Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.182Z", - "deployTransactionHash": "0x7b0979e47c736474f40783c225a9dacd926d2f452725274493d5c7b122a6024d", + "generatedAt": "2018-05-12T00:22:15.150Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:12.418Z", + "deployTransactionHash": "0x30eed67ceb1c3ebd5c235da1cf58a490de7a95f6c7fe90998b60184e254bbc42", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "1aee8ce6c1ed891053a114cc53e90661", - "deployedBytecodeHash": "9449685df0e18e918086c1eb18a3cb90", + "bytecodeHash": "7b671216a10bbb9643a8b9f48eabe6ee", + "deployedBytecodeHash": "aaa8149858e7a4fda67a0feeaf2846c5", "sourceHash": "fe888fa25a5c364abea5c0856f1ebd5b", "source": "/* Contract to collect fees from system\n TODO: calculateExchangeFee + Exchange params and setters\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/TransferFeeInterface.sol\";\n\n\ncontract FeeAccount is SystemAccount, TransferFeeInterface {\n\n using SafeMath for uint256;\n\n struct TransferFee {\n uint pt; // in parts per million (ppm) , ie. 2,000 = 0.2%\n uint min; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n uint max; // with base unit of augmint token, eg. 2 decimals for token, eg. 310 = 3.1 ACE\n }\n\n TransferFee public transferFee;\n\n event TransferFeesChanged(uint transferFeePt, uint transferFeeMin, uint transferFeeMax);\n\n constructor(uint transferFeePt, uint transferFeeMin, uint transferFeeMax) public {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function () public payable { // solhint-disable-line no-empty-blocks\n // to accept ETH sent into feeAccount (defaulting fee in ETH )\n }\n\n function setTransferFees(uint transferFeePt, uint transferFeeMin, uint transferFeeMax)\n external restrict(\"MonetaryBoard\") {\n transferFee = TransferFee(transferFeePt, transferFeeMin, transferFeeMax);\n emit TransferFeesChanged(transferFeePt, transferFeeMin, transferFeeMax);\n }\n\n function calculateTransferFee(address from, address to, uint amount) external view returns (uint256 fee) {\n if (!permissions[from][\"NoFeeTransferContracts\"] && !permissions[to][\"NoFeeTransferContracts\"]) {\n fee = amount.mul(transferFee.pt).div(1000000);\n if (fee > transferFee.max) {\n fee = transferFee.max;\n } else if (fee < transferFee.min) {\n fee = transferFee.min;\n }\n }\n return fee;\n }\n\n function calculateExchangeFee(uint weiAmount) external view returns (uint256 weiFee) {\n /* TODO: to be implemented and use in Exchange.sol. always revert for now */\n require(weiAmount != weiAmount, \"not yet implemented\");\n weiFee = transferFee.max; // to silence compiler warnings until it's implemented\n }\n\n}\n" } diff --git a/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json b/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json index cd2da383..2b5a0c57 100644 --- a/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json +++ b/abiniser/deployments/999/InterestEarnedAccount_DEPLOYS.json @@ -48,15 +48,15 @@ "source": "/* Contract to hold earned interest from loans repaid\n premiums for locks are being accrued (i.e. transferred) to Locker */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract InterestEarnedAccount is SystemAccount {\n\n function transferInterest(AugmintTokenInterface augmintToken, address locker, uint interestAmount)\n external restrict(\"MonetarySupervisorContract\") {\n augmintToken.transfer(locker, interestAmount);\n }\n\n}\n" }, "0x1b9441428f9e682bab4f9cc70fdf50adcc3411f4": { - "generatedAt": "2018-05-09T12:19:24.870Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:14.518Z", - "deployTransactionHash": "0x4a2ddab9798f94b774cf51564da108298989ae38eea8945303d43d885e4a7382", + "generatedAt": "2018-05-12T00:22:15.155Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:09.283Z", + "deployTransactionHash": "0x2cdb7fd6f6500ec39f69a8633af900a2264fe9b6b2696b3f5930af950a708a24", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "5fcc9d704336df748a50d2813334e9bf", - "deployedBytecodeHash": "345c5e752b0283e593d29d6193059fdd", + "bytecodeHash": "435a1a8da265b92f0ae3fd4eb5089baa", + "deployedBytecodeHash": "f7581b87c6854d5aec16c65f738876d8", "sourceHash": "080bdccd163b74b49fb908b443c45199", "source": "/* Contract to hold earned interest from loans repaid\n premiums for locks are being accrued (i.e. transferred) to Locker */\n\npragma solidity ^0.4.23;\nimport \"./generic/SystemAccount.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\n\n\ncontract InterestEarnedAccount is SystemAccount {\n\n function transferInterest(AugmintTokenInterface augmintToken, address locker, uint interestAmount)\n external restrict(\"MonetarySupervisorContract\") {\n augmintToken.transfer(locker, interestAmount);\n }\n\n}\n" } diff --git a/abiniser/deployments/999/LoanManager_DEPLOYS.json b/abiniser/deployments/999/LoanManager_DEPLOYS.json index ec7a7ce9..083b0da1 100644 --- a/abiniser/deployments/999/LoanManager_DEPLOYS.json +++ b/abiniser/deployments/999/LoanManager_DEPLOYS.json @@ -66,15 +66,15 @@ "source": "/*\n Contract to manage Augmint token loan contracts backed by ETH\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/loanFlow.png\n\n TODO:\n - create MonetarySupervisor interface and use it instead?\n - make data arg generic bytes?\n - make collect() run as long as gas provided allows\n*/\npragma solidity ^0.4.23;\n\nimport \"./Rates.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\n\n\ncontract LoanManager is Restricted {\n using SafeMath for uint256;\n\n uint16 public constant CHUNK_SIZE = 100;\n\n enum LoanState { Open, Repaid, Defaulted, Collected } // NB: Defaulted state is not stored, only getters calculate\n\n struct LoanProduct {\n uint minDisbursedAmount; // 0: with decimals set in AugmintToken.decimals\n uint32 term; // 1\n uint32 discountRate; // 2: discountRate in parts per million , ie. 10,000 = 1%\n uint32 collateralRatio; // 3: loan token amount / colleteral pegged ccy value\n // in parts per million , ie. 10,000 = 1%\n uint32 defaultingFeePt; // 4: % of collateral in parts per million , ie. 50,000 = 5%\n bool isActive; // 5\n }\n\n /* NB: we don't need to store loan parameters because loan products can't be altered (only disabled/enabled) */\n struct LoanData {\n uint collateralAmount; // 0\n uint repaymentAmount; // 1\n address borrower; // 2\n uint32 productId; // 3\n LoanState state; // 4\n uint40 maturity; // 5\n }\n\n LoanProduct[] public products;\n\n LoanData[] public loans;\n mapping(address => uint[]) public accountLoans; // owner account address => array of loan Ids\n\n Rates public rates; // instance of ETH/pegged currency rate provider contract\n AugmintTokenInterface public augmintToken; // instance of token contract\n MonetarySupervisor public monetarySupervisor;\n\n event NewLoan(uint32 productId, uint loanId, address indexed borrower, uint collateralAmount, uint loanAmount,\n uint repaymentAmount, uint40 maturity);\n\n event LoanProductActiveStateChanged(uint32 productId, bool newState);\n\n event LoanProductAdded(uint32 productId);\n\n event LoanRepayed(uint loanId, address borrower);\n\n event LoanCollected(uint loanId, address indexed borrower, uint collectedCollateral,\n uint releasedCollateral, uint defaultingFee);\n\n event SystemContractsChanged(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor);\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor, Rates _rates)\n public {\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n rates = _rates;\n }\n\n function addLoanProduct(uint32 term, uint32 discountRate, uint32 collateralRatio, uint minDisbursedAmount,\n uint32 defaultingFeePt, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newProductId = products.push(\n LoanProduct(minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, isActive)\n ) - 1;\n\n uint32 newProductId = uint32(_newProductId);\n require(newProductId == _newProductId, \"productId overflow\");\n\n emit LoanProductAdded(newProductId);\n }\n\n function setLoanProductActiveState(uint32 productId, bool newState)\n external restrict (\"MonetaryBoard\") {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n products[productId].isActive = false;\n emit LoanProductActiveStateChanged(productId, newState);\n }\n\n function newEthBackedLoan(uint32 productId) external payable {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n LoanProduct storage product = products[productId];\n require(product.isActive, \"product must be in active state\"); // valid product\n\n\n // calculate loan values based on ETH sent in with Tx\n uint tokenValue = rates.convertFromWei(augmintToken.peggedSymbol(), msg.value);\n uint repaymentAmount = tokenValue.mul(product.collateralRatio).div(1000000);\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, repaymentAmount);\n\n require(loanAmount >= product.minDisbursedAmount, \"loanAmount must be >= minDisbursedAmount\");\n\n uint expiration = now.add(product.term);\n uint40 maturity = uint40(expiration);\n require(maturity == expiration, \"maturity overflow\");\n\n // Create new loan\n uint loanId = loans.push(LoanData(msg.value, repaymentAmount, msg.sender,\n productId, LoanState.Open, maturity)) - 1;\n\n // Store ref to new loan\n accountLoans[msg.sender].push(loanId);\n\n // Issue tokens and send to borrower\n monetarySupervisor.issueLoan(msg.sender, loanAmount);\n\n emit NewLoan(productId, loanId, msg.sender, msg.value, loanAmount, repaymentAmount, maturity);\n }\n\n /* repay loan, called from AugmintToken's transferAndNotify\n Flow for repaying loan:\n 1) user calls token contract's transferAndNotify loanId passed in data arg\n 2) transferAndNotify transfers tokens to the Lender contract\n 3) transferAndNotify calls Lender.transferNotification with lockProductId\n */\n // from arg is not used as we allow anyone to repay a loan:\n function transferNotification(address, uint repaymentAmount, uint loanId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n\n _repayLoan(loanId, repaymentAmount);\n }\n\n function collect(uint[] loanIds) external {\n /* when there are a lots of loans to be collected then\n the client need to call it in batches to make sure tx won't exceed block gas limit.\n Anyone can call it - can't cause harm as it only allows to collect loans which they are defaulted\n TODO: optimise defaulting fee calculations\n */\n uint totalLoanAmountCollected;\n uint totalCollateralToCollect;\n uint totalDefaultingFee;\n for (uint i = 0; i < loanIds.length; i++) {\n require(i < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanIds[i]];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(now >= loan.maturity, \"current time must be later than maturity\");\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, loan.repaymentAmount);\n\n totalLoanAmountCollected = totalLoanAmountCollected.add(loanAmount);\n\n loan.state = LoanState.Collected;\n\n // send ETH collateral to augmintToken reserve\n uint defaultingFeeInToken = loan.repaymentAmount.mul(product.defaultingFeePt).div(1000000);\n uint defaultingFee = rates.convertToWei(augmintToken.peggedSymbol(), defaultingFeeInToken);\n uint targetCollection = rates.convertToWei(augmintToken.peggedSymbol(),\n loan.repaymentAmount).add(defaultingFee);\n\n uint releasedCollateral;\n if (targetCollection < loan.collateralAmount) {\n releasedCollateral = loan.collateralAmount.sub(targetCollection);\n loan.borrower.transfer(releasedCollateral);\n }\n uint collateralToCollect = loan.collateralAmount.sub(releasedCollateral);\n if (defaultingFee >= collateralToCollect) {\n defaultingFee = collateralToCollect;\n collateralToCollect = 0;\n } else {\n collateralToCollect = collateralToCollect.sub(defaultingFee);\n }\n totalDefaultingFee = totalDefaultingFee.add(defaultingFee);\n\n totalCollateralToCollect = totalCollateralToCollect.add(collateralToCollect);\n\n emit LoanCollected(loanIds[i], loan.borrower, collateralToCollect.add(defaultingFee), releasedCollateral, defaultingFee);\n }\n\n if (totalCollateralToCollect > 0) {\n address(monetarySupervisor.augmintReserves()).transfer(totalCollateralToCollect);\n }\n\n if (totalDefaultingFee > 0){\n address(augmintToken.feeAccount()).transfer(totalDefaultingFee);\n }\n\n monetarySupervisor.loanCollectionNotification(totalLoanAmountCollected);// update KPIs\n\n }\n\n /* to allow upgrade of Rates and MonetarySupervisor contracts */\n function setSystemContracts(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n monetarySupervisor = newMonetarySupervisor;\n emit SystemContractsChanged(newRatesContract, newMonetarySupervisor);\n }\n\n function getProductCount() external view returns (uint ct) {\n return products.length;\n }\n\n // returns CHUNK_SIZE loan products starting from some offset:\n // [ productId, minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, maxLoanAmount, isActive ]\n function getProducts(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= products.length) { break; }\n\n LoanProduct storage product = products[offset + i];\n\n response[i] = [offset + i, product.minDisbursedAmount, product.term, product.discountRate,\n product.collateralRatio, product.defaultingFeePt,\n monetarySupervisor.getMaxLoanAmount(product.minDisbursedAmount), product.isActive ? 1 : 0 ];\n }\n }\n\n function getLoanCount() external view returns (uint ct) {\n return loans.length;\n }\n\n /* returns CHUNK_SIZE loans starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoans(uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loans.length) { break; }\n\n response[i] = getLoanTuple(offset + i);\n }\n }\n\n function getLoanCountForAddress(address borrower) external view returns (uint) {\n return accountLoans[borrower].length;\n }\n\n /* returns CHUNK_SIZE loans of a given account, starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoansForAddress(address borrower, uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n uint[] storage loansForAddress = accountLoans[borrower];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loansForAddress.length) { break; }\n\n response[i] = getLoanTuple(loansForAddress[offset + i]);\n }\n }\n\n function getLoanTuple(uint loanId) public view returns (uint[10] result) {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n uint disbursementTime = loan.maturity - product.term;\n\n LoanState loanState =\n loan.state == LoanState.Open && now >= loan.maturity ? LoanState.Defaulted : loan.state;\n\n result = [loanId, loan.collateralAmount, loan.repaymentAmount, uint(loan.borrower),\n loan.productId, uint(loanState), loan.maturity, disbursementTime, loanAmount, interestAmount];\n }\n\n function calculateLoanValues(LoanProduct storage product, uint repaymentAmount)\n internal view returns (uint loanAmount, uint interestAmount) {\n // calculate loan values based on repayment amount\n loanAmount = repaymentAmount.mul(product.discountRate).div(1000000);\n interestAmount = loanAmount > repaymentAmount ? 0 : repaymentAmount.sub(loanAmount);\n }\n\n /* internal function, assuming repayment amount already transfered */\n function _repayLoan(uint loanId, uint repaymentAmount) internal {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(repaymentAmount == loan.repaymentAmount, \"repaymentAmount must be equal to tokens sent\");\n require(now <= loan.maturity, \"current time must be earlier than maturity\");\n\n LoanProduct storage product = products[loan.productId];\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n\n loans[loanId].state = LoanState.Repaid;\n\n if (interestAmount > 0) {\n augmintToken.transfer(monetarySupervisor.interestEarnedAccount(), interestAmount);\n augmintToken.burn(loanAmount);\n } else {\n // negative or zero interest (i.e. discountRate >= 0)\n augmintToken.burn(repaymentAmount);\n }\n\n monetarySupervisor.loanRepaymentNotification(loanAmount); // update KPIs\n\n loan.borrower.transfer(loan.collateralAmount); // send back ETH collateral\n\n emit LoanRepayed(loanId, loan.borrower);\n }\n\n}\n" }, "0x8280d8de1424a92f69a4055e5666d2e1e6950a33": { - "generatedAt": "2018-05-09T12:19:24.981Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.220Z", - "deployTransactionHash": "0xd8b39ba1a6e17f23d0f5e9be5fc157fed229459f1a0cd817db55455c0823e9d5", + "generatedAt": "2018-05-12T00:22:15.222Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:12.428Z", + "deployTransactionHash": "0xbe49c6789885926bfcd5c5ac1322f41fbe2b380e202cde626ca337bc93a21203", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "e96ef4ce2aae9198d215ce6ca82b89c2", - "deployedBytecodeHash": "1635532ec88fcc2442b41e10ea8ae2d1", + "bytecodeHash": "feef104fe0b04cf9bde178c2f8ee4c04", + "deployedBytecodeHash": "d16030995b358f9fd29dafc6df7e6d35", "sourceHash": "9d5f96db98b6d336c18b8c6df5c7cd92", "source": "/*\n Contract to manage Augmint token loan contracts backed by ETH\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/loanFlow.png\n\n TODO:\n - create MonetarySupervisor interface and use it instead?\n - make data arg generic bytes?\n - make collect() run as long as gas provided allows\n*/\npragma solidity ^0.4.23;\n\nimport \"./Rates.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\n\n\ncontract LoanManager is Restricted {\n using SafeMath for uint256;\n\n uint16 public constant CHUNK_SIZE = 100;\n\n enum LoanState { Open, Repaid, Defaulted, Collected } // NB: Defaulted state is not stored, only getters calculate\n\n struct LoanProduct {\n uint minDisbursedAmount; // 0: with decimals set in AugmintToken.decimals\n uint32 term; // 1\n uint32 discountRate; // 2: discountRate in parts per million , ie. 10,000 = 1%\n uint32 collateralRatio; // 3: loan token amount / colleteral pegged ccy value\n // in parts per million , ie. 10,000 = 1%\n uint32 defaultingFeePt; // 4: % of collateral in parts per million , ie. 50,000 = 5%\n bool isActive; // 5\n }\n\n /* NB: we don't need to store loan parameters because loan products can't be altered (only disabled/enabled) */\n struct LoanData {\n uint collateralAmount; // 0\n uint repaymentAmount; // 1\n address borrower; // 2\n uint32 productId; // 3\n LoanState state; // 4\n uint40 maturity; // 5\n }\n\n LoanProduct[] public products;\n\n LoanData[] public loans;\n mapping(address => uint[]) public accountLoans; // owner account address => array of loan Ids\n\n Rates public rates; // instance of ETH/pegged currency rate provider contract\n AugmintTokenInterface public augmintToken; // instance of token contract\n MonetarySupervisor public monetarySupervisor;\n\n event NewLoan(uint32 productId, uint loanId, address indexed borrower, uint collateralAmount, uint loanAmount,\n uint repaymentAmount, uint40 maturity);\n\n event LoanProductActiveStateChanged(uint32 productId, bool newState);\n\n event LoanProductAdded(uint32 productId);\n\n event LoanRepayed(uint loanId, address borrower);\n\n event LoanCollected(uint loanId, address indexed borrower, uint collectedCollateral,\n uint releasedCollateral, uint defaultingFee);\n\n event SystemContractsChanged(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor);\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor, Rates _rates)\n public {\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n rates = _rates;\n }\n\n function addLoanProduct(uint32 term, uint32 discountRate, uint32 collateralRatio, uint minDisbursedAmount,\n uint32 defaultingFeePt, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newProductId = products.push(\n LoanProduct(minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, isActive)\n ) - 1;\n\n uint32 newProductId = uint32(_newProductId);\n require(newProductId == _newProductId, \"productId overflow\");\n\n emit LoanProductAdded(newProductId);\n }\n\n function setLoanProductActiveState(uint32 productId, bool newState)\n external restrict (\"MonetaryBoard\") {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n products[productId].isActive = false;\n emit LoanProductActiveStateChanged(productId, newState);\n }\n\n function newEthBackedLoan(uint32 productId) external payable {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n LoanProduct storage product = products[productId];\n require(product.isActive, \"product must be in active state\"); // valid product\n\n\n // calculate loan values based on ETH sent in with Tx\n uint tokenValue = rates.convertFromWei(augmintToken.peggedSymbol(), msg.value);\n uint repaymentAmount = tokenValue.mul(product.collateralRatio).div(1000000);\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, repaymentAmount);\n\n require(loanAmount >= product.minDisbursedAmount, \"loanAmount must be >= minDisbursedAmount\");\n\n uint expiration = now.add(product.term);\n uint40 maturity = uint40(expiration);\n require(maturity == expiration, \"maturity overflow\");\n\n // Create new loan\n uint loanId = loans.push(LoanData(msg.value, repaymentAmount, msg.sender,\n productId, LoanState.Open, maturity)) - 1;\n\n // Store ref to new loan\n accountLoans[msg.sender].push(loanId);\n\n // Issue tokens and send to borrower\n monetarySupervisor.issueLoan(msg.sender, loanAmount);\n\n emit NewLoan(productId, loanId, msg.sender, msg.value, loanAmount, repaymentAmount, maturity);\n }\n\n /* repay loan, called from AugmintToken's transferAndNotify\n Flow for repaying loan:\n 1) user calls token contract's transferAndNotify loanId passed in data arg\n 2) transferAndNotify transfers tokens to the Lender contract\n 3) transferAndNotify calls Lender.transferNotification with lockProductId\n */\n // from arg is not used as we allow anyone to repay a loan:\n function transferNotification(address, uint repaymentAmount, uint loanId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n\n _repayLoan(loanId, repaymentAmount);\n }\n\n function collect(uint[] loanIds) external {\n /* when there are a lots of loans to be collected then\n the client need to call it in batches to make sure tx won't exceed block gas limit.\n Anyone can call it - can't cause harm as it only allows to collect loans which they are defaulted\n TODO: optimise defaulting fee calculations\n */\n uint totalLoanAmountCollected;\n uint totalCollateralToCollect;\n uint totalDefaultingFee;\n for (uint i = 0; i < loanIds.length; i++) {\n require(i < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanIds[i]];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(now >= loan.maturity, \"current time must be later than maturity\");\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, loan.repaymentAmount);\n\n totalLoanAmountCollected = totalLoanAmountCollected.add(loanAmount);\n\n loan.state = LoanState.Collected;\n\n // send ETH collateral to augmintToken reserve\n uint defaultingFeeInToken = loan.repaymentAmount.mul(product.defaultingFeePt).div(1000000);\n uint defaultingFee = rates.convertToWei(augmintToken.peggedSymbol(), defaultingFeeInToken);\n uint targetCollection = rates.convertToWei(augmintToken.peggedSymbol(),\n loan.repaymentAmount).add(defaultingFee);\n\n uint releasedCollateral;\n if (targetCollection < loan.collateralAmount) {\n releasedCollateral = loan.collateralAmount.sub(targetCollection);\n loan.borrower.transfer(releasedCollateral);\n }\n uint collateralToCollect = loan.collateralAmount.sub(releasedCollateral);\n if (defaultingFee >= collateralToCollect) {\n defaultingFee = collateralToCollect;\n collateralToCollect = 0;\n } else {\n collateralToCollect = collateralToCollect.sub(defaultingFee);\n }\n totalDefaultingFee = totalDefaultingFee.add(defaultingFee);\n\n totalCollateralToCollect = totalCollateralToCollect.add(collateralToCollect);\n\n emit LoanCollected(loanIds[i], loan.borrower, collateralToCollect.add(defaultingFee), releasedCollateral, defaultingFee);\n }\n\n if (totalCollateralToCollect > 0) {\n address(monetarySupervisor.augmintReserves()).transfer(totalCollateralToCollect);\n }\n\n if (totalDefaultingFee > 0){\n address(augmintToken.feeAccount()).transfer(totalDefaultingFee);\n }\n\n monetarySupervisor.loanCollectionNotification(totalLoanAmountCollected);// update KPIs\n\n }\n\n /* to allow upgrade of Rates and MonetarySupervisor contracts */\n function setSystemContracts(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n monetarySupervisor = newMonetarySupervisor;\n emit SystemContractsChanged(newRatesContract, newMonetarySupervisor);\n }\n\n function getProductCount() external view returns (uint ct) {\n return products.length;\n }\n\n // returns CHUNK_SIZE loan products starting from some offset:\n // [ productId, minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, maxLoanAmount, isActive ]\n function getProducts(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= products.length) { break; }\n\n LoanProduct storage product = products[offset + i];\n\n response[i] = [offset + i, product.minDisbursedAmount, product.term, product.discountRate,\n product.collateralRatio, product.defaultingFeePt,\n monetarySupervisor.getMaxLoanAmount(product.minDisbursedAmount), product.isActive ? 1 : 0 ];\n }\n }\n\n function getLoanCount() external view returns (uint ct) {\n return loans.length;\n }\n\n /* returns CHUNK_SIZE loans starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoans(uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loans.length) { break; }\n\n response[i] = getLoanTuple(offset + i);\n }\n }\n\n function getLoanCountForAddress(address borrower) external view returns (uint) {\n return accountLoans[borrower].length;\n }\n\n /* returns CHUNK_SIZE loans of a given account, starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoansForAddress(address borrower, uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n uint[] storage loansForAddress = accountLoans[borrower];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loansForAddress.length) { break; }\n\n response[i] = getLoanTuple(loansForAddress[offset + i]);\n }\n }\n\n function getLoanTuple(uint loanId) public view returns (uint[10] result) {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n uint disbursementTime = loan.maturity - product.term;\n\n LoanState loanState =\n loan.state == LoanState.Open && now >= loan.maturity ? LoanState.Defaulted : loan.state;\n\n result = [loanId, loan.collateralAmount, loan.repaymentAmount, uint(loan.borrower),\n loan.productId, uint(loanState), loan.maturity, disbursementTime, loanAmount, interestAmount];\n }\n\n function calculateLoanValues(LoanProduct storage product, uint repaymentAmount)\n internal view returns (uint loanAmount, uint interestAmount) {\n // calculate loan values based on repayment amount\n loanAmount = repaymentAmount.mul(product.discountRate).div(1000000);\n interestAmount = loanAmount > repaymentAmount ? 0 : repaymentAmount.sub(loanAmount);\n }\n\n /* internal function, assuming repayment amount already transfered */\n function _repayLoan(uint loanId, uint repaymentAmount) internal {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(repaymentAmount == loan.repaymentAmount, \"repaymentAmount must be equal to tokens sent\");\n require(now <= loan.maturity, \"current time must be earlier than maturity\");\n\n LoanProduct storage product = products[loan.productId];\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n\n loans[loanId].state = LoanState.Repaid;\n\n if (interestAmount > 0) {\n augmintToken.transfer(monetarySupervisor.interestEarnedAccount(), interestAmount);\n augmintToken.burn(loanAmount);\n } else {\n // negative or zero interest (i.e. discountRate >= 0)\n augmintToken.burn(repaymentAmount);\n }\n\n monetarySupervisor.loanRepaymentNotification(loanAmount); // update KPIs\n\n loan.borrower.transfer(loan.collateralAmount); // send back ETH collateral\n\n emit LoanRepayed(loanId, loan.borrower);\n }\n\n}\n" } diff --git a/abiniser/deployments/999/Locker_DEPLOYS.json b/abiniser/deployments/999/Locker_DEPLOYS.json index 41ed7391..4e19a9d9 100644 --- a/abiniser/deployments/999/Locker_DEPLOYS.json +++ b/abiniser/deployments/999/Locker_DEPLOYS.json @@ -66,15 +66,15 @@ "source": "/* contract for tracking locked funds\n\n requirements\n -> lock funds\n -> unlock funds\n -> index locks by address\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/lockFlow.png\n\n TODO / think about:\n -> self-destruct function?\n\n*/\n\npragma solidity ^0.4.23;\n\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\n\n\ncontract Locker is Restricted, TokenReceiver {\n\n using SafeMath for uint256;\n\n uint public constant CHUNK_SIZE = 100;\n\n event NewLockProduct(uint32 indexed lockProductId, uint32 perTermInterest, uint32 durationInSecs,\n uint32 minimumLockAmount, bool isActive);\n\n event LockProductActiveChange(uint32 indexed lockProductId, bool newActiveState);\n\n // NB: amountLocked includes the original amount, plus interest\n event NewLock(address indexed lockOwner, uint lockId, uint amountLocked, uint interestEarned,\n uint40 lockedUntil, uint32 perTermInterest, uint32 durationInSecs);\n\n event LockReleased(address indexed lockOwner, uint lockId);\n\n event MonetarySupervisorChanged(MonetarySupervisor newMonetarySupervisor);\n\n struct LockProduct {\n // perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n uint32 perTermInterest;\n uint32 durationInSecs;\n uint32 minimumLockAmount;\n bool isActive;\n }\n\n /* NB: we don't need to store lock parameters because lockProducts can't be altered (only disabled/enabled) */\n struct Lock {\n uint amountLocked;\n address owner;\n uint32 productId;\n uint40 lockedUntil;\n bool isActive;\n }\n\n AugmintTokenInterface public augmintToken;\n MonetarySupervisor public monetarySupervisor;\n\n LockProduct[] public lockProducts;\n\n Lock[] public locks;\n\n // lock ids for an account\n mapping(address => uint[]) public accountLocks;\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor) public {\n\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n\n }\n\n function addLockProduct(uint32 perTermInterest, uint32 durationInSecs, uint32 minimumLockAmount, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newLockProductId = lockProducts.push(\n LockProduct(perTermInterest, durationInSecs, minimumLockAmount, isActive)) - 1;\n uint32 newLockProductId = uint32(_newLockProductId);\n require(newLockProductId == _newLockProductId, \"lockProduct overflow\");\n emit NewLockProduct(newLockProductId, perTermInterest, durationInSecs, minimumLockAmount, isActive);\n\n }\n\n function setLockProductActiveState(uint32 lockProductId, bool isActive) external restrict(\"MonetaryBoard\") {\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n\n lockProducts[lockProductId].isActive = isActive;\n emit LockProductActiveChange(lockProductId, isActive);\n\n }\n\n /* lock funds, called from AugmintToken's transferAndNotify\n Flow for locking tokens:\n 1) user calls token contract's transferAndNotify lockProductId passed in data arg\n 2) transferAndNotify transfers tokens to the Lock contract\n 3) transferAndNotify calls Lock.transferNotification with lockProductId\n */\n function transferNotification(address from, uint256 amountToLock, uint _lockProductId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n uint32 lockProductId = uint32(_lockProductId);\n require(lockProductId == _lockProductId, \"lockProductId overflow\");\n /* TODO: make data arg generic bytes\n uint productId;\n assembly { // solhint-disable-line no-inline-assembly\n productId := mload(data)\n } */\n _createLock(lockProductId, from, amountToLock);\n }\n\n function releaseFunds(uint lockId) external {\n // next line would revert but require to emit reason:\n require(lockId < locks.length, \"invalid lockId\");\n Lock storage lock = locks[lockId];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n require(lock.isActive, \"lock must be in active state\");\n require(now >= lock.lockedUntil, \"current time must be later than lockedUntil\");\n\n lock.isActive = false;\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n monetarySupervisor.releaseFundsNotification(lock.amountLocked); // to maintain totalLockAmount\n augmintToken.transferWithNarrative(lock.owner, lock.amountLocked.add(interestEarned),\n \"Funds released from lock\");\n\n emit LockReleased(lock.owner, lockId);\n }\n\n function setMonetarySupervisor(MonetarySupervisor newMonetarySupervisor) external restrict(\"MonetaryBoard\") {\n monetarySupervisor = newMonetarySupervisor;\n emit MonetarySupervisorChanged(newMonetarySupervisor);\n }\n\n function getLockProductCount() external view returns (uint) {\n\n return lockProducts.length;\n\n }\n\n // returns 20 lock products starting from some offset\n // lock products are encoded as [ perTermInterest, durationInSecs, minimumLockAmount, maxLockAmount, isActive ]\n function getLockProducts(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= lockProducts.length) { break; }\n\n LockProduct storage lockProduct = lockProducts[offset + i];\n\n response[i] = [ lockProduct.perTermInterest, lockProduct.durationInSecs, lockProduct.minimumLockAmount,\n monetarySupervisor.getMaxLockAmount(lockProduct.minimumLockAmount, lockProduct.perTermInterest),\n lockProduct.isActive ? 1 : 0 ];\n }\n }\n\n function getLockCount() external view returns (uint) {\n return locks.length;\n }\n\n function getLockCountForAddress(address lockOwner) external view returns (uint) {\n return accountLocks[lockOwner].length;\n }\n\n // returns CHUNK_SIZE locks starting from some offset\n // lock products are encoded as\n // [lockId, owner, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n // NB: perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n function getLocks(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locks.length) { break; }\n\n Lock storage lock = locks[offset + i];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [uint(offset + i), uint(lock.owner), lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0];\n }\n }\n\n // returns CHUNK_SIZE locks of a given account, starting from some offset\n // lock products are encoded as\n // [lockId, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n function getLocksForAddress(address lockOwner, uint offset) external view returns (uint[7][CHUNK_SIZE] response) {\n\n uint[] storage locksForAddress = accountLocks[lockOwner];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locksForAddress.length) { break; }\n\n Lock storage lock = locks[locksForAddress[offset + i]];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [ locksForAddress[offset + i], lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0 ];\n }\n }\n\n function calculateInterest(uint32 perTermInterest, uint amountToLock) public pure returns (uint interestEarned) {\n interestEarned = amountToLock.mul(perTermInterest).div(1000000);\n }\n\n // Internal function. assumes amountToLock is already transferred to this Lock contract\n function _createLock(uint32 lockProductId, address lockOwner, uint amountToLock) internal returns(uint lockId) {\n LockProduct storage lockProduct = lockProducts[lockProductId];\n require(lockProduct.isActive, \"lockProduct must be in active state\");\n require(amountToLock >= lockProduct.minimumLockAmount, \"amountToLock must be >= minimumLockAmount\");\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, amountToLock);\n uint expiration = now.add(lockProduct.durationInSecs);\n uint40 lockedUntil = uint40(expiration);\n require(lockedUntil == expiration, \"lockedUntil overflow\");\n\n lockId = locks.push(Lock(amountToLock, lockOwner, lockProductId, lockedUntil, true)) - 1;\n accountLocks[lockOwner].push(lockId);\n\n monetarySupervisor.requestInterest(amountToLock, interestEarned); // update KPIs & transfer interest here\n\n emit NewLock(lockOwner, lockId, amountToLock, interestEarned, lockedUntil, lockProduct.perTermInterest,\n lockProduct.durationInSecs);\n }\n\n}\n" }, "0x3444841a0e3417fd486aebc096ef3e67b75d7939": { - "generatedAt": "2018-05-09T12:19:25.074Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.193Z", - "deployTransactionHash": "0xf2d8f45148bd8fb86cd3105d204f2583fe4e92655b5528d184e3d2f64eab058c", + "generatedAt": "2018-05-12T00:22:15.251Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:12.442Z", + "deployTransactionHash": "0x7d1abed1db9494281c59fa6b7242aed68c8fe109ba641c6714bdb6bd70d30627", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "9b5e7b3da1f0e565bcb907a0d79c0e98", - "deployedBytecodeHash": "4e1e5f0bc52471787c3b57559a69a7e9", + "bytecodeHash": "c6c9a4350153bfd4b81eaf228df169f4", + "deployedBytecodeHash": "878366e51dfa79329645837646563469", "sourceHash": "7ffe14f90465530802dc1f5762e0217f", "source": "/* contract for tracking locked funds\n\n requirements\n -> lock funds\n -> unlock funds\n -> index locks by address\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/lockFlow.png\n\n TODO / think about:\n -> self-destruct function?\n\n*/\n\npragma solidity ^0.4.23;\n\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\n\n\ncontract Locker is Restricted, TokenReceiver {\n\n using SafeMath for uint256;\n\n uint public constant CHUNK_SIZE = 100;\n\n event NewLockProduct(uint32 indexed lockProductId, uint32 perTermInterest, uint32 durationInSecs,\n uint32 minimumLockAmount, bool isActive);\n\n event LockProductActiveChange(uint32 indexed lockProductId, bool newActiveState);\n\n // NB: amountLocked includes the original amount, plus interest\n event NewLock(address indexed lockOwner, uint lockId, uint amountLocked, uint interestEarned,\n uint40 lockedUntil, uint32 perTermInterest, uint32 durationInSecs);\n\n event LockReleased(address indexed lockOwner, uint lockId);\n\n event MonetarySupervisorChanged(MonetarySupervisor newMonetarySupervisor);\n\n struct LockProduct {\n // perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n uint32 perTermInterest;\n uint32 durationInSecs;\n uint32 minimumLockAmount;\n bool isActive;\n }\n\n /* NB: we don't need to store lock parameters because lockProducts can't be altered (only disabled/enabled) */\n struct Lock {\n uint amountLocked;\n address owner;\n uint32 productId;\n uint40 lockedUntil;\n bool isActive;\n }\n\n AugmintTokenInterface public augmintToken;\n MonetarySupervisor public monetarySupervisor;\n\n LockProduct[] public lockProducts;\n\n Lock[] public locks;\n\n // lock ids for an account\n mapping(address => uint[]) public accountLocks;\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor) public {\n\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n\n }\n\n function addLockProduct(uint32 perTermInterest, uint32 durationInSecs, uint32 minimumLockAmount, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newLockProductId = lockProducts.push(\n LockProduct(perTermInterest, durationInSecs, minimumLockAmount, isActive)) - 1;\n uint32 newLockProductId = uint32(_newLockProductId);\n require(newLockProductId == _newLockProductId, \"lockProduct overflow\");\n emit NewLockProduct(newLockProductId, perTermInterest, durationInSecs, minimumLockAmount, isActive);\n\n }\n\n function setLockProductActiveState(uint32 lockProductId, bool isActive) external restrict(\"MonetaryBoard\") {\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n\n lockProducts[lockProductId].isActive = isActive;\n emit LockProductActiveChange(lockProductId, isActive);\n\n }\n\n /* lock funds, called from AugmintToken's transferAndNotify\n Flow for locking tokens:\n 1) user calls token contract's transferAndNotify lockProductId passed in data arg\n 2) transferAndNotify transfers tokens to the Lock contract\n 3) transferAndNotify calls Lock.transferNotification with lockProductId\n */\n function transferNotification(address from, uint256 amountToLock, uint _lockProductId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n uint32 lockProductId = uint32(_lockProductId);\n require(lockProductId == _lockProductId, \"lockProductId overflow\");\n /* TODO: make data arg generic bytes\n uint productId;\n assembly { // solhint-disable-line no-inline-assembly\n productId := mload(data)\n } */\n _createLock(lockProductId, from, amountToLock);\n }\n\n function releaseFunds(uint lockId) external {\n // next line would revert but require to emit reason:\n require(lockId < locks.length, \"invalid lockId\");\n Lock storage lock = locks[lockId];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n require(lock.isActive, \"lock must be in active state\");\n require(now >= lock.lockedUntil, \"current time must be later than lockedUntil\");\n\n lock.isActive = false;\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n monetarySupervisor.releaseFundsNotification(lock.amountLocked); // to maintain totalLockAmount\n augmintToken.transferWithNarrative(lock.owner, lock.amountLocked.add(interestEarned),\n \"Funds released from lock\");\n\n emit LockReleased(lock.owner, lockId);\n }\n\n function setMonetarySupervisor(MonetarySupervisor newMonetarySupervisor) external restrict(\"MonetaryBoard\") {\n monetarySupervisor = newMonetarySupervisor;\n emit MonetarySupervisorChanged(newMonetarySupervisor);\n }\n\n function getLockProductCount() external view returns (uint) {\n\n return lockProducts.length;\n\n }\n\n // returns 20 lock products starting from some offset\n // lock products are encoded as [ perTermInterest, durationInSecs, minimumLockAmount, maxLockAmount, isActive ]\n function getLockProducts(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= lockProducts.length) { break; }\n\n LockProduct storage lockProduct = lockProducts[offset + i];\n\n response[i] = [ lockProduct.perTermInterest, lockProduct.durationInSecs, lockProduct.minimumLockAmount,\n monetarySupervisor.getMaxLockAmount(lockProduct.minimumLockAmount, lockProduct.perTermInterest),\n lockProduct.isActive ? 1 : 0 ];\n }\n }\n\n function getLockCount() external view returns (uint) {\n return locks.length;\n }\n\n function getLockCountForAddress(address lockOwner) external view returns (uint) {\n return accountLocks[lockOwner].length;\n }\n\n // returns CHUNK_SIZE locks starting from some offset\n // lock products are encoded as\n // [lockId, owner, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n // NB: perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n function getLocks(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locks.length) { break; }\n\n Lock storage lock = locks[offset + i];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [uint(offset + i), uint(lock.owner), lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0];\n }\n }\n\n // returns CHUNK_SIZE locks of a given account, starting from some offset\n // lock products are encoded as\n // [lockId, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n function getLocksForAddress(address lockOwner, uint offset) external view returns (uint[7][CHUNK_SIZE] response) {\n\n uint[] storage locksForAddress = accountLocks[lockOwner];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locksForAddress.length) { break; }\n\n Lock storage lock = locks[locksForAddress[offset + i]];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [ locksForAddress[offset + i], lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0 ];\n }\n }\n\n function calculateInterest(uint32 perTermInterest, uint amountToLock) public pure returns (uint interestEarned) {\n interestEarned = amountToLock.mul(perTermInterest).div(1000000);\n }\n\n // Internal function. assumes amountToLock is already transferred to this Lock contract\n function _createLock(uint32 lockProductId, address lockOwner, uint amountToLock) internal returns(uint lockId) {\n LockProduct storage lockProduct = lockProducts[lockProductId];\n require(lockProduct.isActive, \"lockProduct must be in active state\");\n require(amountToLock >= lockProduct.minimumLockAmount, \"amountToLock must be >= minimumLockAmount\");\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, amountToLock);\n uint expiration = now.add(lockProduct.durationInSecs);\n uint40 lockedUntil = uint40(expiration);\n require(lockedUntil == expiration, \"lockedUntil overflow\");\n\n lockId = locks.push(Lock(amountToLock, lockOwner, lockProductId, lockedUntil, true)) - 1;\n accountLocks[lockOwner].push(lockId);\n\n monetarySupervisor.requestInterest(amountToLock, interestEarned); // update KPIs & transfer interest here\n\n emit NewLock(lockOwner, lockId, amountToLock, interestEarned, lockedUntil, lockProduct.perTermInterest,\n lockProduct.durationInSecs);\n }\n\n}\n" } diff --git a/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json b/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json index c1594721..1380e8a6 100644 --- a/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json +++ b/abiniser/deployments/999/MonetarySupervisor_DEPLOYS.json @@ -61,15 +61,15 @@ "source": "/* MonetarySupervisor\n - maintains system wide KPIs (eg totalLockAmount, totalLoanAmount)\n - holds system wide parameters/limits\n - enforces system wide limits\n - burns and issues to AugmintReserves\n - Send funds from reserve to exchange when intervening (not implemented yet)\n - Converts older versions of AugmintTokens in 1:1 to new\n\n TODO:\n - Mcreate and use MonetarySupervisorInterface?\n - create and use InterestEarnedAccount interface ?\n\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\nimport \"./InterestEarnedAccount.sol\";\nimport \"./AugmintReserves.sol\";\n\n\ncontract MonetarySupervisor is Restricted, TokenReceiver { // solhint-disable-line no-empty-blocks\n using SafeMath for uint256;\n\n uint public constant PERCENT_100 = 1000000;\n\n AugmintTokenInterface public augmintToken;\n InterestEarnedAccount public interestEarnedAccount;\n AugmintReserves public augmintReserves;\n\n uint public issuedByMonetaryBoard; // supply issued manually by monetary board\n\n uint public totalLoanAmount; // total amount of all loans without interest, in token\n uint public totalLockedAmount; // total amount of all locks without premium, in token\n\n /**********\n Parameters to ensure totalLoanAmount or totalLockedAmount difference is within limits and system also works\n when total loan or lock amounts are low.\n for test calculations: https://docs.google.com/spreadsheets/d/1MeWYPYZRIm1n9lzpvbq8kLfQg1hhvk5oJY6NrR401S0\n **********/\n struct LtdParams {\n uint lockDifferenceLimit; /* only allow a new lock if Loan To Deposit ratio would stay above\n (1 - lockDifferenceLimit) with new lock. Stored as parts per million */\n uint loanDifferenceLimit; /* only allow a new loan if Loan To Deposit ratio would stay above\n (1 + loanDifferenceLimit) with new loan. Stored as parts per million */\n /* allowedDifferenceAmount param is to ensure the system is not \"freezing\" when totalLoanAmount or\n totalLockAmount is low.\n It allows a new loan or lock (up to an amount to reach this difference) even if LTD will go below / above\n lockDifferenceLimit / loanDifferenceLimit with the new lock/loan */\n uint allowedDifferenceAmount;\n }\n\n LtdParams public ltdParams;\n\n /* Previously deployed AugmintTokens which are accepted for conversion (see transferNotification() )\n NB: it's not iterable so old version addresses needs to be added for UI manually after each deploy */\n mapping(address => bool) public acceptedLegacyAugmintTokens;\n\n event LtdParamsChanged(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount);\n\n event AcceptedLegacyAugmintTokenChanged(address augmintTokenAddress, bool newAcceptedState);\n\n event LegacyTokenConverted(address oldTokenAddress, address account, uint amount);\n\n event KPIsAdjusted(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment);\n\n event SystemContractsChanged(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves);\n\n constructor(AugmintTokenInterface _augmintToken, AugmintReserves _augmintReserves,\n InterestEarnedAccount _interestEarnedAccount,\n uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount) public {\n augmintToken = _augmintToken;\n augmintReserves = _augmintReserves;\n interestEarnedAccount = _interestEarnedAccount;\n\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n function issueToReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.add(amount);\n augmintToken.issueTo(augmintReserves, amount);\n }\n\n function burnFromReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.sub(amount);\n augmintReserves.burn(augmintToken, amount);\n }\n\n /* Locker requesting interest when locking funds. Enforcing LTD to stay within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function requestInterest(uint amountToLock, uint interestAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n require(amountToLock <= getMaxLockAmountAllowedByLtd(), \"amountToLock must be <= maxLockAmountAllowedByLtd\");\n\n totalLockedAmount = totalLockedAmount.add(amountToLock);\n // next line would revert but require to emit reason:\n require(augmintToken.balanceOf(address(interestEarnedAccount)) >= interestAmount,\n \"interestEarnedAccount balance must be >= interestAmount\");\n interestEarnedAccount.transferInterest(augmintToken, msg.sender, interestAmount); // transfer interest to Locker\n }\n\n // Locker notifying when releasing funds to update KPIs\n function releaseFundsNotification(uint lockedAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n totalLockedAmount = totalLockedAmount.sub(lockedAmount);\n }\n\n /* Issue loan if LTD stays within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function issueLoan(address borrower, uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n require(loanAmount <= getMaxLoanAmountAllowedByLtd(), \"loanAmount must be <= maxLoanAmountAllowedByLtd\");\n totalLoanAmount = totalLoanAmount.add(loanAmount);\n augmintToken.issueTo(borrower, loanAmount);\n }\n\n function loanRepaymentNotification(uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(loanAmount);\n }\n\n // NB: this is called by Lender contract with the sum of all loans collected in batch\n function loanCollectionNotification(uint totalLoanAmountCollected) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(totalLoanAmountCollected);\n }\n\n function setAcceptedLegacyAugmintToken(address legacyAugmintTokenAddress, bool newAcceptedState)\n external restrict(\"MonetaryBoard\") {\n acceptedLegacyAugmintTokens[legacyAugmintTokenAddress] = newAcceptedState;\n emit AcceptedLegacyAugmintTokenChanged(legacyAugmintTokenAddress, newAcceptedState);\n }\n\n function setLtdParams(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount)\n external restrict(\"MonetaryBoard\") {\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n\n emit LtdParamsChanged(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n /* function to migrate old totalLoanAmount and totalLockedAmount from old monetarySupervisor contract\n when it's upgraded.\n Set new monetarySupervisor contract in all locker and loanManager contracts before executing this */\n function adjustKPIs(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment)\n external restrict(\"MonetaryBoard\") {\n totalLoanAmount = totalLoanAmount.add(totalLoanAmountAdjustment);\n totalLockedAmount = totalLockedAmount.add(totalLockedAmountAdjustment);\n\n emit KPIsAdjusted(totalLoanAmountAdjustment, totalLockedAmountAdjustment);\n }\n\n /* to allow upgrades of InterestEarnedAccount and AugmintReserves contracts. */\n function setSystemContracts(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves)\n external restrict(\"MonetaryBoard\") {\n interestEarnedAccount = newInterestEarnedAccount;\n augmintReserves = newAugmintReserves;\n emit SystemContractsChanged(newInterestEarnedAccount, newAugmintReserves);\n }\n\n /* User can request to convert their tokens from older AugmintToken versions in 1:1\n transferNotification is called from AugmintToken's transferAndNotify\n Flow for converting old tokens:\n 1) user calls old token contract's transferAndNotify with the amount to convert,\n addressing the new MonetarySupervisor Contract\n 2) transferAndNotify transfers user's old tokens to the current MonetarySupervisor contract's address\n 3) transferAndNotify calls MonetarySupervisor.transferNotification\n 4) MonetarySupervisor checks if old AugmintToken is permitted\n 5) MonetarySupervisor issues new tokens to user's account in current AugmintToken\n 6) MonetarySupervisor burns old tokens from own balance\n */\n function transferNotification(address from, uint amount, uint /* data, not used */ ) external {\n AugmintTokenInterface legacyToken = AugmintTokenInterface(msg.sender);\n require(acceptedLegacyAugmintTokens[legacyToken], \"msg.sender must be allowed in acceptedLegacyAugmintTokens\");\n\n legacyToken.burn(amount);\n augmintToken.issueTo(from, amount);\n emit LegacyTokenConverted(msg.sender, from, amount);\n }\n\n function getLoanToDepositRatio() external view returns (uint loanToDepositRatio) {\n loanToDepositRatio = totalLockedAmount == 0 ? 0 : totalLockedAmount.mul(PERCENT_100).div(totalLoanAmount);\n }\n\n /* Helper function for UI.\n Returns max lock amount based on minLockAmount, interestPt, using LTD params & interestEarnedAccount balance */\n function getMaxLockAmount(uint minLockAmount, uint interestPt) external view returns (uint maxLock) {\n uint allowedByEarning = augmintToken.balanceOf(address(interestEarnedAccount)).mul(PERCENT_100).div(interestPt);\n uint allowedByLtd = getMaxLockAmountAllowedByLtd();\n maxLock = allowedByEarning < allowedByLtd ? allowedByEarning : allowedByLtd;\n maxLock = maxLock < minLockAmount ? 0 : maxLock;\n }\n\n /* Helper function for UI.\n Returns max loan amount based on minLoanAmont using LTD params */\n function getMaxLoanAmount(uint minLoanAmount) external view returns (uint maxLoan) {\n uint allowedByLtd = getMaxLoanAmountAllowedByLtd();\n maxLoan = allowedByLtd < minLoanAmount ? 0 : allowedByLtd;\n }\n\n /* returns maximum lockable token amount allowed by LTD params. */\n function getMaxLockAmountAllowedByLtd() public view returns(uint maxLockByLtd) {\n uint allowedByLtdDifferencePt = totalLoanAmount.mul(PERCENT_100).div(PERCENT_100\n .sub(ltdParams.lockDifferenceLimit));\n allowedByLtdDifferencePt = totalLockedAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLockedAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLockedAmount >= totalLoanAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLoanAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLockedAmount);\n\n maxLockByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n /* returns maximum borrowable token amount allowed by LTD params */\n function getMaxLoanAmountAllowedByLtd() public view returns(uint maxLoanByLtd) {\n uint allowedByLtdDifferencePt = totalLockedAmount.mul(ltdParams.loanDifferenceLimit.add(PERCENT_100))\n .div(PERCENT_100);\n allowedByLtdDifferencePt = totalLoanAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLoanAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLoanAmount >= totalLockedAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLockedAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLoanAmount);\n\n maxLoanByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n}\n" }, "0x80d62c62138ed74699008c6f041b589c51619145": { - "generatedAt": "2018-05-09T12:19:24.897Z", - "truffleContractFileUpdatedAt": "2018-05-09T12:19:17.178Z", - "deployTransactionHash": "0x6cad685d7b05755ae58e792936e933d0710c1a03d7a9b70d6724853b74c45b22", + "generatedAt": "2018-05-12T00:22:15.188Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:12.412Z", + "deployTransactionHash": "0x06d7f813c999ae03a38d8d9e8bea67ee85b1d8669d8fbba98f064b55e63d4ab8", "compiler": { "name": "solc", "version": "0.4.23+commit.124ca40d.Emscripten.clang" }, - "bytecodeHash": "5d834220507e83a131733731236263e3", - "deployedBytecodeHash": "b077fe6a352003e4f465a281bd3a503f", + "bytecodeHash": "83f4ac731042d988dad02af18401f3f3", + "deployedBytecodeHash": "6356e4110fb4cbff5b57908614a63219", "sourceHash": "cbd67b35e74bbbc9ef5a78c3180eb002", "source": "/* MonetarySupervisor\n - maintains system wide KPIs (eg totalLockAmount, totalLoanAmount)\n - holds system wide parameters/limits\n - enforces system wide limits\n - burns and issues to AugmintReserves\n - Send funds from reserve to exchange when intervening (not implemented yet)\n - Converts older versions of AugmintTokens in 1:1 to new\n\n TODO:\n - Mcreate and use MonetarySupervisorInterface?\n - create and use InterestEarnedAccount interface ?\n\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\nimport \"./InterestEarnedAccount.sol\";\nimport \"./AugmintReserves.sol\";\n\n\ncontract MonetarySupervisor is Restricted, TokenReceiver { // solhint-disable-line no-empty-blocks\n using SafeMath for uint256;\n\n uint public constant PERCENT_100 = 1000000;\n\n AugmintTokenInterface public augmintToken;\n InterestEarnedAccount public interestEarnedAccount;\n AugmintReserves public augmintReserves;\n\n uint public issuedByMonetaryBoard; // supply issued manually by monetary board\n\n uint public totalLoanAmount; // total amount of all loans without interest, in token\n uint public totalLockedAmount; // total amount of all locks without premium, in token\n\n /**********\n Parameters to ensure totalLoanAmount or totalLockedAmount difference is within limits and system also works\n when total loan or lock amounts are low.\n for test calculations: https://docs.google.com/spreadsheets/d/1MeWYPYZRIm1n9lzpvbq8kLfQg1hhvk5oJY6NrR401S0\n **********/\n struct LtdParams {\n uint lockDifferenceLimit; /* only allow a new lock if Loan To Deposit ratio would stay above\n (1 - lockDifferenceLimit) with new lock. Stored as parts per million */\n uint loanDifferenceLimit; /* only allow a new loan if Loan To Deposit ratio would stay above\n (1 + loanDifferenceLimit) with new loan. Stored as parts per million */\n /* allowedDifferenceAmount param is to ensure the system is not \"freezing\" when totalLoanAmount or\n totalLockAmount is low.\n It allows a new loan or lock (up to an amount to reach this difference) even if LTD will go below / above\n lockDifferenceLimit / loanDifferenceLimit with the new lock/loan */\n uint allowedDifferenceAmount;\n }\n\n LtdParams public ltdParams;\n\n /* Previously deployed AugmintTokens which are accepted for conversion (see transferNotification() )\n NB: it's not iterable so old version addresses needs to be added for UI manually after each deploy */\n mapping(address => bool) public acceptedLegacyAugmintTokens;\n\n event LtdParamsChanged(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount);\n\n event AcceptedLegacyAugmintTokenChanged(address augmintTokenAddress, bool newAcceptedState);\n\n event LegacyTokenConverted(address oldTokenAddress, address account, uint amount);\n\n event KPIsAdjusted(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment);\n\n event SystemContractsChanged(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves);\n\n constructor(AugmintTokenInterface _augmintToken, AugmintReserves _augmintReserves,\n InterestEarnedAccount _interestEarnedAccount,\n uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount) public {\n augmintToken = _augmintToken;\n augmintReserves = _augmintReserves;\n interestEarnedAccount = _interestEarnedAccount;\n\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n function issueToReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.add(amount);\n augmintToken.issueTo(augmintReserves, amount);\n }\n\n function burnFromReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.sub(amount);\n augmintReserves.burn(augmintToken, amount);\n }\n\n /* Locker requesting interest when locking funds. Enforcing LTD to stay within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function requestInterest(uint amountToLock, uint interestAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n require(amountToLock <= getMaxLockAmountAllowedByLtd(), \"amountToLock must be <= maxLockAmountAllowedByLtd\");\n\n totalLockedAmount = totalLockedAmount.add(amountToLock);\n // next line would revert but require to emit reason:\n require(augmintToken.balanceOf(address(interestEarnedAccount)) >= interestAmount,\n \"interestEarnedAccount balance must be >= interestAmount\");\n interestEarnedAccount.transferInterest(augmintToken, msg.sender, interestAmount); // transfer interest to Locker\n }\n\n // Locker notifying when releasing funds to update KPIs\n function releaseFundsNotification(uint lockedAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n totalLockedAmount = totalLockedAmount.sub(lockedAmount);\n }\n\n /* Issue loan if LTD stays within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function issueLoan(address borrower, uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n require(loanAmount <= getMaxLoanAmountAllowedByLtd(), \"loanAmount must be <= maxLoanAmountAllowedByLtd\");\n totalLoanAmount = totalLoanAmount.add(loanAmount);\n augmintToken.issueTo(borrower, loanAmount);\n }\n\n function loanRepaymentNotification(uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(loanAmount);\n }\n\n // NB: this is called by Lender contract with the sum of all loans collected in batch\n function loanCollectionNotification(uint totalLoanAmountCollected) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(totalLoanAmountCollected);\n }\n\n function setAcceptedLegacyAugmintToken(address legacyAugmintTokenAddress, bool newAcceptedState)\n external restrict(\"MonetaryBoard\") {\n acceptedLegacyAugmintTokens[legacyAugmintTokenAddress] = newAcceptedState;\n emit AcceptedLegacyAugmintTokenChanged(legacyAugmintTokenAddress, newAcceptedState);\n }\n\n function setLtdParams(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount)\n external restrict(\"MonetaryBoard\") {\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n\n emit LtdParamsChanged(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n /* function to migrate old totalLoanAmount and totalLockedAmount from old monetarySupervisor contract\n when it's upgraded.\n Set new monetarySupervisor contract in all locker and loanManager contracts before executing this */\n function adjustKPIs(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment)\n external restrict(\"MonetaryBoard\") {\n totalLoanAmount = totalLoanAmount.add(totalLoanAmountAdjustment);\n totalLockedAmount = totalLockedAmount.add(totalLockedAmountAdjustment);\n\n emit KPIsAdjusted(totalLoanAmountAdjustment, totalLockedAmountAdjustment);\n }\n\n /* to allow upgrades of InterestEarnedAccount and AugmintReserves contracts. */\n function setSystemContracts(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves)\n external restrict(\"MonetaryBoard\") {\n interestEarnedAccount = newInterestEarnedAccount;\n augmintReserves = newAugmintReserves;\n emit SystemContractsChanged(newInterestEarnedAccount, newAugmintReserves);\n }\n\n /* User can request to convert their tokens from older AugmintToken versions in 1:1\n transferNotification is called from AugmintToken's transferAndNotify\n Flow for converting old tokens:\n 1) user calls old token contract's transferAndNotify with the amount to convert,\n addressing the new MonetarySupervisor Contract\n 2) transferAndNotify transfers user's old tokens to the current MonetarySupervisor contract's address\n 3) transferAndNotify calls MonetarySupervisor.transferNotification\n 4) MonetarySupervisor checks if old AugmintToken is permitted\n 5) MonetarySupervisor issues new tokens to user's account in current AugmintToken\n 6) MonetarySupervisor burns old tokens from own balance\n */\n function transferNotification(address from, uint amount, uint /* data, not used */ ) external {\n AugmintTokenInterface legacyToken = AugmintTokenInterface(msg.sender);\n require(acceptedLegacyAugmintTokens[legacyToken], \"msg.sender must be allowed in acceptedLegacyAugmintTokens\");\n\n legacyToken.burn(amount);\n augmintToken.issueTo(from, amount);\n emit LegacyTokenConverted(msg.sender, from, amount);\n }\n\n function getLoanToDepositRatio() external view returns (uint loanToDepositRatio) {\n loanToDepositRatio = totalLockedAmount == 0 ? 0 : totalLockedAmount.mul(PERCENT_100).div(totalLoanAmount);\n }\n\n /* Helper function for UI.\n Returns max lock amount based on minLockAmount, interestPt, using LTD params & interestEarnedAccount balance */\n function getMaxLockAmount(uint minLockAmount, uint interestPt) external view returns (uint maxLock) {\n uint allowedByEarning = augmintToken.balanceOf(address(interestEarnedAccount)).mul(PERCENT_100).div(interestPt);\n uint allowedByLtd = getMaxLockAmountAllowedByLtd();\n maxLock = allowedByEarning < allowedByLtd ? allowedByEarning : allowedByLtd;\n maxLock = maxLock < minLockAmount ? 0 : maxLock;\n }\n\n /* Helper function for UI.\n Returns max loan amount based on minLoanAmont using LTD params */\n function getMaxLoanAmount(uint minLoanAmount) external view returns (uint maxLoan) {\n uint allowedByLtd = getMaxLoanAmountAllowedByLtd();\n maxLoan = allowedByLtd < minLoanAmount ? 0 : allowedByLtd;\n }\n\n /* returns maximum lockable token amount allowed by LTD params. */\n function getMaxLockAmountAllowedByLtd() public view returns(uint maxLockByLtd) {\n uint allowedByLtdDifferencePt = totalLoanAmount.mul(PERCENT_100).div(PERCENT_100\n .sub(ltdParams.lockDifferenceLimit));\n allowedByLtdDifferencePt = totalLockedAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLockedAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLockedAmount >= totalLoanAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLoanAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLockedAmount);\n\n maxLockByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n /* returns maximum borrowable token amount allowed by LTD params */\n function getMaxLoanAmountAllowedByLtd() public view returns(uint maxLoanByLtd) {\n uint allowedByLtdDifferencePt = totalLockedAmount.mul(ltdParams.loanDifferenceLimit.add(PERCENT_100))\n .div(PERCENT_100);\n allowedByLtdDifferencePt = totalLoanAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLoanAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLoanAmount >= totalLockedAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLockedAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLoanAmount);\n\n maxLoanByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n}\n" } diff --git a/abiniser/deployments/999/TokenAEur_DEPLOYS.json b/abiniser/deployments/999/TokenAEur_DEPLOYS.json index 74c9874b..64d61260 100644 --- a/abiniser/deployments/999/TokenAEur_DEPLOYS.json +++ b/abiniser/deployments/999/TokenAEur_DEPLOYS.json @@ -1,6 +1,6 @@ { "contractName": "TokenAEur", - "latestAbiHash": "6b1597192b36a746724929ec9ee7b6b8", + "latestAbiHash": "4b49e7e6d1a9a2de81a4d2d088acbc04", "deployedAbis": { "27721a2c77dc40da7639abd46791c3d7": { "latestDeployedAddress": "0x1b9441428f9e682bab4f9cc70fdf50adcc3411f4", @@ -71,6 +71,24 @@ "source": "/* Augmint Crypto Euro token (ACE) implementation */\npragma solidity ^0.4.23;\nimport \"./interfaces/TransferFeeInterface.sol\";\nimport \"./generic/AugmintToken.sol\";\n\n\ncontract TokenAEur is AugmintToken {\n constructor(TransferFeeInterface _feeAccount)\n public AugmintToken(\"Augmint Crypto Euro\", \"AEUR\", \"EUR\", 2, _feeAccount)\n {} // solhint-disable-line no-empty-blocks\n\n}\n" } } + }, + "4b49e7e6d1a9a2de81a4d2d088acbc04": { + "latestDeployedAddress": "0x1a93886326b0220731f25926d56074f7f0b074ce", + "deployments": { + "0x1a93886326b0220731f25926d56074f7f0b074ce": { + "generatedAt": "2018-05-12T00:22:15.164Z", + "truffleContractFileUpdatedAt": "2018-05-12T00:22:12.405Z", + "deployTransactionHash": "0xf13fed4e1c899f3287abfa6aefe1a5e3f63a61e3eb525b990297894d7be92607", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "042675fc70cfed8762b1e7de8d15799f", + "deployedBytecodeHash": "03b0c0d8c49d83af34516ac82bc0b2a9", + "sourceHash": "61dded7836f4431e621382340f32670c", + "source": "/* Augmint Crypto Euro token (ACE) implementation */\npragma solidity ^0.4.23;\nimport \"./interfaces/TransferFeeInterface.sol\";\nimport \"./generic/AugmintToken.sol\";\n\n\ncontract TokenAEur is AugmintToken {\n constructor(TransferFeeInterface _feeAccount)\n public AugmintToken(\"Augmint Crypto Euro\", \"AEUR\", \"EUR\", 2, _feeAccount)\n {} // solhint-disable-line no-empty-blocks\n\n}\n" + } + } } } } \ No newline at end of file From 3620d0701c5ccf8a2299f49a5f19d4d1be888b26 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 11 May 2018 21:37:01 -0400 Subject: [PATCH 25/26] deploy new TokenAEur and all dependent contracts to Rinkeby --- abiniser/deployments/4/Exchange_DEPLOYS.json | 15 +- .../deployments/4/LoanManager_DEPLOYS.json | 15 +- abiniser/deployments/4/Locker_DEPLOYS.json | 15 +- .../4/MonetarySupervisor_DEPLOYS.json | 15 +- abiniser/deployments/4/TokenAEur_DEPLOYS.json | 20 +- rinkeby_migrations/17_new_TokenAEur.js | 181 ++++++++++++++++++ 6 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 rinkeby_migrations/17_new_TokenAEur.js diff --git a/abiniser/deployments/4/Exchange_DEPLOYS.json b/abiniser/deployments/4/Exchange_DEPLOYS.json index e6f4666b..861e9189 100644 --- a/abiniser/deployments/4/Exchange_DEPLOYS.json +++ b/abiniser/deployments/4/Exchange_DEPLOYS.json @@ -50,7 +50,7 @@ } }, "3c157a5256a2093da587f166d4dbd537": { - "latestDeployedAddress": "0xa2ed50765110b695816c658d5d6d1d32bcd03866", + "latestDeployedAddress": "0xc5b604f8e046dff26642ca544c9eb3064e02ecd9", "deployments": { "0xa2ed50765110b695816c658d5d6d1d32bcd03866": { "generatedAt": "2018-05-03T22:05:03.992Z", @@ -64,6 +64,19 @@ "deployedBytecodeHash": "2c390a9b03f57a87af44432fc6444647", "sourceHash": "6e27bdbdc64d0d9cf52de1abc46c4cf7", "source": "/* Augmint's Internal Exchange\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/exchangeFlow.png\n\n TODO:\n - change to wihtdrawal pattern, see: https://github.com/Augmint/augmint-contracts/issues/17\n - deduct fee\n - consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)\n - use Rates interface?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./Rates.sol\";\n\n\ncontract Exchange is Restricted {\n using SafeMath for uint256;\n\n AugmintTokenInterface public augmintToken;\n Rates public rates;\n\n uint public constant CHUNK_SIZE = 100;\n\n struct Order {\n uint64 index;\n address maker;\n\n // % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million\n // I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity\n uint32 price;\n\n // buy order: amount in wei\n // sell order: token amount\n uint amount;\n }\n\n uint64 public orderCount;\n mapping(uint64 => Order) public buyTokenOrders;\n mapping(uint64 => Order) public sellTokenOrders;\n\n uint64[] private activeBuyOrders;\n uint64[] private activeSellOrders;\n\n /* used to stop executing matchMultiple when running out of gas.\n actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/\n uint32 private constant ORDER_MATCH_WORST_GAS = 100000;\n\n event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);\n\n event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,\n uint64 sellTokenOrderId, uint publishedRate, uint32 price, uint fillRate, uint weiAmount, uint tokenAmount);\n\n event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);\n\n event RatesContractChanged(Rates newRatesContract);\n\n constructor(AugmintTokenInterface _augmintToken, Rates _rates) public {\n augmintToken = _augmintToken;\n rates = _rates;\n }\n\n /* to allow upgrade of Rates contract */\n function setRatesContract(Rates newRatesContract)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n emit RatesContractChanged(newRatesContract);\n }\n\n function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(msg.value > 0, \"msg.value must be > 0\");\n\n orderId = ++orderCount;\n buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);\n activeBuyOrders.push(orderId);\n\n emit NewOrder(orderId, msg.sender, price, 0, msg.value);\n }\n\n /* this function requires previous approval to transfer tokens */\n function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {\n augmintToken.transferFrom(msg.sender, this, tokenAmount);\n return _placeSellTokenOrder(msg.sender, price, tokenAmount);\n }\n\n /* place sell token order called from AugmintToken's transferAndNotify\n Flow:\n 1) user calls token contract's transferAndNotify price passed in data arg\n 2) transferAndNotify transfers tokens to the Exchange contract\n 3) transferAndNotify calls Exchange.transferNotification with lockProductId\n */\n function transferNotification(address maker, uint tokenAmount, uint price) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n _placeSellTokenOrder(maker, uint32(price), tokenAmount);\n }\n\n function cancelBuyTokenOrder(uint64 buyTokenId) external {\n Order storage order = buyTokenOrders[buyTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeBuyOrder(order);\n\n msg.sender.transfer(amount);\n\n emit CancelledOrder(buyTokenId, msg.sender, 0, amount);\n }\n\n function cancelSellTokenOrder(uint64 sellTokenId) external {\n Order storage order = sellTokenOrders[sellTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeSellOrder(order);\n\n augmintToken.transferWithNarrative(msg.sender, amount, \"Sell token order cancelled\");\n\n emit CancelledOrder(sellTokenId, msg.sender, amount, 0);\n }\n\n /* matches any two orders if the sell price >= buy price\n trade price meets in the middle\n reverts if any of the orders have been removed\n */\n function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {\n _fillOrder(buyTokenId, sellTokenId);\n }\n\n /* matches as many orders as possible from the passed orders\n Runs as long as gas is available for the call.\n Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)\n */\n function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {\n uint len = buyTokenIds.length;\n require(len == sellTokenIds.length, \"buyTokenIds and sellTokenIds lengths must be equal\");\n\n for (uint i = 0; i < len && gasleft() > ORDER_MATCH_WORST_GAS; i++) {\n _fillOrder(buyTokenIds[i], sellTokenIds[i]);\n matchCount++;\n }\n }\n\n function getActiveOrderCounts() external view returns(uint buyTokenOrderCount, uint sellTokenOrderCount) {\n return(activeBuyOrders.length, activeSellOrders.length);\n }\n\n // returns CHUNK_SIZE orders starting from offset\n // orders are encoded as [id, maker, price, amount]\n function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {\n uint64 orderId = activeBuyOrders[offset + i];\n Order storage order = buyTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {\n uint64 orderId = activeSellOrders[offset + i];\n Order storage order = sellTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {\n Order storage buy = buyTokenOrders[buyTokenId];\n Order storage sell = sellTokenOrders[sellTokenId];\n\n require(buy.price >= sell.price, \"buy price must be >= sell price\");\n\n // pick maker's price (whoever placed order sooner considered as maker)\n uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;\n\n uint publishedRate;\n (publishedRate, ) = rates.rates(augmintToken.peggedSymbol());\n uint fillRate = publishedRate.mul(price).roundedDiv(1000000);\n\n uint sellWei = sell.amount.mul(1 ether).roundedDiv(fillRate);\n\n uint tradedWei;\n uint tradedTokens;\n if (sellWei <= buy.amount) {\n tradedWei = sellWei;\n tradedTokens = sell.amount;\n } else {\n tradedWei = buy.amount;\n tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);\n }\n\n buy.amount = buy.amount.sub(tradedWei);\n if (buy.amount == 0) {\n _removeBuyOrder(buy);\n }\n\n sell.amount = sell.amount.sub(tradedTokens);\n if (sell.amount == 0) {\n _removeSellOrder(sell);\n }\n\n augmintToken.transferWithNarrative(buy.maker, tradedTokens, \"Buy token order fill\");\n sell.maker.transfer(tradedWei);\n\n emit OrderFill(buy.maker, sell.maker, buyTokenId,\n sellTokenId, publishedRate, price, fillRate, tradedWei, tradedTokens);\n }\n\n function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)\n private returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(tokenAmount > 0, \"tokenAmount must be > 0\");\n\n orderId = ++orderCount;\n sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);\n activeSellOrders.push(orderId);\n\n emit NewOrder(orderId, maker, price, tokenAmount, 0);\n }\n\n function _removeBuyOrder(Order storage order) private {\n _removeOrder(activeBuyOrders, order.index);\n }\n\n function _removeSellOrder(Order storage order) private {\n _removeOrder(activeSellOrders, order.index);\n }\n\n function _removeOrder(uint64[] storage orders, uint64 index) private {\n if (index < orders.length - 1) {\n orders[index] = orders[orders.length - 1];\n }\n orders.length--;\n }\n\n}\n" + }, + "0xc5b604f8e046dff26642ca544c9eb3064e02ecd9": { + "generatedAt": "2018-05-12T01:34:38.557Z", + "truffleContractFileUpdatedAt": "2018-05-12T01:30:50.090Z", + "deployTransactionHash": "0x7f022b010da30ab1c76d7c976a0b8f19ac6d7eab34ba46d19fcbfad1695fcb21", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "0c3aae2a1e26a2cc4f1800e62fa873ee", + "deployedBytecodeHash": "4c6ad15993389d8d7326c731b2b92d63", + "sourceHash": "6e27bdbdc64d0d9cf52de1abc46c4cf7", + "source": "/* Augmint's Internal Exchange\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/exchangeFlow.png\n\n TODO:\n - change to wihtdrawal pattern, see: https://github.com/Augmint/augmint-contracts/issues/17\n - deduct fee\n - consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)\n - use Rates interface?\n*/\npragma solidity ^0.4.23;\n\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./Rates.sol\";\n\n\ncontract Exchange is Restricted {\n using SafeMath for uint256;\n\n AugmintTokenInterface public augmintToken;\n Rates public rates;\n\n uint public constant CHUNK_SIZE = 100;\n\n struct Order {\n uint64 index;\n address maker;\n\n // % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million\n // I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity\n uint32 price;\n\n // buy order: amount in wei\n // sell order: token amount\n uint amount;\n }\n\n uint64 public orderCount;\n mapping(uint64 => Order) public buyTokenOrders;\n mapping(uint64 => Order) public sellTokenOrders;\n\n uint64[] private activeBuyOrders;\n uint64[] private activeSellOrders;\n\n /* used to stop executing matchMultiple when running out of gas.\n actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/\n uint32 private constant ORDER_MATCH_WORST_GAS = 100000;\n\n event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);\n\n event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,\n uint64 sellTokenOrderId, uint publishedRate, uint32 price, uint fillRate, uint weiAmount, uint tokenAmount);\n\n event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);\n\n event RatesContractChanged(Rates newRatesContract);\n\n constructor(AugmintTokenInterface _augmintToken, Rates _rates) public {\n augmintToken = _augmintToken;\n rates = _rates;\n }\n\n /* to allow upgrade of Rates contract */\n function setRatesContract(Rates newRatesContract)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n emit RatesContractChanged(newRatesContract);\n }\n\n function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(msg.value > 0, \"msg.value must be > 0\");\n\n orderId = ++orderCount;\n buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);\n activeBuyOrders.push(orderId);\n\n emit NewOrder(orderId, msg.sender, price, 0, msg.value);\n }\n\n /* this function requires previous approval to transfer tokens */\n function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {\n augmintToken.transferFrom(msg.sender, this, tokenAmount);\n return _placeSellTokenOrder(msg.sender, price, tokenAmount);\n }\n\n /* place sell token order called from AugmintToken's transferAndNotify\n Flow:\n 1) user calls token contract's transferAndNotify price passed in data arg\n 2) transferAndNotify transfers tokens to the Exchange contract\n 3) transferAndNotify calls Exchange.transferNotification with lockProductId\n */\n function transferNotification(address maker, uint tokenAmount, uint price) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n _placeSellTokenOrder(maker, uint32(price), tokenAmount);\n }\n\n function cancelBuyTokenOrder(uint64 buyTokenId) external {\n Order storage order = buyTokenOrders[buyTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeBuyOrder(order);\n\n msg.sender.transfer(amount);\n\n emit CancelledOrder(buyTokenId, msg.sender, 0, amount);\n }\n\n function cancelSellTokenOrder(uint64 sellTokenId) external {\n Order storage order = sellTokenOrders[sellTokenId];\n require(order.maker == msg.sender, \"msg.sender must be order.maker\");\n\n uint amount = order.amount;\n order.amount = 0;\n _removeSellOrder(order);\n\n augmintToken.transferWithNarrative(msg.sender, amount, \"Sell token order cancelled\");\n\n emit CancelledOrder(sellTokenId, msg.sender, amount, 0);\n }\n\n /* matches any two orders if the sell price >= buy price\n trade price meets in the middle\n reverts if any of the orders have been removed\n */\n function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {\n _fillOrder(buyTokenId, sellTokenId);\n }\n\n /* matches as many orders as possible from the passed orders\n Runs as long as gas is available for the call.\n Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)\n */\n function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {\n uint len = buyTokenIds.length;\n require(len == sellTokenIds.length, \"buyTokenIds and sellTokenIds lengths must be equal\");\n\n for (uint i = 0; i < len && gasleft() > ORDER_MATCH_WORST_GAS; i++) {\n _fillOrder(buyTokenIds[i], sellTokenIds[i]);\n matchCount++;\n }\n }\n\n function getActiveOrderCounts() external view returns(uint buyTokenOrderCount, uint sellTokenOrderCount) {\n return(activeBuyOrders.length, activeSellOrders.length);\n }\n\n // returns CHUNK_SIZE orders starting from offset\n // orders are encoded as [id, maker, price, amount]\n function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {\n uint64 orderId = activeBuyOrders[offset + i];\n Order storage order = buyTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {\n uint64 orderId = activeSellOrders[offset + i];\n Order storage order = sellTokenOrders[orderId];\n response[i] = [orderId, uint(order.maker), order.price, order.amount];\n }\n }\n\n function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {\n Order storage buy = buyTokenOrders[buyTokenId];\n Order storage sell = sellTokenOrders[sellTokenId];\n\n require(buy.price >= sell.price, \"buy price must be >= sell price\");\n\n // pick maker's price (whoever placed order sooner considered as maker)\n uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;\n\n uint publishedRate;\n (publishedRate, ) = rates.rates(augmintToken.peggedSymbol());\n uint fillRate = publishedRate.mul(price).roundedDiv(1000000);\n\n uint sellWei = sell.amount.mul(1 ether).roundedDiv(fillRate);\n\n uint tradedWei;\n uint tradedTokens;\n if (sellWei <= buy.amount) {\n tradedWei = sellWei;\n tradedTokens = sell.amount;\n } else {\n tradedWei = buy.amount;\n tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);\n }\n\n buy.amount = buy.amount.sub(tradedWei);\n if (buy.amount == 0) {\n _removeBuyOrder(buy);\n }\n\n sell.amount = sell.amount.sub(tradedTokens);\n if (sell.amount == 0) {\n _removeSellOrder(sell);\n }\n\n augmintToken.transferWithNarrative(buy.maker, tradedTokens, \"Buy token order fill\");\n sell.maker.transfer(tradedWei);\n\n emit OrderFill(buy.maker, sell.maker, buyTokenId,\n sellTokenId, publishedRate, price, fillRate, tradedWei, tradedTokens);\n }\n\n function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)\n private returns (uint64 orderId) {\n require(price > 0, \"price must be > 0\");\n require(tokenAmount > 0, \"tokenAmount must be > 0\");\n\n orderId = ++orderCount;\n sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);\n activeSellOrders.push(orderId);\n\n emit NewOrder(orderId, maker, price, tokenAmount, 0);\n }\n\n function _removeBuyOrder(Order storage order) private {\n _removeOrder(activeBuyOrders, order.index);\n }\n\n function _removeSellOrder(Order storage order) private {\n _removeOrder(activeSellOrders, order.index);\n }\n\n function _removeOrder(uint64[] storage orders, uint64 index) private {\n if (index < orders.length - 1) {\n orders[index] = orders[orders.length - 1];\n }\n orders.length--;\n }\n\n}\n" } } } diff --git a/abiniser/deployments/4/LoanManager_DEPLOYS.json b/abiniser/deployments/4/LoanManager_DEPLOYS.json index 0d49d1cb..c23b02f0 100644 --- a/abiniser/deployments/4/LoanManager_DEPLOYS.json +++ b/abiniser/deployments/4/LoanManager_DEPLOYS.json @@ -37,7 +37,7 @@ } }, "291572b8d2ffe95dca1733ebc1472e08": { - "latestDeployedAddress": "0xbdb02f82d7ad574f9f549895caf41e23a8981b07", + "latestDeployedAddress": "0x214919abe3f2b7ca7a43a799c4fc7132bbf78e8a", "deployments": { "0xbdb02f82d7ad574f9f549895caf41e23a8981b07": { "generatedAt": "2018-04-25T12:31:29.206Z", @@ -51,6 +51,19 @@ "deployedBytecodeHash": "7b195ed87405ef49bec0955ea58bb0ea", "sourceHash": "9d5f96db98b6d336c18b8c6df5c7cd92", "source": "/*\n Contract to manage Augmint token loan contracts backed by ETH\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/loanFlow.png\n\n TODO:\n - create MonetarySupervisor interface and use it instead?\n - make data arg generic bytes?\n - make collect() run as long as gas provided allows\n*/\npragma solidity ^0.4.23;\n\nimport \"./Rates.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\n\n\ncontract LoanManager is Restricted {\n using SafeMath for uint256;\n\n uint16 public constant CHUNK_SIZE = 100;\n\n enum LoanState { Open, Repaid, Defaulted, Collected } // NB: Defaulted state is not stored, only getters calculate\n\n struct LoanProduct {\n uint minDisbursedAmount; // 0: with decimals set in AugmintToken.decimals\n uint32 term; // 1\n uint32 discountRate; // 2: discountRate in parts per million , ie. 10,000 = 1%\n uint32 collateralRatio; // 3: loan token amount / colleteral pegged ccy value\n // in parts per million , ie. 10,000 = 1%\n uint32 defaultingFeePt; // 4: % of collateral in parts per million , ie. 50,000 = 5%\n bool isActive; // 5\n }\n\n /* NB: we don't need to store loan parameters because loan products can't be altered (only disabled/enabled) */\n struct LoanData {\n uint collateralAmount; // 0\n uint repaymentAmount; // 1\n address borrower; // 2\n uint32 productId; // 3\n LoanState state; // 4\n uint40 maturity; // 5\n }\n\n LoanProduct[] public products;\n\n LoanData[] public loans;\n mapping(address => uint[]) public accountLoans; // owner account address => array of loan Ids\n\n Rates public rates; // instance of ETH/pegged currency rate provider contract\n AugmintTokenInterface public augmintToken; // instance of token contract\n MonetarySupervisor public monetarySupervisor;\n\n event NewLoan(uint32 productId, uint loanId, address indexed borrower, uint collateralAmount, uint loanAmount,\n uint repaymentAmount, uint40 maturity);\n\n event LoanProductActiveStateChanged(uint32 productId, bool newState);\n\n event LoanProductAdded(uint32 productId);\n\n event LoanRepayed(uint loanId, address borrower);\n\n event LoanCollected(uint loanId, address indexed borrower, uint collectedCollateral,\n uint releasedCollateral, uint defaultingFee);\n\n event SystemContractsChanged(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor);\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor, Rates _rates)\n public {\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n rates = _rates;\n }\n\n function addLoanProduct(uint32 term, uint32 discountRate, uint32 collateralRatio, uint minDisbursedAmount,\n uint32 defaultingFeePt, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newProductId = products.push(\n LoanProduct(minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, isActive)\n ) - 1;\n\n uint32 newProductId = uint32(_newProductId);\n require(newProductId == _newProductId, \"productId overflow\");\n\n emit LoanProductAdded(newProductId);\n }\n\n function setLoanProductActiveState(uint32 productId, bool newState)\n external restrict (\"MonetaryBoard\") {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n products[productId].isActive = false;\n emit LoanProductActiveStateChanged(productId, newState);\n }\n\n function newEthBackedLoan(uint32 productId) external payable {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n LoanProduct storage product = products[productId];\n require(product.isActive, \"product must be in active state\"); // valid product\n\n\n // calculate loan values based on ETH sent in with Tx\n uint tokenValue = rates.convertFromWei(augmintToken.peggedSymbol(), msg.value);\n uint repaymentAmount = tokenValue.mul(product.collateralRatio).div(1000000);\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, repaymentAmount);\n\n require(loanAmount >= product.minDisbursedAmount, \"loanAmount must be >= minDisbursedAmount\");\n\n uint expiration = now.add(product.term);\n uint40 maturity = uint40(expiration);\n require(maturity == expiration, \"maturity overflow\");\n\n // Create new loan\n uint loanId = loans.push(LoanData(msg.value, repaymentAmount, msg.sender,\n productId, LoanState.Open, maturity)) - 1;\n\n // Store ref to new loan\n accountLoans[msg.sender].push(loanId);\n\n // Issue tokens and send to borrower\n monetarySupervisor.issueLoan(msg.sender, loanAmount);\n\n emit NewLoan(productId, loanId, msg.sender, msg.value, loanAmount, repaymentAmount, maturity);\n }\n\n /* repay loan, called from AugmintToken's transferAndNotify\n Flow for repaying loan:\n 1) user calls token contract's transferAndNotify loanId passed in data arg\n 2) transferAndNotify transfers tokens to the Lender contract\n 3) transferAndNotify calls Lender.transferNotification with lockProductId\n */\n // from arg is not used as we allow anyone to repay a loan:\n function transferNotification(address, uint repaymentAmount, uint loanId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n\n _repayLoan(loanId, repaymentAmount);\n }\n\n function collect(uint[] loanIds) external {\n /* when there are a lots of loans to be collected then\n the client need to call it in batches to make sure tx won't exceed block gas limit.\n Anyone can call it - can't cause harm as it only allows to collect loans which they are defaulted\n TODO: optimise defaulting fee calculations\n */\n uint totalLoanAmountCollected;\n uint totalCollateralToCollect;\n uint totalDefaultingFee;\n for (uint i = 0; i < loanIds.length; i++) {\n require(i < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanIds[i]];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(now >= loan.maturity, \"current time must be later than maturity\");\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, loan.repaymentAmount);\n\n totalLoanAmountCollected = totalLoanAmountCollected.add(loanAmount);\n\n loan.state = LoanState.Collected;\n\n // send ETH collateral to augmintToken reserve\n uint defaultingFeeInToken = loan.repaymentAmount.mul(product.defaultingFeePt).div(1000000);\n uint defaultingFee = rates.convertToWei(augmintToken.peggedSymbol(), defaultingFeeInToken);\n uint targetCollection = rates.convertToWei(augmintToken.peggedSymbol(),\n loan.repaymentAmount).add(defaultingFee);\n\n uint releasedCollateral;\n if (targetCollection < loan.collateralAmount) {\n releasedCollateral = loan.collateralAmount.sub(targetCollection);\n loan.borrower.transfer(releasedCollateral);\n }\n uint collateralToCollect = loan.collateralAmount.sub(releasedCollateral);\n if (defaultingFee >= collateralToCollect) {\n defaultingFee = collateralToCollect;\n collateralToCollect = 0;\n } else {\n collateralToCollect = collateralToCollect.sub(defaultingFee);\n }\n totalDefaultingFee = totalDefaultingFee.add(defaultingFee);\n\n totalCollateralToCollect = totalCollateralToCollect.add(collateralToCollect);\n\n emit LoanCollected(loanIds[i], loan.borrower, collateralToCollect.add(defaultingFee), releasedCollateral, defaultingFee);\n }\n\n if (totalCollateralToCollect > 0) {\n address(monetarySupervisor.augmintReserves()).transfer(totalCollateralToCollect);\n }\n\n if (totalDefaultingFee > 0){\n address(augmintToken.feeAccount()).transfer(totalDefaultingFee);\n }\n\n monetarySupervisor.loanCollectionNotification(totalLoanAmountCollected);// update KPIs\n\n }\n\n /* to allow upgrade of Rates and MonetarySupervisor contracts */\n function setSystemContracts(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n monetarySupervisor = newMonetarySupervisor;\n emit SystemContractsChanged(newRatesContract, newMonetarySupervisor);\n }\n\n function getProductCount() external view returns (uint ct) {\n return products.length;\n }\n\n // returns CHUNK_SIZE loan products starting from some offset:\n // [ productId, minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, maxLoanAmount, isActive ]\n function getProducts(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= products.length) { break; }\n\n LoanProduct storage product = products[offset + i];\n\n response[i] = [offset + i, product.minDisbursedAmount, product.term, product.discountRate,\n product.collateralRatio, product.defaultingFeePt,\n monetarySupervisor.getMaxLoanAmount(product.minDisbursedAmount), product.isActive ? 1 : 0 ];\n }\n }\n\n function getLoanCount() external view returns (uint ct) {\n return loans.length;\n }\n\n /* returns CHUNK_SIZE loans starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoans(uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loans.length) { break; }\n\n response[i] = getLoanTuple(offset + i);\n }\n }\n\n function getLoanCountForAddress(address borrower) external view returns (uint) {\n return accountLoans[borrower].length;\n }\n\n /* returns CHUNK_SIZE loans of a given account, starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoansForAddress(address borrower, uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n uint[] storage loansForAddress = accountLoans[borrower];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loansForAddress.length) { break; }\n\n response[i] = getLoanTuple(loansForAddress[offset + i]);\n }\n }\n\n function getLoanTuple(uint loanId) public view returns (uint[10] result) {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n uint disbursementTime = loan.maturity - product.term;\n\n LoanState loanState =\n loan.state == LoanState.Open && now >= loan.maturity ? LoanState.Defaulted : loan.state;\n\n result = [loanId, loan.collateralAmount, loan.repaymentAmount, uint(loan.borrower),\n loan.productId, uint(loanState), loan.maturity, disbursementTime, loanAmount, interestAmount];\n }\n\n function calculateLoanValues(LoanProduct storage product, uint repaymentAmount)\n internal view returns (uint loanAmount, uint interestAmount) {\n // calculate loan values based on repayment amount\n loanAmount = repaymentAmount.mul(product.discountRate).div(1000000);\n interestAmount = loanAmount > repaymentAmount ? 0 : repaymentAmount.sub(loanAmount);\n }\n\n /* internal function, assuming repayment amount already transfered */\n function _repayLoan(uint loanId, uint repaymentAmount) internal {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(repaymentAmount == loan.repaymentAmount, \"repaymentAmount must be equal to tokens sent\");\n require(now <= loan.maturity, \"current time must be earlier than maturity\");\n\n LoanProduct storage product = products[loan.productId];\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n\n loans[loanId].state = LoanState.Repaid;\n\n if (interestAmount > 0) {\n augmintToken.transfer(monetarySupervisor.interestEarnedAccount(), interestAmount);\n augmintToken.burn(loanAmount);\n } else {\n // negative or zero interest (i.e. discountRate >= 0)\n augmintToken.burn(repaymentAmount);\n }\n\n monetarySupervisor.loanRepaymentNotification(loanAmount); // update KPIs\n\n loan.borrower.transfer(loan.collateralAmount); // send back ETH collateral\n\n emit LoanRepayed(loanId, loan.borrower);\n }\n\n}\n" + }, + "0x214919abe3f2b7ca7a43a799c4fc7132bbf78e8a": { + "generatedAt": "2018-05-12T01:34:38.508Z", + "truffleContractFileUpdatedAt": "2018-05-12T01:30:50.403Z", + "deployTransactionHash": "0x809f88d01aa3ddef9c215492f1e6dcf18db2efecc925469af77b49a2c4df3b93", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "feef104fe0b04cf9bde178c2f8ee4c04", + "deployedBytecodeHash": "d16030995b358f9fd29dafc6df7e6d35", + "sourceHash": "9d5f96db98b6d336c18b8c6df5c7cd92", + "source": "/*\n Contract to manage Augmint token loan contracts backed by ETH\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/loanFlow.png\n\n TODO:\n - create MonetarySupervisor interface and use it instead?\n - make data arg generic bytes?\n - make collect() run as long as gas provided allows\n*/\npragma solidity ^0.4.23;\n\nimport \"./Rates.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\n\n\ncontract LoanManager is Restricted {\n using SafeMath for uint256;\n\n uint16 public constant CHUNK_SIZE = 100;\n\n enum LoanState { Open, Repaid, Defaulted, Collected } // NB: Defaulted state is not stored, only getters calculate\n\n struct LoanProduct {\n uint minDisbursedAmount; // 0: with decimals set in AugmintToken.decimals\n uint32 term; // 1\n uint32 discountRate; // 2: discountRate in parts per million , ie. 10,000 = 1%\n uint32 collateralRatio; // 3: loan token amount / colleteral pegged ccy value\n // in parts per million , ie. 10,000 = 1%\n uint32 defaultingFeePt; // 4: % of collateral in parts per million , ie. 50,000 = 5%\n bool isActive; // 5\n }\n\n /* NB: we don't need to store loan parameters because loan products can't be altered (only disabled/enabled) */\n struct LoanData {\n uint collateralAmount; // 0\n uint repaymentAmount; // 1\n address borrower; // 2\n uint32 productId; // 3\n LoanState state; // 4\n uint40 maturity; // 5\n }\n\n LoanProduct[] public products;\n\n LoanData[] public loans;\n mapping(address => uint[]) public accountLoans; // owner account address => array of loan Ids\n\n Rates public rates; // instance of ETH/pegged currency rate provider contract\n AugmintTokenInterface public augmintToken; // instance of token contract\n MonetarySupervisor public monetarySupervisor;\n\n event NewLoan(uint32 productId, uint loanId, address indexed borrower, uint collateralAmount, uint loanAmount,\n uint repaymentAmount, uint40 maturity);\n\n event LoanProductActiveStateChanged(uint32 productId, bool newState);\n\n event LoanProductAdded(uint32 productId);\n\n event LoanRepayed(uint loanId, address borrower);\n\n event LoanCollected(uint loanId, address indexed borrower, uint collectedCollateral,\n uint releasedCollateral, uint defaultingFee);\n\n event SystemContractsChanged(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor);\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor, Rates _rates)\n public {\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n rates = _rates;\n }\n\n function addLoanProduct(uint32 term, uint32 discountRate, uint32 collateralRatio, uint minDisbursedAmount,\n uint32 defaultingFeePt, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newProductId = products.push(\n LoanProduct(minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, isActive)\n ) - 1;\n\n uint32 newProductId = uint32(_newProductId);\n require(newProductId == _newProductId, \"productId overflow\");\n\n emit LoanProductAdded(newProductId);\n }\n\n function setLoanProductActiveState(uint32 productId, bool newState)\n external restrict (\"MonetaryBoard\") {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n products[productId].isActive = false;\n emit LoanProductActiveStateChanged(productId, newState);\n }\n\n function newEthBackedLoan(uint32 productId) external payable {\n require(productId < products.length, \"invalid productId\"); // next line would revert but require to emit reason\n LoanProduct storage product = products[productId];\n require(product.isActive, \"product must be in active state\"); // valid product\n\n\n // calculate loan values based on ETH sent in with Tx\n uint tokenValue = rates.convertFromWei(augmintToken.peggedSymbol(), msg.value);\n uint repaymentAmount = tokenValue.mul(product.collateralRatio).div(1000000);\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, repaymentAmount);\n\n require(loanAmount >= product.minDisbursedAmount, \"loanAmount must be >= minDisbursedAmount\");\n\n uint expiration = now.add(product.term);\n uint40 maturity = uint40(expiration);\n require(maturity == expiration, \"maturity overflow\");\n\n // Create new loan\n uint loanId = loans.push(LoanData(msg.value, repaymentAmount, msg.sender,\n productId, LoanState.Open, maturity)) - 1;\n\n // Store ref to new loan\n accountLoans[msg.sender].push(loanId);\n\n // Issue tokens and send to borrower\n monetarySupervisor.issueLoan(msg.sender, loanAmount);\n\n emit NewLoan(productId, loanId, msg.sender, msg.value, loanAmount, repaymentAmount, maturity);\n }\n\n /* repay loan, called from AugmintToken's transferAndNotify\n Flow for repaying loan:\n 1) user calls token contract's transferAndNotify loanId passed in data arg\n 2) transferAndNotify transfers tokens to the Lender contract\n 3) transferAndNotify calls Lender.transferNotification with lockProductId\n */\n // from arg is not used as we allow anyone to repay a loan:\n function transferNotification(address, uint repaymentAmount, uint loanId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n\n _repayLoan(loanId, repaymentAmount);\n }\n\n function collect(uint[] loanIds) external {\n /* when there are a lots of loans to be collected then\n the client need to call it in batches to make sure tx won't exceed block gas limit.\n Anyone can call it - can't cause harm as it only allows to collect loans which they are defaulted\n TODO: optimise defaulting fee calculations\n */\n uint totalLoanAmountCollected;\n uint totalCollateralToCollect;\n uint totalDefaultingFee;\n for (uint i = 0; i < loanIds.length; i++) {\n require(i < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanIds[i]];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(now >= loan.maturity, \"current time must be later than maturity\");\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n (loanAmount, ) = calculateLoanValues(product, loan.repaymentAmount);\n\n totalLoanAmountCollected = totalLoanAmountCollected.add(loanAmount);\n\n loan.state = LoanState.Collected;\n\n // send ETH collateral to augmintToken reserve\n uint defaultingFeeInToken = loan.repaymentAmount.mul(product.defaultingFeePt).div(1000000);\n uint defaultingFee = rates.convertToWei(augmintToken.peggedSymbol(), defaultingFeeInToken);\n uint targetCollection = rates.convertToWei(augmintToken.peggedSymbol(),\n loan.repaymentAmount).add(defaultingFee);\n\n uint releasedCollateral;\n if (targetCollection < loan.collateralAmount) {\n releasedCollateral = loan.collateralAmount.sub(targetCollection);\n loan.borrower.transfer(releasedCollateral);\n }\n uint collateralToCollect = loan.collateralAmount.sub(releasedCollateral);\n if (defaultingFee >= collateralToCollect) {\n defaultingFee = collateralToCollect;\n collateralToCollect = 0;\n } else {\n collateralToCollect = collateralToCollect.sub(defaultingFee);\n }\n totalDefaultingFee = totalDefaultingFee.add(defaultingFee);\n\n totalCollateralToCollect = totalCollateralToCollect.add(collateralToCollect);\n\n emit LoanCollected(loanIds[i], loan.borrower, collateralToCollect.add(defaultingFee), releasedCollateral, defaultingFee);\n }\n\n if (totalCollateralToCollect > 0) {\n address(monetarySupervisor.augmintReserves()).transfer(totalCollateralToCollect);\n }\n\n if (totalDefaultingFee > 0){\n address(augmintToken.feeAccount()).transfer(totalDefaultingFee);\n }\n\n monetarySupervisor.loanCollectionNotification(totalLoanAmountCollected);// update KPIs\n\n }\n\n /* to allow upgrade of Rates and MonetarySupervisor contracts */\n function setSystemContracts(Rates newRatesContract, MonetarySupervisor newMonetarySupervisor)\n external restrict(\"MonetaryBoard\") {\n rates = newRatesContract;\n monetarySupervisor = newMonetarySupervisor;\n emit SystemContractsChanged(newRatesContract, newMonetarySupervisor);\n }\n\n function getProductCount() external view returns (uint ct) {\n return products.length;\n }\n\n // returns CHUNK_SIZE loan products starting from some offset:\n // [ productId, minDisbursedAmount, term, discountRate, collateralRatio, defaultingFeePt, maxLoanAmount, isActive ]\n function getProducts(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= products.length) { break; }\n\n LoanProduct storage product = products[offset + i];\n\n response[i] = [offset + i, product.minDisbursedAmount, product.term, product.discountRate,\n product.collateralRatio, product.defaultingFeePt,\n monetarySupervisor.getMaxLoanAmount(product.minDisbursedAmount), product.isActive ? 1 : 0 ];\n }\n }\n\n function getLoanCount() external view returns (uint ct) {\n return loans.length;\n }\n\n /* returns CHUNK_SIZE loans starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoans(uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loans.length) { break; }\n\n response[i] = getLoanTuple(offset + i);\n }\n }\n\n function getLoanCountForAddress(address borrower) external view returns (uint) {\n return accountLoans[borrower].length;\n }\n\n /* returns CHUNK_SIZE loans of a given account, starting from some offset. Loans data encoded as:\n [loanId, collateralAmount, repaymentAmount, borrower, productId, state, maturity, disbursementTime,\n loanAmount, interestAmount ] */\n function getLoansForAddress(address borrower, uint offset) external view returns (uint[10][CHUNK_SIZE] response) {\n\n uint[] storage loansForAddress = accountLoans[borrower];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= loansForAddress.length) { break; }\n\n response[i] = getLoanTuple(loansForAddress[offset + i]);\n }\n }\n\n function getLoanTuple(uint loanId) public view returns (uint[10] result) {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n LoanProduct storage product = products[loan.productId];\n\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n uint disbursementTime = loan.maturity - product.term;\n\n LoanState loanState =\n loan.state == LoanState.Open && now >= loan.maturity ? LoanState.Defaulted : loan.state;\n\n result = [loanId, loan.collateralAmount, loan.repaymentAmount, uint(loan.borrower),\n loan.productId, uint(loanState), loan.maturity, disbursementTime, loanAmount, interestAmount];\n }\n\n function calculateLoanValues(LoanProduct storage product, uint repaymentAmount)\n internal view returns (uint loanAmount, uint interestAmount) {\n // calculate loan values based on repayment amount\n loanAmount = repaymentAmount.mul(product.discountRate).div(1000000);\n interestAmount = loanAmount > repaymentAmount ? 0 : repaymentAmount.sub(loanAmount);\n }\n\n /* internal function, assuming repayment amount already transfered */\n function _repayLoan(uint loanId, uint repaymentAmount) internal {\n require(loanId < loans.length, \"invalid loanId\"); // next line would revert but require to emit reason\n LoanData storage loan = loans[loanId];\n require(loan.state == LoanState.Open, \"loan state must be Open\");\n require(repaymentAmount == loan.repaymentAmount, \"repaymentAmount must be equal to tokens sent\");\n require(now <= loan.maturity, \"current time must be earlier than maturity\");\n\n LoanProduct storage product = products[loan.productId];\n uint loanAmount;\n uint interestAmount;\n (loanAmount, interestAmount) = calculateLoanValues(product, loan.repaymentAmount);\n\n loans[loanId].state = LoanState.Repaid;\n\n if (interestAmount > 0) {\n augmintToken.transfer(monetarySupervisor.interestEarnedAccount(), interestAmount);\n augmintToken.burn(loanAmount);\n } else {\n // negative or zero interest (i.e. discountRate >= 0)\n augmintToken.burn(repaymentAmount);\n }\n\n monetarySupervisor.loanRepaymentNotification(loanAmount); // update KPIs\n\n loan.borrower.transfer(loan.collateralAmount); // send back ETH collateral\n\n emit LoanRepayed(loanId, loan.borrower);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/4/Locker_DEPLOYS.json b/abiniser/deployments/4/Locker_DEPLOYS.json index f79cdd6b..4b58877e 100644 --- a/abiniser/deployments/4/Locker_DEPLOYS.json +++ b/abiniser/deployments/4/Locker_DEPLOYS.json @@ -37,7 +37,7 @@ } }, "66e3e89133d9bbd91baac5552f21f7e1": { - "latestDeployedAddress": "0xf98ae1fb568b267a7632bf54579a153c892e2ec2", + "latestDeployedAddress": "0xd0b6136c2e35c288a903e836feb9535954e4a9e9", "deployments": { "0xf98ae1fb568b267a7632bf54579a153c892e2ec2": { "generatedAt": "2018-04-25T12:31:29.231Z", @@ -51,6 +51,19 @@ "deployedBytecodeHash": "c88454f70e3cc7b363b425ef66ef47ca", "sourceHash": "7ffe14f90465530802dc1f5762e0217f", "source": "/* contract for tracking locked funds\n\n requirements\n -> lock funds\n -> unlock funds\n -> index locks by address\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/lockFlow.png\n\n TODO / think about:\n -> self-destruct function?\n\n*/\n\npragma solidity ^0.4.23;\n\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\n\n\ncontract Locker is Restricted, TokenReceiver {\n\n using SafeMath for uint256;\n\n uint public constant CHUNK_SIZE = 100;\n\n event NewLockProduct(uint32 indexed lockProductId, uint32 perTermInterest, uint32 durationInSecs,\n uint32 minimumLockAmount, bool isActive);\n\n event LockProductActiveChange(uint32 indexed lockProductId, bool newActiveState);\n\n // NB: amountLocked includes the original amount, plus interest\n event NewLock(address indexed lockOwner, uint lockId, uint amountLocked, uint interestEarned,\n uint40 lockedUntil, uint32 perTermInterest, uint32 durationInSecs);\n\n event LockReleased(address indexed lockOwner, uint lockId);\n\n event MonetarySupervisorChanged(MonetarySupervisor newMonetarySupervisor);\n\n struct LockProduct {\n // perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n uint32 perTermInterest;\n uint32 durationInSecs;\n uint32 minimumLockAmount;\n bool isActive;\n }\n\n /* NB: we don't need to store lock parameters because lockProducts can't be altered (only disabled/enabled) */\n struct Lock {\n uint amountLocked;\n address owner;\n uint32 productId;\n uint40 lockedUntil;\n bool isActive;\n }\n\n AugmintTokenInterface public augmintToken;\n MonetarySupervisor public monetarySupervisor;\n\n LockProduct[] public lockProducts;\n\n Lock[] public locks;\n\n // lock ids for an account\n mapping(address => uint[]) public accountLocks;\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor) public {\n\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n\n }\n\n function addLockProduct(uint32 perTermInterest, uint32 durationInSecs, uint32 minimumLockAmount, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newLockProductId = lockProducts.push(\n LockProduct(perTermInterest, durationInSecs, minimumLockAmount, isActive)) - 1;\n uint32 newLockProductId = uint32(_newLockProductId);\n require(newLockProductId == _newLockProductId, \"lockProduct overflow\");\n emit NewLockProduct(newLockProductId, perTermInterest, durationInSecs, minimumLockAmount, isActive);\n\n }\n\n function setLockProductActiveState(uint32 lockProductId, bool isActive) external restrict(\"MonetaryBoard\") {\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n\n lockProducts[lockProductId].isActive = isActive;\n emit LockProductActiveChange(lockProductId, isActive);\n\n }\n\n /* lock funds, called from AugmintToken's transferAndNotify\n Flow for locking tokens:\n 1) user calls token contract's transferAndNotify lockProductId passed in data arg\n 2) transferAndNotify transfers tokens to the Lock contract\n 3) transferAndNotify calls Lock.transferNotification with lockProductId\n */\n function transferNotification(address from, uint256 amountToLock, uint _lockProductId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n uint32 lockProductId = uint32(_lockProductId);\n require(lockProductId == _lockProductId, \"lockProductId overflow\");\n /* TODO: make data arg generic bytes\n uint productId;\n assembly { // solhint-disable-line no-inline-assembly\n productId := mload(data)\n } */\n _createLock(lockProductId, from, amountToLock);\n }\n\n function releaseFunds(uint lockId) external {\n // next line would revert but require to emit reason:\n require(lockId < locks.length, \"invalid lockId\");\n Lock storage lock = locks[lockId];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n require(lock.isActive, \"lock must be in active state\");\n require(now >= lock.lockedUntil, \"current time must be later than lockedUntil\");\n\n lock.isActive = false;\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n monetarySupervisor.releaseFundsNotification(lock.amountLocked); // to maintain totalLockAmount\n augmintToken.transferWithNarrative(lock.owner, lock.amountLocked.add(interestEarned),\n \"Funds released from lock\");\n\n emit LockReleased(lock.owner, lockId);\n }\n\n function setMonetarySupervisor(MonetarySupervisor newMonetarySupervisor) external restrict(\"MonetaryBoard\") {\n monetarySupervisor = newMonetarySupervisor;\n emit MonetarySupervisorChanged(newMonetarySupervisor);\n }\n\n function getLockProductCount() external view returns (uint) {\n\n return lockProducts.length;\n\n }\n\n // returns 20 lock products starting from some offset\n // lock products are encoded as [ perTermInterest, durationInSecs, minimumLockAmount, maxLockAmount, isActive ]\n function getLockProducts(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= lockProducts.length) { break; }\n\n LockProduct storage lockProduct = lockProducts[offset + i];\n\n response[i] = [ lockProduct.perTermInterest, lockProduct.durationInSecs, lockProduct.minimumLockAmount,\n monetarySupervisor.getMaxLockAmount(lockProduct.minimumLockAmount, lockProduct.perTermInterest),\n lockProduct.isActive ? 1 : 0 ];\n }\n }\n\n function getLockCount() external view returns (uint) {\n return locks.length;\n }\n\n function getLockCountForAddress(address lockOwner) external view returns (uint) {\n return accountLocks[lockOwner].length;\n }\n\n // returns CHUNK_SIZE locks starting from some offset\n // lock products are encoded as\n // [lockId, owner, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n // NB: perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n function getLocks(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locks.length) { break; }\n\n Lock storage lock = locks[offset + i];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [uint(offset + i), uint(lock.owner), lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0];\n }\n }\n\n // returns CHUNK_SIZE locks of a given account, starting from some offset\n // lock products are encoded as\n // [lockId, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n function getLocksForAddress(address lockOwner, uint offset) external view returns (uint[7][CHUNK_SIZE] response) {\n\n uint[] storage locksForAddress = accountLocks[lockOwner];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locksForAddress.length) { break; }\n\n Lock storage lock = locks[locksForAddress[offset + i]];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [ locksForAddress[offset + i], lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0 ];\n }\n }\n\n function calculateInterest(uint32 perTermInterest, uint amountToLock) public pure returns (uint interestEarned) {\n interestEarned = amountToLock.mul(perTermInterest).div(1000000);\n }\n\n // Internal function. assumes amountToLock is already transferred to this Lock contract\n function _createLock(uint32 lockProductId, address lockOwner, uint amountToLock) internal returns(uint lockId) {\n LockProduct storage lockProduct = lockProducts[lockProductId];\n require(lockProduct.isActive, \"lockProduct must be in active state\");\n require(amountToLock >= lockProduct.minimumLockAmount, \"amountToLock must be >= minimumLockAmount\");\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, amountToLock);\n uint expiration = now.add(lockProduct.durationInSecs);\n uint40 lockedUntil = uint40(expiration);\n require(lockedUntil == expiration, \"lockedUntil overflow\");\n\n lockId = locks.push(Lock(amountToLock, lockOwner, lockProductId, lockedUntil, true)) - 1;\n accountLocks[lockOwner].push(lockId);\n\n monetarySupervisor.requestInterest(amountToLock, interestEarned); // update KPIs & transfer interest here\n\n emit NewLock(lockOwner, lockId, amountToLock, interestEarned, lockedUntil, lockProduct.perTermInterest,\n lockProduct.durationInSecs);\n }\n\n}\n" + }, + "0xd0b6136c2e35c288a903e836feb9535954e4a9e9": { + "generatedAt": "2018-05-12T01:34:38.533Z", + "truffleContractFileUpdatedAt": "2018-05-12T01:30:50.619Z", + "deployTransactionHash": "0x827346689bfd341aaa1020c44d18f77943fd67afe095aa0a2c2eefe809e78a22", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "c6c9a4350153bfd4b81eaf228df169f4", + "deployedBytecodeHash": "878366e51dfa79329645837646563469", + "sourceHash": "7ffe14f90465530802dc1f5762e0217f", + "source": "/* contract for tracking locked funds\n\n requirements\n -> lock funds\n -> unlock funds\n -> index locks by address\n\n For flows see: https://github.com/Augmint/augmint-contracts/blob/master/docs/lockFlow.png\n\n TODO / think about:\n -> self-destruct function?\n\n*/\n\npragma solidity ^0.4.23;\n\nimport \"./generic/Restricted.sol\";\nimport \"./generic/SafeMath.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./MonetarySupervisor.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\n\n\ncontract Locker is Restricted, TokenReceiver {\n\n using SafeMath for uint256;\n\n uint public constant CHUNK_SIZE = 100;\n\n event NewLockProduct(uint32 indexed lockProductId, uint32 perTermInterest, uint32 durationInSecs,\n uint32 minimumLockAmount, bool isActive);\n\n event LockProductActiveChange(uint32 indexed lockProductId, bool newActiveState);\n\n // NB: amountLocked includes the original amount, plus interest\n event NewLock(address indexed lockOwner, uint lockId, uint amountLocked, uint interestEarned,\n uint40 lockedUntil, uint32 perTermInterest, uint32 durationInSecs);\n\n event LockReleased(address indexed lockOwner, uint lockId);\n\n event MonetarySupervisorChanged(MonetarySupervisor newMonetarySupervisor);\n\n struct LockProduct {\n // perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n uint32 perTermInterest;\n uint32 durationInSecs;\n uint32 minimumLockAmount;\n bool isActive;\n }\n\n /* NB: we don't need to store lock parameters because lockProducts can't be altered (only disabled/enabled) */\n struct Lock {\n uint amountLocked;\n address owner;\n uint32 productId;\n uint40 lockedUntil;\n bool isActive;\n }\n\n AugmintTokenInterface public augmintToken;\n MonetarySupervisor public monetarySupervisor;\n\n LockProduct[] public lockProducts;\n\n Lock[] public locks;\n\n // lock ids for an account\n mapping(address => uint[]) public accountLocks;\n\n constructor(AugmintTokenInterface _augmintToken, MonetarySupervisor _monetarySupervisor) public {\n\n augmintToken = _augmintToken;\n monetarySupervisor = _monetarySupervisor;\n\n }\n\n function addLockProduct(uint32 perTermInterest, uint32 durationInSecs, uint32 minimumLockAmount, bool isActive)\n external restrict(\"MonetaryBoard\") {\n\n uint _newLockProductId = lockProducts.push(\n LockProduct(perTermInterest, durationInSecs, minimumLockAmount, isActive)) - 1;\n uint32 newLockProductId = uint32(_newLockProductId);\n require(newLockProductId == _newLockProductId, \"lockProduct overflow\");\n emit NewLockProduct(newLockProductId, perTermInterest, durationInSecs, minimumLockAmount, isActive);\n\n }\n\n function setLockProductActiveState(uint32 lockProductId, bool isActive) external restrict(\"MonetaryBoard\") {\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n\n lockProducts[lockProductId].isActive = isActive;\n emit LockProductActiveChange(lockProductId, isActive);\n\n }\n\n /* lock funds, called from AugmintToken's transferAndNotify\n Flow for locking tokens:\n 1) user calls token contract's transferAndNotify lockProductId passed in data arg\n 2) transferAndNotify transfers tokens to the Lock contract\n 3) transferAndNotify calls Lock.transferNotification with lockProductId\n */\n function transferNotification(address from, uint256 amountToLock, uint _lockProductId) external {\n require(msg.sender == address(augmintToken), \"msg.sender must be augmintToken\");\n // next line would revert but require to emit reason:\n require(lockProductId < lockProducts.length, \"invalid lockProductId\");\n uint32 lockProductId = uint32(_lockProductId);\n require(lockProductId == _lockProductId, \"lockProductId overflow\");\n /* TODO: make data arg generic bytes\n uint productId;\n assembly { // solhint-disable-line no-inline-assembly\n productId := mload(data)\n } */\n _createLock(lockProductId, from, amountToLock);\n }\n\n function releaseFunds(uint lockId) external {\n // next line would revert but require to emit reason:\n require(lockId < locks.length, \"invalid lockId\");\n Lock storage lock = locks[lockId];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n require(lock.isActive, \"lock must be in active state\");\n require(now >= lock.lockedUntil, \"current time must be later than lockedUntil\");\n\n lock.isActive = false;\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n monetarySupervisor.releaseFundsNotification(lock.amountLocked); // to maintain totalLockAmount\n augmintToken.transferWithNarrative(lock.owner, lock.amountLocked.add(interestEarned),\n \"Funds released from lock\");\n\n emit LockReleased(lock.owner, lockId);\n }\n\n function setMonetarySupervisor(MonetarySupervisor newMonetarySupervisor) external restrict(\"MonetaryBoard\") {\n monetarySupervisor = newMonetarySupervisor;\n emit MonetarySupervisorChanged(newMonetarySupervisor);\n }\n\n function getLockProductCount() external view returns (uint) {\n\n return lockProducts.length;\n\n }\n\n // returns 20 lock products starting from some offset\n // lock products are encoded as [ perTermInterest, durationInSecs, minimumLockAmount, maxLockAmount, isActive ]\n function getLockProducts(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {\n for (uint8 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= lockProducts.length) { break; }\n\n LockProduct storage lockProduct = lockProducts[offset + i];\n\n response[i] = [ lockProduct.perTermInterest, lockProduct.durationInSecs, lockProduct.minimumLockAmount,\n monetarySupervisor.getMaxLockAmount(lockProduct.minimumLockAmount, lockProduct.perTermInterest),\n lockProduct.isActive ? 1 : 0 ];\n }\n }\n\n function getLockCount() external view returns (uint) {\n return locks.length;\n }\n\n function getLockCountForAddress(address lockOwner) external view returns (uint) {\n return accountLocks[lockOwner].length;\n }\n\n // returns CHUNK_SIZE locks starting from some offset\n // lock products are encoded as\n // [lockId, owner, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n // NB: perTermInterest is in millionths (i.e. 1,000,000 = 100%):\n function getLocks(uint offset) external view returns (uint[8][CHUNK_SIZE] response) {\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locks.length) { break; }\n\n Lock storage lock = locks[offset + i];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [uint(offset + i), uint(lock.owner), lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0];\n }\n }\n\n // returns CHUNK_SIZE locks of a given account, starting from some offset\n // lock products are encoded as\n // [lockId, amountLocked, interestEarned, lockedUntil, perTermInterest, durationInSecs, isActive ]\n function getLocksForAddress(address lockOwner, uint offset) external view returns (uint[7][CHUNK_SIZE] response) {\n\n uint[] storage locksForAddress = accountLocks[lockOwner];\n\n for (uint16 i = 0; i < CHUNK_SIZE; i++) {\n\n if (offset + i >= locksForAddress.length) { break; }\n\n Lock storage lock = locks[locksForAddress[offset + i]];\n LockProduct storage lockProduct = lockProducts[lock.productId];\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, lock.amountLocked);\n\n response[i] = [ locksForAddress[offset + i], lock.amountLocked, interestEarned, lock.lockedUntil,\n lockProduct.perTermInterest, lockProduct.durationInSecs, lock.isActive ? 1 : 0 ];\n }\n }\n\n function calculateInterest(uint32 perTermInterest, uint amountToLock) public pure returns (uint interestEarned) {\n interestEarned = amountToLock.mul(perTermInterest).div(1000000);\n }\n\n // Internal function. assumes amountToLock is already transferred to this Lock contract\n function _createLock(uint32 lockProductId, address lockOwner, uint amountToLock) internal returns(uint lockId) {\n LockProduct storage lockProduct = lockProducts[lockProductId];\n require(lockProduct.isActive, \"lockProduct must be in active state\");\n require(amountToLock >= lockProduct.minimumLockAmount, \"amountToLock must be >= minimumLockAmount\");\n\n uint interestEarned = calculateInterest(lockProduct.perTermInterest, amountToLock);\n uint expiration = now.add(lockProduct.durationInSecs);\n uint40 lockedUntil = uint40(expiration);\n require(lockedUntil == expiration, \"lockedUntil overflow\");\n\n lockId = locks.push(Lock(amountToLock, lockOwner, lockProductId, lockedUntil, true)) - 1;\n accountLocks[lockOwner].push(lockId);\n\n monetarySupervisor.requestInterest(amountToLock, interestEarned); // update KPIs & transfer interest here\n\n emit NewLock(lockOwner, lockId, amountToLock, interestEarned, lockedUntil, lockProduct.perTermInterest,\n lockProduct.durationInSecs);\n }\n\n}\n" } } } diff --git a/abiniser/deployments/4/MonetarySupervisor_DEPLOYS.json b/abiniser/deployments/4/MonetarySupervisor_DEPLOYS.json index 01121587..b1e0b4bf 100644 --- a/abiniser/deployments/4/MonetarySupervisor_DEPLOYS.json +++ b/abiniser/deployments/4/MonetarySupervisor_DEPLOYS.json @@ -32,7 +32,7 @@ } }, "a552ee1f90ae83cb91d07311ae8eab1e": { - "latestDeployedAddress": "0xa00a5d1882c3f690e3d0d975ebe378120b70ae87", + "latestDeployedAddress": "0xc19a45f5cbfa93be512ef07177feb3f7b3ae4518", "deployments": { "0x2e8b07a973f8e136aa39922dff21ad187a6e8e7d": { "generatedAt": "2018-04-25T12:31:29.126Z", @@ -59,6 +59,19 @@ "deployedBytecodeHash": "afc07e949d0fc0522760e21001e6442e", "sourceHash": "cbd67b35e74bbbc9ef5a78c3180eb002", "source": "/* MonetarySupervisor\n - maintains system wide KPIs (eg totalLockAmount, totalLoanAmount)\n - holds system wide parameters/limits\n - enforces system wide limits\n - burns and issues to AugmintReserves\n - Send funds from reserve to exchange when intervening (not implemented yet)\n - Converts older versions of AugmintTokens in 1:1 to new\n\n TODO:\n - Mcreate and use MonetarySupervisorInterface?\n - create and use InterestEarnedAccount interface ?\n\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\nimport \"./InterestEarnedAccount.sol\";\nimport \"./AugmintReserves.sol\";\n\n\ncontract MonetarySupervisor is Restricted, TokenReceiver { // solhint-disable-line no-empty-blocks\n using SafeMath for uint256;\n\n uint public constant PERCENT_100 = 1000000;\n\n AugmintTokenInterface public augmintToken;\n InterestEarnedAccount public interestEarnedAccount;\n AugmintReserves public augmintReserves;\n\n uint public issuedByMonetaryBoard; // supply issued manually by monetary board\n\n uint public totalLoanAmount; // total amount of all loans without interest, in token\n uint public totalLockedAmount; // total amount of all locks without premium, in token\n\n /**********\n Parameters to ensure totalLoanAmount or totalLockedAmount difference is within limits and system also works\n when total loan or lock amounts are low.\n for test calculations: https://docs.google.com/spreadsheets/d/1MeWYPYZRIm1n9lzpvbq8kLfQg1hhvk5oJY6NrR401S0\n **********/\n struct LtdParams {\n uint lockDifferenceLimit; /* only allow a new lock if Loan To Deposit ratio would stay above\n (1 - lockDifferenceLimit) with new lock. Stored as parts per million */\n uint loanDifferenceLimit; /* only allow a new loan if Loan To Deposit ratio would stay above\n (1 + loanDifferenceLimit) with new loan. Stored as parts per million */\n /* allowedDifferenceAmount param is to ensure the system is not \"freezing\" when totalLoanAmount or\n totalLockAmount is low.\n It allows a new loan or lock (up to an amount to reach this difference) even if LTD will go below / above\n lockDifferenceLimit / loanDifferenceLimit with the new lock/loan */\n uint allowedDifferenceAmount;\n }\n\n LtdParams public ltdParams;\n\n /* Previously deployed AugmintTokens which are accepted for conversion (see transferNotification() )\n NB: it's not iterable so old version addresses needs to be added for UI manually after each deploy */\n mapping(address => bool) public acceptedLegacyAugmintTokens;\n\n event LtdParamsChanged(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount);\n\n event AcceptedLegacyAugmintTokenChanged(address augmintTokenAddress, bool newAcceptedState);\n\n event LegacyTokenConverted(address oldTokenAddress, address account, uint amount);\n\n event KPIsAdjusted(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment);\n\n event SystemContractsChanged(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves);\n\n constructor(AugmintTokenInterface _augmintToken, AugmintReserves _augmintReserves,\n InterestEarnedAccount _interestEarnedAccount,\n uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount) public {\n augmintToken = _augmintToken;\n augmintReserves = _augmintReserves;\n interestEarnedAccount = _interestEarnedAccount;\n\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n function issueToReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.add(amount);\n augmintToken.issueTo(augmintReserves, amount);\n }\n\n function burnFromReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.sub(amount);\n augmintReserves.burn(augmintToken, amount);\n }\n\n /* Locker requesting interest when locking funds. Enforcing LTD to stay within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function requestInterest(uint amountToLock, uint interestAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n require(amountToLock <= getMaxLockAmountAllowedByLtd(), \"amountToLock must be <= maxLockAmountAllowedByLtd\");\n\n totalLockedAmount = totalLockedAmount.add(amountToLock);\n // next line would revert but require to emit reason:\n require(augmintToken.balanceOf(address(interestEarnedAccount)) >= interestAmount,\n \"interestEarnedAccount balance must be >= interestAmount\");\n interestEarnedAccount.transferInterest(augmintToken, msg.sender, interestAmount); // transfer interest to Locker\n }\n\n // Locker notifying when releasing funds to update KPIs\n function releaseFundsNotification(uint lockedAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n totalLockedAmount = totalLockedAmount.sub(lockedAmount);\n }\n\n /* Issue loan if LTD stays within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function issueLoan(address borrower, uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n require(loanAmount <= getMaxLoanAmountAllowedByLtd(), \"loanAmount must be <= maxLoanAmountAllowedByLtd\");\n totalLoanAmount = totalLoanAmount.add(loanAmount);\n augmintToken.issueTo(borrower, loanAmount);\n }\n\n function loanRepaymentNotification(uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(loanAmount);\n }\n\n // NB: this is called by Lender contract with the sum of all loans collected in batch\n function loanCollectionNotification(uint totalLoanAmountCollected) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(totalLoanAmountCollected);\n }\n\n function setAcceptedLegacyAugmintToken(address legacyAugmintTokenAddress, bool newAcceptedState)\n external restrict(\"MonetaryBoard\") {\n acceptedLegacyAugmintTokens[legacyAugmintTokenAddress] = newAcceptedState;\n emit AcceptedLegacyAugmintTokenChanged(legacyAugmintTokenAddress, newAcceptedState);\n }\n\n function setLtdParams(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount)\n external restrict(\"MonetaryBoard\") {\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n\n emit LtdParamsChanged(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n /* function to migrate old totalLoanAmount and totalLockedAmount from old monetarySupervisor contract\n when it's upgraded.\n Set new monetarySupervisor contract in all locker and loanManager contracts before executing this */\n function adjustKPIs(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment)\n external restrict(\"MonetaryBoard\") {\n totalLoanAmount = totalLoanAmount.add(totalLoanAmountAdjustment);\n totalLockedAmount = totalLockedAmount.add(totalLockedAmountAdjustment);\n\n emit KPIsAdjusted(totalLoanAmountAdjustment, totalLockedAmountAdjustment);\n }\n\n /* to allow upgrades of InterestEarnedAccount and AugmintReserves contracts. */\n function setSystemContracts(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves)\n external restrict(\"MonetaryBoard\") {\n interestEarnedAccount = newInterestEarnedAccount;\n augmintReserves = newAugmintReserves;\n emit SystemContractsChanged(newInterestEarnedAccount, newAugmintReserves);\n }\n\n /* User can request to convert their tokens from older AugmintToken versions in 1:1\n transferNotification is called from AugmintToken's transferAndNotify\n Flow for converting old tokens:\n 1) user calls old token contract's transferAndNotify with the amount to convert,\n addressing the new MonetarySupervisor Contract\n 2) transferAndNotify transfers user's old tokens to the current MonetarySupervisor contract's address\n 3) transferAndNotify calls MonetarySupervisor.transferNotification\n 4) MonetarySupervisor checks if old AugmintToken is permitted\n 5) MonetarySupervisor issues new tokens to user's account in current AugmintToken\n 6) MonetarySupervisor burns old tokens from own balance\n */\n function transferNotification(address from, uint amount, uint /* data, not used */ ) external {\n AugmintTokenInterface legacyToken = AugmintTokenInterface(msg.sender);\n require(acceptedLegacyAugmintTokens[legacyToken], \"msg.sender must be allowed in acceptedLegacyAugmintTokens\");\n\n legacyToken.burn(amount);\n augmintToken.issueTo(from, amount);\n emit LegacyTokenConverted(msg.sender, from, amount);\n }\n\n function getLoanToDepositRatio() external view returns (uint loanToDepositRatio) {\n loanToDepositRatio = totalLockedAmount == 0 ? 0 : totalLockedAmount.mul(PERCENT_100).div(totalLoanAmount);\n }\n\n /* Helper function for UI.\n Returns max lock amount based on minLockAmount, interestPt, using LTD params & interestEarnedAccount balance */\n function getMaxLockAmount(uint minLockAmount, uint interestPt) external view returns (uint maxLock) {\n uint allowedByEarning = augmintToken.balanceOf(address(interestEarnedAccount)).mul(PERCENT_100).div(interestPt);\n uint allowedByLtd = getMaxLockAmountAllowedByLtd();\n maxLock = allowedByEarning < allowedByLtd ? allowedByEarning : allowedByLtd;\n maxLock = maxLock < minLockAmount ? 0 : maxLock;\n }\n\n /* Helper function for UI.\n Returns max loan amount based on minLoanAmont using LTD params */\n function getMaxLoanAmount(uint minLoanAmount) external view returns (uint maxLoan) {\n uint allowedByLtd = getMaxLoanAmountAllowedByLtd();\n maxLoan = allowedByLtd < minLoanAmount ? 0 : allowedByLtd;\n }\n\n /* returns maximum lockable token amount allowed by LTD params. */\n function getMaxLockAmountAllowedByLtd() public view returns(uint maxLockByLtd) {\n uint allowedByLtdDifferencePt = totalLoanAmount.mul(PERCENT_100).div(PERCENT_100\n .sub(ltdParams.lockDifferenceLimit));\n allowedByLtdDifferencePt = totalLockedAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLockedAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLockedAmount >= totalLoanAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLoanAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLockedAmount);\n\n maxLockByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n /* returns maximum borrowable token amount allowed by LTD params */\n function getMaxLoanAmountAllowedByLtd() public view returns(uint maxLoanByLtd) {\n uint allowedByLtdDifferencePt = totalLockedAmount.mul(ltdParams.loanDifferenceLimit.add(PERCENT_100))\n .div(PERCENT_100);\n allowedByLtdDifferencePt = totalLoanAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLoanAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLoanAmount >= totalLockedAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLockedAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLoanAmount);\n\n maxLoanByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n}\n" + }, + "0xc19a45f5cbfa93be512ef07177feb3f7b3ae4518": { + "generatedAt": "2018-05-12T01:34:38.474Z", + "truffleContractFileUpdatedAt": "2018-05-12T01:30:50.663Z", + "deployTransactionHash": "0x1dc6702b64f4edcba0201b1c4e49c47fbbbbaab7788ed37ee104e6bb3df3b21c", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "83f4ac731042d988dad02af18401f3f3", + "deployedBytecodeHash": "6356e4110fb4cbff5b57908614a63219", + "sourceHash": "cbd67b35e74bbbc9ef5a78c3180eb002", + "source": "/* MonetarySupervisor\n - maintains system wide KPIs (eg totalLockAmount, totalLoanAmount)\n - holds system wide parameters/limits\n - enforces system wide limits\n - burns and issues to AugmintReserves\n - Send funds from reserve to exchange when intervening (not implemented yet)\n - Converts older versions of AugmintTokens in 1:1 to new\n\n TODO:\n - Mcreate and use MonetarySupervisorInterface?\n - create and use InterestEarnedAccount interface ?\n\n*/\n\npragma solidity ^0.4.23;\nimport \"./generic/SafeMath.sol\";\nimport \"./generic/Restricted.sol\";\nimport \"./interfaces/AugmintTokenInterface.sol\";\nimport \"./interfaces/TokenReceiver.sol\";\nimport \"./InterestEarnedAccount.sol\";\nimport \"./AugmintReserves.sol\";\n\n\ncontract MonetarySupervisor is Restricted, TokenReceiver { // solhint-disable-line no-empty-blocks\n using SafeMath for uint256;\n\n uint public constant PERCENT_100 = 1000000;\n\n AugmintTokenInterface public augmintToken;\n InterestEarnedAccount public interestEarnedAccount;\n AugmintReserves public augmintReserves;\n\n uint public issuedByMonetaryBoard; // supply issued manually by monetary board\n\n uint public totalLoanAmount; // total amount of all loans without interest, in token\n uint public totalLockedAmount; // total amount of all locks without premium, in token\n\n /**********\n Parameters to ensure totalLoanAmount or totalLockedAmount difference is within limits and system also works\n when total loan or lock amounts are low.\n for test calculations: https://docs.google.com/spreadsheets/d/1MeWYPYZRIm1n9lzpvbq8kLfQg1hhvk5oJY6NrR401S0\n **********/\n struct LtdParams {\n uint lockDifferenceLimit; /* only allow a new lock if Loan To Deposit ratio would stay above\n (1 - lockDifferenceLimit) with new lock. Stored as parts per million */\n uint loanDifferenceLimit; /* only allow a new loan if Loan To Deposit ratio would stay above\n (1 + loanDifferenceLimit) with new loan. Stored as parts per million */\n /* allowedDifferenceAmount param is to ensure the system is not \"freezing\" when totalLoanAmount or\n totalLockAmount is low.\n It allows a new loan or lock (up to an amount to reach this difference) even if LTD will go below / above\n lockDifferenceLimit / loanDifferenceLimit with the new lock/loan */\n uint allowedDifferenceAmount;\n }\n\n LtdParams public ltdParams;\n\n /* Previously deployed AugmintTokens which are accepted for conversion (see transferNotification() )\n NB: it's not iterable so old version addresses needs to be added for UI manually after each deploy */\n mapping(address => bool) public acceptedLegacyAugmintTokens;\n\n event LtdParamsChanged(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount);\n\n event AcceptedLegacyAugmintTokenChanged(address augmintTokenAddress, bool newAcceptedState);\n\n event LegacyTokenConverted(address oldTokenAddress, address account, uint amount);\n\n event KPIsAdjusted(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment);\n\n event SystemContractsChanged(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves);\n\n constructor(AugmintTokenInterface _augmintToken, AugmintReserves _augmintReserves,\n InterestEarnedAccount _interestEarnedAccount,\n uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount) public {\n augmintToken = _augmintToken;\n augmintReserves = _augmintReserves;\n interestEarnedAccount = _interestEarnedAccount;\n\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n function issueToReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.add(amount);\n augmintToken.issueTo(augmintReserves, amount);\n }\n\n function burnFromReserve(uint amount) external restrict(\"MonetaryBoard\") {\n issuedByMonetaryBoard = issuedByMonetaryBoard.sub(amount);\n augmintReserves.burn(augmintToken, amount);\n }\n\n /* Locker requesting interest when locking funds. Enforcing LTD to stay within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function requestInterest(uint amountToLock, uint interestAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n require(amountToLock <= getMaxLockAmountAllowedByLtd(), \"amountToLock must be <= maxLockAmountAllowedByLtd\");\n\n totalLockedAmount = totalLockedAmount.add(amountToLock);\n // next line would revert but require to emit reason:\n require(augmintToken.balanceOf(address(interestEarnedAccount)) >= interestAmount,\n \"interestEarnedAccount balance must be >= interestAmount\");\n interestEarnedAccount.transferInterest(augmintToken, msg.sender, interestAmount); // transfer interest to Locker\n }\n\n // Locker notifying when releasing funds to update KPIs\n function releaseFundsNotification(uint lockedAmount) external {\n // only whitelisted LockerContracts\n require(permissions[msg.sender][\"LockerContracts\"], \"msg.sender must have LockerContracts permission\");\n totalLockedAmount = totalLockedAmount.sub(lockedAmount);\n }\n\n /* Issue loan if LTD stays within range allowed by LTD params\n NB: it does not know about min loan amount, it's the loan contract's responsibility to enforce it */\n function issueLoan(address borrower, uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n require(loanAmount <= getMaxLoanAmountAllowedByLtd(), \"loanAmount must be <= maxLoanAmountAllowedByLtd\");\n totalLoanAmount = totalLoanAmount.add(loanAmount);\n augmintToken.issueTo(borrower, loanAmount);\n }\n\n function loanRepaymentNotification(uint loanAmount) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(loanAmount);\n }\n\n // NB: this is called by Lender contract with the sum of all loans collected in batch\n function loanCollectionNotification(uint totalLoanAmountCollected) external {\n // only whitelisted LoanManager contracts\n require(permissions[msg.sender][\"LoanManagerContracts\"],\n \"msg.sender must have LoanManagerContracts permission\");\n totalLoanAmount = totalLoanAmount.sub(totalLoanAmountCollected);\n }\n\n function setAcceptedLegacyAugmintToken(address legacyAugmintTokenAddress, bool newAcceptedState)\n external restrict(\"MonetaryBoard\") {\n acceptedLegacyAugmintTokens[legacyAugmintTokenAddress] = newAcceptedState;\n emit AcceptedLegacyAugmintTokenChanged(legacyAugmintTokenAddress, newAcceptedState);\n }\n\n function setLtdParams(uint lockDifferenceLimit, uint loanDifferenceLimit, uint allowedDifferenceAmount)\n external restrict(\"MonetaryBoard\") {\n ltdParams = LtdParams(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n\n emit LtdParamsChanged(lockDifferenceLimit, loanDifferenceLimit, allowedDifferenceAmount);\n }\n\n /* function to migrate old totalLoanAmount and totalLockedAmount from old monetarySupervisor contract\n when it's upgraded.\n Set new monetarySupervisor contract in all locker and loanManager contracts before executing this */\n function adjustKPIs(uint totalLoanAmountAdjustment, uint totalLockedAmountAdjustment)\n external restrict(\"MonetaryBoard\") {\n totalLoanAmount = totalLoanAmount.add(totalLoanAmountAdjustment);\n totalLockedAmount = totalLockedAmount.add(totalLockedAmountAdjustment);\n\n emit KPIsAdjusted(totalLoanAmountAdjustment, totalLockedAmountAdjustment);\n }\n\n /* to allow upgrades of InterestEarnedAccount and AugmintReserves contracts. */\n function setSystemContracts(InterestEarnedAccount newInterestEarnedAccount, AugmintReserves newAugmintReserves)\n external restrict(\"MonetaryBoard\") {\n interestEarnedAccount = newInterestEarnedAccount;\n augmintReserves = newAugmintReserves;\n emit SystemContractsChanged(newInterestEarnedAccount, newAugmintReserves);\n }\n\n /* User can request to convert their tokens from older AugmintToken versions in 1:1\n transferNotification is called from AugmintToken's transferAndNotify\n Flow for converting old tokens:\n 1) user calls old token contract's transferAndNotify with the amount to convert,\n addressing the new MonetarySupervisor Contract\n 2) transferAndNotify transfers user's old tokens to the current MonetarySupervisor contract's address\n 3) transferAndNotify calls MonetarySupervisor.transferNotification\n 4) MonetarySupervisor checks if old AugmintToken is permitted\n 5) MonetarySupervisor issues new tokens to user's account in current AugmintToken\n 6) MonetarySupervisor burns old tokens from own balance\n */\n function transferNotification(address from, uint amount, uint /* data, not used */ ) external {\n AugmintTokenInterface legacyToken = AugmintTokenInterface(msg.sender);\n require(acceptedLegacyAugmintTokens[legacyToken], \"msg.sender must be allowed in acceptedLegacyAugmintTokens\");\n\n legacyToken.burn(amount);\n augmintToken.issueTo(from, amount);\n emit LegacyTokenConverted(msg.sender, from, amount);\n }\n\n function getLoanToDepositRatio() external view returns (uint loanToDepositRatio) {\n loanToDepositRatio = totalLockedAmount == 0 ? 0 : totalLockedAmount.mul(PERCENT_100).div(totalLoanAmount);\n }\n\n /* Helper function for UI.\n Returns max lock amount based on minLockAmount, interestPt, using LTD params & interestEarnedAccount balance */\n function getMaxLockAmount(uint minLockAmount, uint interestPt) external view returns (uint maxLock) {\n uint allowedByEarning = augmintToken.balanceOf(address(interestEarnedAccount)).mul(PERCENT_100).div(interestPt);\n uint allowedByLtd = getMaxLockAmountAllowedByLtd();\n maxLock = allowedByEarning < allowedByLtd ? allowedByEarning : allowedByLtd;\n maxLock = maxLock < minLockAmount ? 0 : maxLock;\n }\n\n /* Helper function for UI.\n Returns max loan amount based on minLoanAmont using LTD params */\n function getMaxLoanAmount(uint minLoanAmount) external view returns (uint maxLoan) {\n uint allowedByLtd = getMaxLoanAmountAllowedByLtd();\n maxLoan = allowedByLtd < minLoanAmount ? 0 : allowedByLtd;\n }\n\n /* returns maximum lockable token amount allowed by LTD params. */\n function getMaxLockAmountAllowedByLtd() public view returns(uint maxLockByLtd) {\n uint allowedByLtdDifferencePt = totalLoanAmount.mul(PERCENT_100).div(PERCENT_100\n .sub(ltdParams.lockDifferenceLimit));\n allowedByLtdDifferencePt = totalLockedAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLockedAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLockedAmount >= totalLoanAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLoanAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLockedAmount);\n\n maxLockByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n /* returns maximum borrowable token amount allowed by LTD params */\n function getMaxLoanAmountAllowedByLtd() public view returns(uint maxLoanByLtd) {\n uint allowedByLtdDifferencePt = totalLockedAmount.mul(ltdParams.loanDifferenceLimit.add(PERCENT_100))\n .div(PERCENT_100);\n allowedByLtdDifferencePt = totalLoanAmount >= allowedByLtdDifferencePt ?\n 0 : allowedByLtdDifferencePt.sub(totalLoanAmount);\n\n uint allowedByLtdDifferenceAmount =\n totalLoanAmount >= totalLockedAmount.add(ltdParams.allowedDifferenceAmount) ?\n 0 : totalLockedAmount.add(ltdParams.allowedDifferenceAmount).sub(totalLoanAmount);\n\n maxLoanByLtd = allowedByLtdDifferencePt > allowedByLtdDifferenceAmount ?\n allowedByLtdDifferencePt : allowedByLtdDifferenceAmount;\n }\n\n}\n" } } } diff --git a/abiniser/deployments/4/TokenAEur_DEPLOYS.json b/abiniser/deployments/4/TokenAEur_DEPLOYS.json index 32740eee..e71ad477 100644 --- a/abiniser/deployments/4/TokenAEur_DEPLOYS.json +++ b/abiniser/deployments/4/TokenAEur_DEPLOYS.json @@ -1,6 +1,6 @@ { "contractName": "TokenAEur", - "latestAbiHash": "d7dd02520f2d92b2ca237f066cf2488d", + "latestAbiHash": "4b49e7e6d1a9a2de81a4d2d088acbc04", "deployedAbis": { "27721a2c77dc40da7639abd46791c3d7": { "latestDeployedAddress": "0x95aa79d7410eb60f49bfd570b445836d402bd7b1", @@ -53,6 +53,24 @@ "source": "/* Augmint Crypto Euro token (ACE) implementation */\npragma solidity ^0.4.23;\nimport \"./interfaces/TransferFeeInterface.sol\";\nimport \"./generic/AugmintToken.sol\";\n\n\ncontract TokenAEur is AugmintToken {\n constructor(TransferFeeInterface _feeAccount)\n public AugmintToken(\"Augmint Crypto Euro\", \"AEUR\", \"EUR\", 2, _feeAccount)\n {} // solhint-disable-line no-empty-blocks\n\n}\n" } } + }, + "4b49e7e6d1a9a2de81a4d2d088acbc04": { + "latestDeployedAddress": "0x6c90c10d7a33815c2baeed66ee8b848f1d95268e", + "deployments": { + "0x6c90c10d7a33815c2baeed66ee8b848f1d95268e": { + "generatedAt": "2018-05-12T01:34:38.456Z", + "truffleContractFileUpdatedAt": "2018-05-12T01:30:50.031Z", + "deployTransactionHash": "0xf45e9817fca468b5438d7a00a6461fd47e14cf04f1bb4b36ccc7302284ec39c5", + "compiler": { + "name": "solc", + "version": "0.4.23+commit.124ca40d.Emscripten.clang" + }, + "bytecodeHash": "042675fc70cfed8762b1e7de8d15799f", + "deployedBytecodeHash": "03b0c0d8c49d83af34516ac82bc0b2a9", + "sourceHash": "61dded7836f4431e621382340f32670c", + "source": "/* Augmint Crypto Euro token (ACE) implementation */\npragma solidity ^0.4.23;\nimport \"./interfaces/TransferFeeInterface.sol\";\nimport \"./generic/AugmintToken.sol\";\n\n\ncontract TokenAEur is AugmintToken {\n constructor(TransferFeeInterface _feeAccount)\n public AugmintToken(\"Augmint Crypto Euro\", \"AEUR\", \"EUR\", 2, _feeAccount)\n {} // solhint-disable-line no-empty-blocks\n\n}\n" + } + } } } } \ No newline at end of file diff --git a/rinkeby_migrations/17_new_TokenAEur.js b/rinkeby_migrations/17_new_TokenAEur.js new file mode 100644 index 00000000..681abf26 --- /dev/null +++ b/rinkeby_migrations/17_new_TokenAEur.js @@ -0,0 +1,181 @@ +/* deploy new TokenAEur version and all dependent contracts: + - MonetarySupervisor (and switch over to it ) + - Locker + - LoanManager + - Exchange +*/ +const Migrations = artifacts.require("./Migrations.sol"); +const TokenAEur = artifacts.require("./TokenAEur.sol"); +const FeeAccount = artifacts.require("./FeeAccount.sol"); +const Exchange = artifacts.require("./Exchange.sol"); +const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); +const InterestEarnedAccount = artifacts.require("./InterestEarnedAccount.sol"); +const AugmintReserves = artifacts.require("./AugmintReserves.sol"); +const Locker = artifacts.require("./Locker.sol"); +const LoanManager = artifacts.require("./LoanManager.sol"); + +module.exports = function(deployer, network, accounts) { + const ratesAddress = "0xcA8100FCcb479516A5b30f8Bc5dAeA09Fb7a7473"; + const feeAccount = FeeAccount.at("0xc26667132b0B798ab87864f7c29c0819c887aADB"); + const augmintReserves = AugmintReserves.at("0xc70b65e40f877cdC6d8D2ebFd44d63EfBeb7fc6D"); + const interestEarnedAccount = InterestEarnedAccount.at("0x3a414d7636defb9d3dfb7342984fe3f7b5125df6"); + const oldMonetarySupervisor = MonetarySupervisor.at("0xa00a5d1882C3F690E3d0D975ebE378120b70ae87"); + + const oldToken1 = TokenAEur.at("0x95aa79d7410eb60f49bfd570b445836d402bd7b1"); + const oldToken2 = TokenAEur.at("0xa35d9de06895a3a2e7ecae26654b88fe71c179ea"); + const oldToken3 = TokenAEur.at("0x135893F1A6B3037BB45182841f18F69327366992"); + + const oldLocker1 = Locker.at("0xf98AE1fb568B267A7632BF54579A153C892E2ec2"); + const oldLoanManager1 = LoanManager.at("0xBdb02f82d7Ad574f9F549895caf41E23a8981b07"); + + deployer.deploy(TokenAEur, feeAccount.address); + + deployer.then(async () => { + /************************************************************* + * Deploy new TokenAEur + **************************************************************/ + + const newTokenAEur = TokenAEur.at(TokenAEur.address); + + /************************************************************* + * Deploy new Exchange + **************************************************************/ + await deployer.deploy(Exchange, newTokenAEur.address, ratesAddress); + const newExchange = Exchange.at(Exchange.address); + + await feeAccount.grantPermission(newExchange.address, "NoFeeTransferContracts"); + + /************************************************************* + * Deploy new MonetarySupervisor + **************************************************************/ + await deployer.deploy( + MonetarySupervisor, + newTokenAEur.address, + augmintReserves.address, + interestEarnedAccount.address, + 200000 /* ltdLockDifferenceLimit */, + 200000 /* ltdLoanDifferenceLimit*/, + 50000 /* allowedLtdDifferenceAmount */ + ); + + const newMonetarySupervisor = MonetarySupervisor.at(MonetarySupervisor.address); + + /************************************************************* + * Deploy new Locker, setup lock products and permissions + **************************************************************/ + await deployer.deploy(Locker, TokenAEur.address, MonetarySupervisor.address); + const newLocker = Locker.at(Locker.address); + console.log(" Adding test lockProducts."); + await Promise.all([ + feeAccount.grantPermission(newLocker.address, "NoFeeTransferContracts"), + newMonetarySupervisor.grantPermission(newLocker.address, "LockerContracts"), + + // (perTermInterest, durationInSecs, minimumLockAmount, isActive) + newLocker.addLockProduct(14472, 7776000, 1000, false), // 90 days 6% p.a. + newLocker.addLockProduct(4019, 2592000, 1000, true), // 30 days, 5% p.a. + newLocker.addLockProduct(1506, 1209600, 1000, true), // 14 days, 4% p.a. + newLocker.addLockProduct(568, 604800, 1000, true), // 7 days, 3% p.a. + + newLocker.addLockProduct(3, 3600, 2000, true), // 60 minutes for testing, ~2.66% p.a. + newLocker.addLockProduct(1, 60, 3000, true) // 1 minute for testing, ~69.15% p.a. + ]); + + /************************************************************* + * Deploy new LoanManager, setup loan products and permissions + **************************************************************/ + await deployer.deploy(LoanManager, newTokenAEur.address, newMonetarySupervisor.address, ratesAddress); + const newLoanManager = LoanManager.at(LoanManager.address); + console.log(" Adding test loanProducts."); + await Promise.all([ + feeAccount.grantPermission(newLoanManager.address, "NoFeeTransferContracts"), + newMonetarySupervisor.grantPermission(newLoanManager.address, "LoanManagerContracts"), + + // term (in sec), discountRate, loanCoverageRatio, minDisbursedAmount (w/ 4 decimals), defaultingFeePt, isActive + newLoanManager.addLoanProduct(7776000, 971661, 600000, 1000, 50000, false), // 90d, 12%. p.a. + newLoanManager.addLoanProduct(2592000, 990641, 600000, 1000, 50000, true), // 30d, 12% p.a. + newLoanManager.addLoanProduct(1209600, 996337, 600000, 1000, 50000, true), // 14d, 10% p.a. + newLoanManager.addLoanProduct(604800, 998170, 600000, 1000, 50000, true), // 7d, 10% p.a. + + newLoanManager.addLoanProduct(3600, 999989, 980000, 2000, 50000, true), // due in 1hr for testing repayments ? p.a. + newLoanManager.addLoanProduct(1, 999999, 990000, 3000, 50000, true) // defaults in 1 secs for testing ? p.a. + ]); + + /************************************************************* + * Grant MonetaryBoard permissions to accounts on new contracts + **************************************************************/ + console.log(" Granting MonetaryBoard permissions on new contracts "); + const monetaryBoardAccounts = [ + accounts[0], + "0x14A9dc091053fCbA9474c5734078238ff9904364" /* Krosza */, + "0xe71E9636e31B838aF0A3c38B3f3449cdC2b7aa87" /* Phraktle */ + ]; + const grantMonetaryBoardTxs = monetaryBoardAccounts.map(acc => [ + newExchange.grantPermission(acc, "MonetaryBoard"), + newLocker.grantPermission(acc, "MonetaryBoard"), + newTokenAEur.grantPermission(acc, "MonetaryBoard"), + newLoanManager.grantPermission(acc, "MonetaryBoard"), + newMonetarySupervisor.grantPermission(acc, "MonetaryBoard") + ]); + Promise.all(grantMonetaryBoardTxs); + + /************************************************************* + * Switch new MonetarySupervisor to live + **************************************************************/ + console.log(" granting permissions for new MS"); + await Promise.all([ + interestEarnedAccount.grantPermission(newMonetarySupervisor.address, "MonetarySupervisorContract"), + newTokenAEur.grantPermission(newMonetarySupervisor.address, "MonetarySupervisorContract"), + augmintReserves.grantPermission(newMonetarySupervisor.address, "MonetarySupervisorContract"), + feeAccount.grantPermission(newMonetarySupervisor.address, "NoFeeTransferContracts"), + + newMonetarySupervisor.grantPermission(newLocker.address, "LockerContracts"), + newMonetarySupervisor.grantPermission(newLoanManager.address, "LoanManagerContracts"), + newMonetarySupervisor.grantPermission(oldLocker1.address, "LockerContracts"), + newMonetarySupervisor.grantPermission(oldLoanManager1.address, "LoanManagerContracts"), + + newMonetarySupervisor.setAcceptedLegacyAugmintToken(oldToken1.address, true), + newMonetarySupervisor.setAcceptedLegacyAugmintToken(oldToken2.address, true), + newMonetarySupervisor.setAcceptedLegacyAugmintToken(oldToken3.address, true), + + // to allow token conversion w/o fee + // NB: NoFeeTransferContracts was set on the token contract in legacy token version. + // For newer versions this permission needs to be set on feeAccount + oldToken1.grantPermission(newMonetarySupervisor.address, "NoFeeTransferContracts"), + oldToken2.grantPermission(newMonetarySupervisor.address, "NoFeeTransferContracts"), + feeAccount.grantPermission(oldToken3.address, "NoFeeTransferContracts") + ]); + + console.log(" switching new MS to live"); + await Promise.all([ + oldLocker1.setMonetarySupervisor(newMonetarySupervisor.address), + oldLoanManager1.setSystemContracts(ratesAddress, newMonetarySupervisor.address) + ]); + + // 3. migrate totals from previous MS (using MS.adjustKPIs) + const [oldTotalLoan, oldTotalLock] = await Promise.all([ + oldMonetarySupervisor.totalLoanAmount(), + oldMonetarySupervisor.totalLockedAmount() + ]); + console.log( + "Migrating KPIs to new MonetarySupervisor contract. totalLoanAmount:", + oldTotalLoan.toString(), + "totalLockedAmount", + oldTotalLock.toString() + ); + await newMonetarySupervisor.adjustKPIs(oldTotalLoan, oldTotalLock); + + console.log("Revoking permission from old MS"); + await Promise.all([ + feeAccount.revokePermission(oldMonetarySupervisor.address, "NoFeeTransferContracts"), + interestEarnedAccount.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), + oldToken3.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), + augmintReserves.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), + + oldMonetarySupervisor.revokePermission(oldLocker1.address, "LockerContracts"), + oldMonetarySupervisor.revokePermission(oldLoanManager1.address, "LoanManagerContracts") + ]); + + console.log(" Done with all migration steps. Updating truffle Migrations step manually"); + await Migrations.at("0xb96f7e79a6b3faf4162e274ff764ca9de598b0c5").setCompleted(17); + }); +}; From b5f6106d4d9d1c849c53faa4aca22c94a72356a8 Mon Sep 17 00:00:00 2001 From: szerintedmi Date: Fri, 11 May 2018 22:29:30 -0400 Subject: [PATCH 26/26] update scripts how they should have been ran --- rinkeby_migrations/17_new_TokenAEur.js | 34 +---------- .../18_set_new_MonetarySupervisor_live.js | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 rinkeby_migrations/18_set_new_MonetarySupervisor_live.js diff --git a/rinkeby_migrations/17_new_TokenAEur.js b/rinkeby_migrations/17_new_TokenAEur.js index 681abf26..db8aa375 100644 --- a/rinkeby_migrations/17_new_TokenAEur.js +++ b/rinkeby_migrations/17_new_TokenAEur.js @@ -19,7 +19,6 @@ module.exports = function(deployer, network, accounts) { const feeAccount = FeeAccount.at("0xc26667132b0B798ab87864f7c29c0819c887aADB"); const augmintReserves = AugmintReserves.at("0xc70b65e40f877cdC6d8D2ebFd44d63EfBeb7fc6D"); const interestEarnedAccount = InterestEarnedAccount.at("0x3a414d7636defb9d3dfb7342984fe3f7b5125df6"); - const oldMonetarySupervisor = MonetarySupervisor.at("0xa00a5d1882C3F690E3d0D975ebE378120b70ae87"); const oldToken1 = TokenAEur.at("0x95aa79d7410eb60f49bfd570b445836d402bd7b1"); const oldToken2 = TokenAEur.at("0xa35d9de06895a3a2e7ecae26654b88fe71c179ea"); @@ -119,7 +118,7 @@ module.exports = function(deployer, network, accounts) { Promise.all(grantMonetaryBoardTxs); /************************************************************* - * Switch new MonetarySupervisor to live + * Grant permissions to new MonetarySupervisor **************************************************************/ console.log(" granting permissions for new MS"); await Promise.all([ @@ -145,36 +144,7 @@ module.exports = function(deployer, network, accounts) { feeAccount.grantPermission(oldToken3.address, "NoFeeTransferContracts") ]); - console.log(" switching new MS to live"); - await Promise.all([ - oldLocker1.setMonetarySupervisor(newMonetarySupervisor.address), - oldLoanManager1.setSystemContracts(ratesAddress, newMonetarySupervisor.address) - ]); - - // 3. migrate totals from previous MS (using MS.adjustKPIs) - const [oldTotalLoan, oldTotalLock] = await Promise.all([ - oldMonetarySupervisor.totalLoanAmount(), - oldMonetarySupervisor.totalLockedAmount() - ]); - console.log( - "Migrating KPIs to new MonetarySupervisor contract. totalLoanAmount:", - oldTotalLoan.toString(), - "totalLockedAmount", - oldTotalLock.toString() - ); - await newMonetarySupervisor.adjustKPIs(oldTotalLoan, oldTotalLock); - - console.log("Revoking permission from old MS"); - await Promise.all([ - feeAccount.revokePermission(oldMonetarySupervisor.address, "NoFeeTransferContracts"), - interestEarnedAccount.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), - oldToken3.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), - augmintReserves.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), - - oldMonetarySupervisor.revokePermission(oldLocker1.address, "LockerContracts"), - oldMonetarySupervisor.revokePermission(oldLoanManager1.address, "LoanManagerContracts") - ]); - + // NB: don't forget to top up earned interest account with new tokens console.log(" Done with all migration steps. Updating truffle Migrations step manually"); await Migrations.at("0xb96f7e79a6b3faf4162e274ff764ca9de598b0c5").setCompleted(17); }); diff --git a/rinkeby_migrations/18_set_new_MonetarySupervisor_live.js b/rinkeby_migrations/18_set_new_MonetarySupervisor_live.js new file mode 100644 index 00000000..709ef6c3 --- /dev/null +++ b/rinkeby_migrations/18_set_new_MonetarySupervisor_live.js @@ -0,0 +1,58 @@ +/* switch over to new MonetSupervisor version in legacy contracts */ +const Migrations = artifacts.require("./Migrations.sol"); +const TokenAEur = artifacts.require("./TokenAEur.sol"); +const FeeAccount = artifacts.require("./FeeAccount.sol"); +const MonetarySupervisor = artifacts.require("./MonetarySupervisor.sol"); +const InterestEarnedAccount = artifacts.require("./InterestEarnedAccount.sol"); +const AugmintReserves = artifacts.require("./AugmintReserves.sol"); +const Locker = artifacts.require("./Locker.sol"); +const LoanManager = artifacts.require("./LoanManager.sol"); + +module.exports = function(deployer, network, accounts) { + const ratesAddress = "0xcA8100FCcb479516A5b30f8Bc5dAeA09Fb7a7473"; + const feeAccount = FeeAccount.at("0xc26667132b0B798ab87864f7c29c0819c887aADB"); + const augmintReserves = AugmintReserves.at("0xc70b65e40f877cdC6d8D2ebFd44d63EfBeb7fc6D"); + const interestEarnedAccount = InterestEarnedAccount.at("0x3a414d7636defb9d3dfb7342984fe3f7b5125df6"); + const oldMonetarySupervisor = MonetarySupervisor.at("0xa00a5d1882C3F690E3d0D975ebE378120b70ae87"); + const newMonetarySupervisor = MonetarySupervisor.at("0xC19a45F5CbfA93Be512ef07177feB3f7b3ae4518"); + + const oldToken3 = TokenAEur.at("0x135893F1A6B3037BB45182841f18F69327366992"); + + const oldLocker1 = Locker.at("0xf98AE1fb568B267A7632BF54579A153C892E2ec2"); + const oldLoanManager1 = LoanManager.at("0xBdb02f82d7Ad574f9F549895caf41E23a8981b07"); + + deployer.then(async () => { + console.log(" switching new MS to live"); + await Promise.all([ + oldLocker1.setMonetarySupervisor(newMonetarySupervisor.address), + oldLoanManager1.setSystemContracts(ratesAddress, newMonetarySupervisor.address) + ]); + + // 3. migrate totals from previous MS (using MS.adjustKPIs) + const [oldTotalLoan, oldTotalLock] = await Promise.all([ + oldMonetarySupervisor.totalLoanAmount(), + oldMonetarySupervisor.totalLockedAmount() + ]); + console.log( + "Migrating KPIs to new MonetarySupervisor contract. totalLoanAmount:", + oldTotalLoan.toString(), + "totalLockedAmount", + oldTotalLock.toString() + ); + await newMonetarySupervisor.adjustKPIs(oldTotalLoan, oldTotalLock); + + console.log("Revoking permission from old MS"); + await Promise.all([ + feeAccount.revokePermission(oldMonetarySupervisor.address, "NoFeeTransferContracts"), + interestEarnedAccount.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), + oldToken3.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), + augmintReserves.revokePermission(oldMonetarySupervisor.address, "MonetarySupervisorContract"), + + oldMonetarySupervisor.revokePermission(oldLocker1.address, "LockerContracts"), + oldMonetarySupervisor.revokePermission(oldLoanManager1.address, "LoanManagerContracts") + ]); + + console.log(" Done with all migration steps. Updating truffle Migrations step manually"); + await Migrations.at("0xb96f7e79a6b3faf4162e274ff764ca9de598b0c5").setCompleted(18); + }); +};