From 89e03836137b6bf19c46b395b95e1dcaf3958408 Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Wed, 30 Oct 2019 16:24:15 -0300 Subject: [PATCH 01/10] Handle same token convertino on SafeTokenConverter --- contracts/utils/SafeTokenConverter.sol | 63 ++++++++++++++++---------- test/diaspore/TestCollateralAlt.js | 4 +- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/contracts/utils/SafeTokenConverter.sol b/contracts/utils/SafeTokenConverter.sol index c20ce715..72a585a2 100644 --- a/contracts/utils/SafeTokenConverter.sol +++ b/contracts/utils/SafeTokenConverter.sol @@ -18,18 +18,23 @@ library SafeTokenConverter { uint256 _fromAmount, uint256 _minReceive ) internal returns (uint256 received) { - require(_fromToken.safeApprove(address(_converter), _fromAmount), "error approving converter"); - uint256 prevToBalance = _toToken.balanceOf(address(this)); - - _converter.convertFrom( - _fromToken, - _toToken, - _fromAmount, - _minReceive - ); - - require(_fromToken.clearApprove(address(_converter)), "error clearing approve"); - received = _toToken.balanceOf(address(this)).sub(prevToBalance); + if (address(_fromToken) == address(_toToken)) { + received = _fromAmount; + } else { + require(_fromToken.safeApprove(address(_converter), _fromAmount), "error approving converter"); + uint256 prevToBalance = _toToken.balanceOf(address(this)); + + _converter.convertFrom( + _fromToken, + _toToken, + _fromAmount, + _minReceive + ); + + require(_fromToken.clearApprove(address(_converter)), "error clearing approve"); + received = _toToken.balanceOf(address(this)).sub(prevToBalance); + } + require(received >= _minReceive, "_minReceived not reached"); } @@ -40,22 +45,27 @@ library SafeTokenConverter { uint256 _toAmount, uint256 _maxSpend ) internal returns (uint256 spend) { - require(_fromToken.safeApprove(address(_converter), _maxSpend), "error approving converter"); + if (address(_fromToken) == address(_toToken)) { + spend = _toAmount; + } else { + require(_fromToken.safeApprove(address(_converter), _maxSpend), "error approving converter"); - uint256 prevFromBalance = _fromToken.balanceOf(address(this)); - uint256 prevToBalance = _toToken.balanceOf(address(this)); + uint256 prevFromBalance = _fromToken.balanceOf(address(this)); + uint256 prevToBalance = _toToken.balanceOf(address(this)); - _converter.convertTo( - _fromToken, - _toToken, - _toAmount, - _maxSpend - ); + _converter.convertTo( + _fromToken, + _toToken, + _toAmount, + _maxSpend + ); + + require(_fromToken.clearApprove(address(_converter)), "error clearing approve"); + spend = prevFromBalance.sub(_fromToken.balanceOf(address(this))); + require(_toToken.balanceOf(address(this)).sub(prevToBalance) >= _toAmount, "_toAmount not received"); + } - require(_fromToken.clearApprove(address(_converter)), "error clearing approve"); - spend = prevFromBalance.sub(_fromToken.balanceOf(address(this))); require(spend <= _maxSpend, "_maxSpend exceeded"); - require(_toToken.balanceOf(address(this)).sub(prevToBalance) >= _toAmount, "_toAmount not received"); } function safeConvertToMax( @@ -67,6 +77,11 @@ library SafeTokenConverter { ) internal returns (uint256 received, uint256 spend) { uint256 maxReceive = _converter.getPriceConvertFrom(_fromToken, _toToken, _maxSpend); + if (address(_fromToken) == address(_toToken)) { + uint256 min = _maxSpend < _toAmount ? _maxSpend : _toAmount; + return (min, min); + } + if (maxReceive < _toAmount) { spend = _maxSpend; received = _converter.safeConvertFrom( diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index b95b5004..15458ff9 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -1648,8 +1648,8 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a ); // Pay debt using RCN collateral - await collateral.payOffDebt(entryId, [], { from: user }); - expect(await loanManager.getStatus(debtId)).to.eq.BN(b(3)); + // await collateral.payOffDebt(entryId, [], { from: user }); + // expect(await loanManager.getStatus(debtId)).to.eq.BN(b(3)); }); }); }); From 1890229dcaf2f9b40817f48166404f722833afa4 Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Wed, 30 Oct 2019 16:53:02 -0300 Subject: [PATCH 02/10] Fix safeConvertToMax fails on same token and converter 0 --- contracts/utils/SafeTokenConverter.sol | 4 ++-- test/diaspore/TestCollateralAlt.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/utils/SafeTokenConverter.sol b/contracts/utils/SafeTokenConverter.sol index 72a585a2..d339ee05 100644 --- a/contracts/utils/SafeTokenConverter.sol +++ b/contracts/utils/SafeTokenConverter.sol @@ -75,13 +75,13 @@ library SafeTokenConverter { uint256 _toAmount, uint256 _maxSpend ) internal returns (uint256 received, uint256 spend) { - uint256 maxReceive = _converter.getPriceConvertFrom(_fromToken, _toToken, _maxSpend); - if (address(_fromToken) == address(_toToken)) { uint256 min = _maxSpend < _toAmount ? _maxSpend : _toAmount; return (min, min); } + uint256 maxReceive = _converter.getPriceConvertFrom(_fromToken, _toToken, _maxSpend); + if (maxReceive < _toAmount) { spend = _maxSpend; received = _converter.safeConvertFrom( diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index 15458ff9..7c18ab44 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -1648,8 +1648,8 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a ); // Pay debt using RCN collateral - // await collateral.payOffDebt(entryId, [], { from: user }); - // expect(await loanManager.getStatus(debtId)).to.eq.BN(b(3)); + await collateral.payOffDebt(entryId, [], { from: user }); + expect(await loanManager.getStatus(debtId)).to.eq.BN(b(2)); }); }); }); From c4f9ccdd54d9c679460b2d13115ac9435e889a8a Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Wed, 30 Oct 2019 16:58:04 -0300 Subject: [PATCH 03/10] Check values on payOffDebt test --- test/diaspore/TestCollateralAlt.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index 7c18ab44..6afdc086 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -1593,7 +1593,7 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a it('Should pay debt using rcn collateral, without oracle', async () => { // Request a loan const modelData = await model.encodeData( - b(1000), + b(1100), MAX_UINT64 ); @@ -1650,6 +1650,11 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a // Pay debt using RCN collateral await collateral.payOffDebt(entryId, [], { from: user }); expect(await loanManager.getStatus(debtId)).to.eq.BN(b(2)); + + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1100)); + expect(await rcn.balanceOf(collateral.address)).to.eq.BN(b(1400)); + const entry = await collateral.entries(b(1)); + expect(entry.amount).to.eq.BN(b(1400)); }); }); }); From 81cca5193376295a7012b602616d81bf96b128fe Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Wed, 30 Oct 2019 18:19:54 -0300 Subject: [PATCH 04/10] Add payOffDebt tests with converter --- contracts/test_utils/diaspore/TestModel.sol | 6 + test/diaspore/TestCollateralAlt.js | 312 +++++++++++++++++++- 2 files changed, 317 insertions(+), 1 deletion(-) diff --git a/contracts/test_utils/diaspore/TestModel.sol b/contracts/test_utils/diaspore/TestModel.sol index a2835f24..98576310 100644 --- a/contracts/test_utils/diaspore/TestModel.sol +++ b/contracts/test_utils/diaspore/TestModel.sol @@ -42,6 +42,7 @@ contract TestModel is ERC165, BytesUtils, Ownable { event SetEngine(address _engine); event SetErrorFlag(bytes32 _id, uint256 _flag); event SetGlobalErrorFlag(uint256 _flag); + event SetTotal(bytes32 _id, uint256 _total); mapping(bytes4 => bool) private _supportedInterface; @@ -84,6 +85,11 @@ contract TestModel is ERC165, BytesUtils, Ownable { emit SetErrorFlag(_id, _flag); } + function setTotal(bytes32 _id, uint128 _total) external onlyOwner { + registry[_id].total = _total; + emit SetTotal(_id, _total); + } + function setEngine(address _engine) external onlyOwner { engine = _engine; emit SetEngine(_engine); diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index 6afdc086..ed793cc6 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -4,6 +4,7 @@ const LoanManager = artifacts.require('LoanManager'); const DebtEngine = artifacts.require('DebtEngine'); const TestToken = artifacts.require('TestToken'); const TestRateOracle = artifacts.require('TestRateOracle'); +const TestConverter = artifacts.require('TestConverter'); const { tryCatchRevert, address0x, toBytes32 } = require('../Helper.js'); const BN = web3.utils.BN; @@ -24,6 +25,7 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a let loanManager; let model; let collateral; + let converter; beforeEach(async () => { rcn = await TestToken.new({ from: owner }); @@ -33,7 +35,8 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a await model.setEngine(debtEngine.address, { from: owner }); // Collateral deploy collateral = await Collateral.new(loanManager.address, { from: owner }); - // await collateral.setConverter(converter.address, { from: owner }); + converter = await TestConverter.new({ from: owner }); + await collateral.setConverter(converter.address, { from: owner }); }); describe('Request collateral', () => { @@ -1656,5 +1659,312 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a const entry = await collateral.entries(b(1)); expect(entry.amount).to.eq.BN(b(1400)); }); + it('Should pay debt using rcn collateral, with loan oracle', async () => { + // Request a loan + const modelData = await model.encodeData( + b(1100), + MAX_UINT64 + ); + + // Create loan oracle + const loanOracle = await TestRateOracle.new(); + await loanOracle.setEquivalent(b('4000000000000000000')); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + loanOracle.address, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await rcn.setBalance(user, b(2500)); + await rcn.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + address0x, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(250)); + await rcn.approve(loanManager.address, b(250), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Pay debt using RCN collateral + await collateral.payOffDebt(entryId, [], { from: user }); + expect(await loanManager.getStatus(debtId)).to.eq.BN(b(2)); + + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(275)); + expect(await rcn.balanceOf(collateral.address)).to.eq.BN(b(2225)); + const entry = await collateral.entries(b(1)); + expect(entry.amount).to.eq.BN(b(2225)); + }); + it('Should pay debt using token collateral, without loan oracle', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1100), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + address0x, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Pay debt using RCN collateral + await collateral.payOffDebt(entryId, [], { from: user }); + expect(await loanManager.getStatus(debtId)).to.eq.BN(b(2)); + + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1100)); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(1950)); + const entry = await collateral.entries(b(1)); + expect(entry.amount).to.eq.BN(b(1950)); + }); + it('Should pay debt using token collateral, without loan oracle, using all the collateral', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1100), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + address0x, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Increase the debt a lot + await model.setTotal(debtId, b(100000), { from: owner }); + + // Pay debt using RCN collateral + await collateral.payOffDebt(entryId, [], { from: user }); + expect(await loanManager.getStatus(debtId)).to.eq.BN(b(1)); + + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(5000)); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(0)); + const entry = await collateral.entries(b(1)); + expect(entry.amount).to.eq.BN(b(0)); + }); + }); + describe('Fail payoff debt', () => { + it('Should fail payoff debt if converter rate is off', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2500000000000000000')); + await converter.setRate(rcn.address, dai.address, b('600000000000000000')); + await collateral.setMaxSpreadRatio(dai.address, b(10000), { from: owner }); + + // Request a loan + const modelData = await model.encodeData( + b(1100), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + address0x, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Pay debt using RCN collateral + await tryCatchRevert( + collateral.payOffDebt(entryId, [], { from: user }), + 'converter return below minimun required' + ); + }); }); }); From f3872a32ec945ba34a2d10c8658dafa3c401c8c3 Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Mon, 4 Nov 2019 15:09:09 +0100 Subject: [PATCH 05/10] Add tests payOffDebt using all collateral with converter --- test/diaspore/TestCollateralAlt.js | 167 +++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index ed793cc6..e1192d82 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -1807,6 +1807,88 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a const entry = await collateral.entries(b(1)); expect(entry.amount).to.eq.BN(b(1950)); }); + it('Should pay debt using token collateral, with loan oracle', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // Create loan oracle + const loanOracle = await TestRateOracle.new(); + await loanOracle.setEquivalent(b('4000000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1100), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + loanOracle.address, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Pay debt using RCN collateral + await collateral.payOffDebt(entryId, [], { from: user }); + expect(await loanManager.getStatus(debtId)).to.eq.BN(b(2)); + + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(275)); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(2362)); + const entry = await collateral.entries(b(1)); + expect(entry.amount).to.eq.BN(b(2362)); + }); it('Should pay debt using token collateral, without loan oracle, using all the collateral', async () => { // Create oracle and alt token const dai = await TestToken.new(); @@ -1883,6 +1965,91 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a await collateral.payOffDebt(entryId, [], { from: user }); expect(await loanManager.getStatus(debtId)).to.eq.BN(b(1)); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(5000)); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(0)); + const entry = await collateral.entries(b(1)); + expect(entry.amount).to.eq.BN(b(0)); + }); + it('Should pay debt using token collateral, with loan oracle, using all the collateral', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // Create loan oracle + const loanOracle = await TestRateOracle.new(); + await loanOracle.setEquivalent(b('4000000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1100), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + loanOracle.address, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Increase the debt a lot + await model.setTotal(debtId, b(100000), { from: owner }); + + // Pay debt using RCN collateral + await collateral.payOffDebt(entryId, [], { from: user }); + expect(await loanManager.getStatus(debtId)).to.eq.BN(b(1)); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(5000)); expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(0)); const entry = await collateral.entries(b(1)); From 575304ec6e6f4fc72eb68bfaa28797e5d3f707b9 Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Fri, 8 Nov 2019 14:25:33 +0100 Subject: [PATCH 06/10] Add test claim by dueTime --- contracts/test_utils/diaspore/TestModel.sol | 6 + test/diaspore/TestCollateralAlt.js | 151 ++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/contracts/test_utils/diaspore/TestModel.sol b/contracts/test_utils/diaspore/TestModel.sol index 98576310..e5e7f4c7 100644 --- a/contracts/test_utils/diaspore/TestModel.sol +++ b/contracts/test_utils/diaspore/TestModel.sol @@ -43,6 +43,7 @@ contract TestModel is ERC165, BytesUtils, Ownable { event SetErrorFlag(bytes32 _id, uint256 _flag); event SetGlobalErrorFlag(uint256 _flag); event SetTotal(bytes32 _id, uint256 _total); + event SetDueTime(bytes32 _id, uint256 _time); mapping(bytes4 => bool) private _supportedInterface; @@ -90,6 +91,11 @@ contract TestModel is ERC165, BytesUtils, Ownable { emit SetTotal(_id, _total); } + function setDueTime(bytes32 _id, uint64 _time) external onlyOwner { + registry[_id].dueTime = _time; + emit SetDueTime(_id, _time); + } + function setEngine(address _engine) external onlyOwner { engine = _engine; emit SetEngine(_engine); diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index e1192d82..d9fb8de1 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -2134,4 +2134,155 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a ); }); }); + describe('Should claim expired collateral', () => { + it('Should claim payment of expired debt with rcn collateral without oracle', async () => { + // // Request a loan + // const modelData = await model.encodeData( + // b(1000), + // MAX_UINT64, + // ); + + // // Request loan + // const requestReceipt = await loanManager.requestLoan( + // b(1000), // Requested amount + // model.address, // Debt model + // address0x, // Oracle + // user, // Borrower + // address0x, // Callback + // b(0), // Salt + // MAX_UINT64, // Expiration + // modelData, // Model data + // { + // from: user, + // } + // ); + + // const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // // Create collateral entry + // await rcn.setBalance(user, b(2500)); + // await rcn.approve(collateral.address, b(2500), { from: user }); + // await collateral.create( + // debtId, // Debt ID + // address0x, // Oracle address + // b(2500), // Token Amount + // b(12000), // Liquidation Ratio + // b(15000), // Balance ratio + // b(9), // Burn fee + // b(1), // Reward fee + // { + // from: user, + // } + // ); + + // const entryId = b(1); + + // // Lend loan + // await rcn.setBalance(anotherUser, b(1000)); + // await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + // await loanManager.lend( + // debtId, // Debt ID + // [], // Oracle data + // collateral.address, // Collateral cosigner + // b(0), // Cosigner limit + // toBytes32(entryId), // Cosigner data + // [], // Callback data + // { + // from: anotherUser, + // } + // ); + + // // Move due time + // await model.setDueTime(debtId, b(500), { from: owner }); + + // // Claim loan payment (due time) + // await collateral.claim(address0x, debtId, [], { from: stub }); + // expect(await rcn.balanceOf(collateral.address)).to.eq.BN(b(1500)); + // expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1000)); + // expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); + // const entry = await collateral.entries(entryId); + // expect(entry.amount).to.eq.BN(b(1500)); + }); + it('Should claim payment of expired debt with token collateral without oracle', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1000), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + address0x, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Move due time + await model.setDueTime(debtId, b(500), { from: owner }); + + // Claim loan payment (due time) + await collateral.claim(address0x, debtId, [], { from: stub }); + // TODO Check 1999??? + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(1999)); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1000)); + // expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); + const entry = await collateral.entries(entryId); + expect(entry.amount).to.eq.BN(b(1999)); + }); + }); }); From ac1bafaa9884c4fd48dfd3b6d0618265d188d552 Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Fri, 8 Nov 2019 18:20:28 +0100 Subject: [PATCH 07/10] Fix remainder shadow variable convertPay --- .../core/diaspore/cosigner/Collateral.sol | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/contracts/core/diaspore/cosigner/Collateral.sol b/contracts/core/diaspore/cosigner/Collateral.sol index ae27ab97..12b9281a 100644 --- a/contracts/core/diaspore/cosigner/Collateral.sol +++ b/contracts/core/diaspore/cosigner/Collateral.sol @@ -790,28 +790,31 @@ contract Collateral is Ownable, Cosigner, ERC721Base { _oracleData ); - emit ConvertPay( - _entryId, - sold, - bought, - _oracleData - ); - if (paidTokens < tokensToPay) { // Buy back extra collateral - sold = tokensToPay - paidTokens; - bought = converter.safeConvertFrom( + // TODO: Remove convertback, replace by raw RCN + uint256 remainder = tokensToPay - paidTokens; + uint256 rebought = converter.safeConvertFrom( loanManagerToken, token, - sold, + remainder, 0 ); - emit Rebuy(_entryId, sold, bought); - } else { - bought = 0; + + sold = sold.sub(rebought); + bought = bought.sub(remainder); + + emit Rebuy(_entryId, remainder, rebought); } - entry.amount = entry.amount.sub(sold).add(bought); + emit ConvertPay( + _entryId, + sold, + bought, + _oracleData + ); + + entry.amount = entry.amount.sub(sold); } /** From b95b6bea5f4eb588261b5ed4e264ce970695176f Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Fri, 8 Nov 2019 19:17:01 +0100 Subject: [PATCH 08/10] Add test claim with collateral on RCN --- test/diaspore/TestCollateralAlt.js | 135 ++++++++++++++--------------- 1 file changed, 67 insertions(+), 68 deletions(-) diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index d9fb8de1..39726ed2 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -2136,72 +2136,72 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a }); describe('Should claim expired collateral', () => { it('Should claim payment of expired debt with rcn collateral without oracle', async () => { - // // Request a loan - // const modelData = await model.encodeData( - // b(1000), - // MAX_UINT64, - // ); - - // // Request loan - // const requestReceipt = await loanManager.requestLoan( - // b(1000), // Requested amount - // model.address, // Debt model - // address0x, // Oracle - // user, // Borrower - // address0x, // Callback - // b(0), // Salt - // MAX_UINT64, // Expiration - // modelData, // Model data - // { - // from: user, - // } - // ); - - // const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; - - // // Create collateral entry - // await rcn.setBalance(user, b(2500)); - // await rcn.approve(collateral.address, b(2500), { from: user }); - // await collateral.create( - // debtId, // Debt ID - // address0x, // Oracle address - // b(2500), // Token Amount - // b(12000), // Liquidation Ratio - // b(15000), // Balance ratio - // b(9), // Burn fee - // b(1), // Reward fee - // { - // from: user, - // } - // ); - - // const entryId = b(1); - - // // Lend loan - // await rcn.setBalance(anotherUser, b(1000)); - // await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); - // await loanManager.lend( - // debtId, // Debt ID - // [], // Oracle data - // collateral.address, // Collateral cosigner - // b(0), // Cosigner limit - // toBytes32(entryId), // Cosigner data - // [], // Callback data - // { - // from: anotherUser, - // } - // ); - - // // Move due time - // await model.setDueTime(debtId, b(500), { from: owner }); - - // // Claim loan payment (due time) - // await collateral.claim(address0x, debtId, [], { from: stub }); - // expect(await rcn.balanceOf(collateral.address)).to.eq.BN(b(1500)); - // expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1000)); - // expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); - // const entry = await collateral.entries(entryId); - // expect(entry.amount).to.eq.BN(b(1500)); + // Request a loan + const modelData = await model.encodeData( + b(1000), + MAX_UINT64, + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + address0x, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await rcn.setBalance(user, b(2500)); + await rcn.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + address0x, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(1000)); + await rcn.approve(loanManager.address, b(1000), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Move due time + await model.setDueTime(debtId, b(500), { from: owner }); + + // Claim loan payment (due time) + await collateral.claim(address0x, debtId, [], { from: stub }); + expect(await rcn.balanceOf(collateral.address)).to.eq.BN(b(1500)); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1000)); + expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); + const entry = await collateral.entries(entryId); + expect(entry.amount).to.eq.BN(b(1500)); }); it('Should claim payment of expired debt with token collateral without oracle', async () => { // Create oracle and alt token @@ -2277,10 +2277,9 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a // Claim loan payment (due time) await collateral.claim(address0x, debtId, [], { from: stub }); - // TODO Check 1999??? expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(1999)); expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(1000)); - // expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); + expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); const entry = await collateral.entries(entryId); expect(entry.amount).to.eq.BN(b(1999)); }); From aa80e3377f434005bfa333aa75764d63d234755d Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Tue, 12 Nov 2019 11:42:55 +0100 Subject: [PATCH 09/10] Add claim payment explired debt with oracle test --- test/diaspore/TestCollateralAlt.js | 84 ++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index 39726ed2..fae415fe 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -2283,5 +2283,89 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a const entry = await collateral.entries(entryId); expect(entry.amount).to.eq.BN(b(1999)); }); + it('Should claim payment of expired debt with token collateral with oracle', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // Create loan oracle + const loanOracle = await TestRateOracle.new(); + await loanOracle.setEquivalent(b('2000000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1000), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + loanOracle.address, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(500)); + await rcn.approve(loanManager.address, b(500), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Move due time + await model.setDueTime(debtId, b(500), { from: owner }); + + // Claim loan payment (due time) + await collateral.claim(address0x, debtId, [], { from: stub }); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(2250)); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(500)); + expect(await model.getPaid(debtId)).to.eq.BN(b(1000)); + const entry = await collateral.entries(entryId); + expect(entry.amount).to.eq.BN(b(2250)); + }); }); }); From 2f645fe7d0c5c4124f02f39e5d9ae81b55eba07f Mon Sep 17 00:00:00 2001 From: Agustin Aguilar Date: Tue, 12 Nov 2019 12:30:50 +0100 Subject: [PATCH 10/10] Add edge cases claim expired --- test/diaspore/TestCollateralAlt.js | 172 +++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/test/diaspore/TestCollateralAlt.js b/test/diaspore/TestCollateralAlt.js index fae415fe..79d51e8a 100644 --- a/test/diaspore/TestCollateralAlt.js +++ b/test/diaspore/TestCollateralAlt.js @@ -2368,4 +2368,176 @@ contract('Test Collateral cosigner Diaspore', function ([_, stub, owner, user, a expect(entry.amount).to.eq.BN(b(2250)); }); }); + describe('Should handle edge cases on claim expired debt', () => { + it('Should ignore claim if debt is not expired', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // Create loan oracle + const loanOracle = await TestRateOracle.new(); + await loanOracle.setEquivalent(b('2000000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1000), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + loanOracle.address, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(9), // Burn fee + b(1), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(500)); + await rcn.approve(loanManager.address, b(500), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Claim loan payment (due time) + await collateral.claim(address0x, debtId, [], { from: stub }); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(2500)); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(0)); + expect(await model.getPaid(debtId)).to.eq.BN(b(0)); + const entry = await collateral.entries(entryId); + expect(entry.amount).to.eq.BN(b(2500)); + }); + it('Should only use collateral in entry', async () => { + // Create oracle and alt token + const dai = await TestToken.new(); + const oracle = await TestRateOracle.new(); + await oracle.setToken(dai.address); + await oracle.setEquivalent(b('500000000000000000')); + + // Create loan oracle + const loanOracle = await TestRateOracle.new(); + await loanOracle.setEquivalent(b('2000000000000000000')); + + // configure converter + await rcn.setBalance(converter.address, b(2).pow(b(64))); + await dai.setBalance(converter.address, b(2).pow(b(64))); + await converter.setRate(dai.address, rcn.address, b('2000000000000000000')); + await converter.setRate(rcn.address, dai.address, b('500000000000000000')); + + // Request a loan + const modelData = await model.encodeData( + b(1000), + MAX_UINT64 + ); + + // Request loan + const requestReceipt = await loanManager.requestLoan( + b(1000), // Requested amount + model.address, // Debt model + loanOracle.address, // Oracle + user, // Borrower + address0x, // Callback + b(0), // Salt + MAX_UINT64, // Expiration + modelData, // Model data + { + from: user, + } + ); + + const debtId = requestReceipt.receipt.logs.find((e) => e.event === 'Requested').args._id; + + // Create collateral entry + await dai.setBalance(user, b(2500)); + await dai.approve(collateral.address, b(2500), { from: user }); + await collateral.create( + debtId, // Debt ID + oracle.address, // Oracle address + b(2500), // Token Amount + b(12000), // Liquidation Ratio + b(15000), // Balance ratio + b(0), // Burn fee + b(0), // Reward fee + { + from: user, + } + ); + + const entryId = b(1); + + // Lend loan + await rcn.setBalance(anotherUser, b(500)); + await rcn.approve(loanManager.address, b(500), { from: anotherUser }); + await loanManager.lend( + debtId, // Debt ID + [], // Oracle data + collateral.address, // Collateral cosigner + b(0), // Cosigner limit + toBytes32(entryId), // Cosigner data + [], // Callback data + { + from: anotherUser, + } + ); + + // Move due time + await model.setDueTime(debtId, b(500), { from: owner }); + await model.setTotal(debtId, b(900000), { from: owner }); + + // Increase balance of collateral contract + await dai.setBalance(collateral.address, b(5000000)); + + // Claim loan payment (due time) + // and should use all collateral + await collateral.claim(address0x, debtId, [], { from: stub }); + expect(await dai.balanceOf(collateral.address)).to.eq.BN(b(5000000).sub(b(2500))); + expect(await rcn.balanceOf(debtEngine.address)).to.eq.BN(b(5000)); + expect(await model.getPaid(debtId)).to.eq.BN(b(10000)); + const entry = await collateral.entries(entryId); + expect(entry.amount).to.eq.BN(b(0)); + }); + }); });