diff --git a/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/AbstractDex.java b/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/AbstractDex.java index eefdbf0e6..59467f03d 100644 --- a/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/AbstractDex.java +++ b/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/AbstractDex.java @@ -644,6 +644,19 @@ public void govWithdraw(int id, Address token, BigInteger value) { Context.call(token, "transfer", getDaofund(), value); } + @External + public void governanceBorrow(Address token, BigInteger amount, Address recipient) { + onlyGovernance(); + BigInteger currentDebt = governanceDebt.getOrDefault(token, BigInteger.ZERO); + governanceDebt.set(token, currentDebt.add(amount)); + Context.call(token, "transfer", recipient, amount); + } + + @External(readonly=true) + public BigInteger getGovernanceDebt(Address token) { + return governanceDebt.getOrDefault(token, BigInteger.ZERO); + } + @External public void govSetPoolTotal(int pid, BigInteger total) { onlyGovernance(); diff --git a/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/DexDBVariables.java b/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/DexDBVariables.java index 97c8e40af..78aed6a20 100644 --- a/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/DexDBVariables.java +++ b/core-contracts/Dex/src/main/java/network/balanced/score/core/dex/DexDBVariables.java @@ -58,6 +58,7 @@ public class DexDBVariables { private static final String TOKEN_PRECISIONS = "token_precisions"; public static final String VERSION = "version"; public static final String ORACLE_PROTECTION = "oracle_protection"; + public static final String GOV_DEBT = "governance_debt"; final static VarDB
governance = Context.newVarDB(GOVERNANCE_ADDRESS, Address.class); @@ -137,4 +138,8 @@ public class DexDBVariables { //Map: pid -> percentage public final static DictDB oracleProtection = Context.newDictDB(ORACLE_PROTECTION, BigInteger.class); + + //Map: token -> amount + final static DictDB governanceDebt = Context.newDictDB(GOV_DEBT, BigInteger.class); + } diff --git a/core-contracts/Loans/src/intTest/java/network/balanced/score/core/loans/LoansIntegrationTest.java b/core-contracts/Loans/src/intTest/java/network/balanced/score/core/loans/LoansIntegrationTest.java index 20419b767..7e27e5a7a 100644 --- a/core-contracts/Loans/src/intTest/java/network/balanced/score/core/loans/LoansIntegrationTest.java +++ b/core-contracts/Loans/src/intTest/java/network/balanced/score/core/loans/LoansIntegrationTest.java @@ -353,107 +353,6 @@ void repayDebt() throws Exception { assertEquals(debt, loanTakerMultiPartialRepayBaS.get("_balance")); } - @Test - @Order(4) - void sellCollateral() throws Exception { - // Arrange - BalancedClient loanTakerFullDebtSell = balanced.newClient(); - BalancedClient loanTakerPartialDebtSell = balanced.newClient(); - BalancedClient loanTakerETHFullSell = balanced.newClient(); - BalancedClient loanTakerETHPartialSell = balanced.newClient(); - BigInteger collateral = BigInteger.TEN.pow(5).multiply(sicxDecimals); - BigInteger collateralETH = BigInteger.TEN.multiply(iethDecimals); - - owner.irc2(ethAddress).mintTo(loanTakerETHFullSell.getAddress(), collateralETH, null); - owner.irc2(ethAddress).mintTo(loanTakerETHPartialSell.getAddress(), collateralETH, null); - - BigInteger loanAmount = BigInteger.valueOf(17).multiply(EXA); - BigInteger ethLoanAmount = BigInteger.TEN.pow(23); - - BigInteger collateralToSellFullDebt = BigInteger.TEN.pow(19); - BigInteger ETHCollateralToSellFullDebt = BigInteger.valueOf(28).multiply(BigInteger.TEN.pow(22)); - BigInteger collateralToSellPartialDebt = BigInteger.valueOf(5).multiply(EXA); - BigInteger ETHCollateralToSellPartialDebt = BigInteger.valueOf(2).multiply(BigInteger.TEN.pow(23)); - - BigInteger minimumReceiveToSellFullDebt = BigInteger.valueOf(16).multiply(EXA); - BigInteger minimumReceiveToSellPartialDebt = BigInteger.valueOf(8).multiply(EXA); - - // Act - loanTakerFullDebtSell.stakeDepositAndBorrow(collateral, loanAmount); - loanTakerPartialDebtSell.stakeDepositAndBorrow(collateral, loanAmount); - loanTakerETHFullSell.depositAndBorrow(ethAddress, collateralETH, ethLoanAmount); - loanTakerETHPartialSell.depositAndBorrow(ethAddress, collateralETH, ethLoanAmount); - - // requested collateral sell worth is more than debt - assertThrows(UserRevertedException.class, () -> - loanTakerFullDebtSell.loans.sellCollateral(collateral, "sICX", minimumReceiveToSellFullDebt)); - assertThrows(UserRevertedException.class, () -> - loanTakerFullDebtSell.loans.sellCollateral(collateralETH, "iETH", minimumReceiveToSellFullDebt)); - - // minimum receive is more than users debt - assertThrows(UserRevertedException.class, () -> - loanTakerFullDebtSell.loans.sellCollateral(collateral, "sICX", loanAmount.add(BigInteger.TEN.pow(19)))); - assertThrows(UserRevertedException.class, () -> - loanTakerFullDebtSell.loans.sellCollateral(collateralETH, "iETH", - ethLoanAmount.add(BigInteger.TEN.pow(19)))); - - BigInteger loanTakerFullDebtSellCollateralPre = loanTakerFullDebtSell.getLoansCollateralPosition("sICX"); - BigInteger loanTakerETHFullDebtSellCollateralPre = loanTakerETHFullSell.getLoansCollateralPosition("iETH"); - BigInteger loanTakerPartialDebtSellCollateralPre = loanTakerPartialDebtSell.getLoansCollateralPosition("sICX"); - BigInteger loanTakerETHPartialDebtSellCollateralPre = loanTakerETHPartialSell.getLoansCollateralPosition( - "iETH"); - - BigInteger loanTakerFullDebtSellDebtPositionPre = loanTakerFullDebtSell.getLoansAssetPosition("sICX", "bnUSD"); - BigInteger loanTakerETHFullDebtSellDebtPositionPre = loanTakerETHFullSell.getLoansAssetPosition("iETH", - "bnUSD"); - BigInteger loanTakerPartialDebtSellDebtPositionPre = loanTakerPartialDebtSell.getLoansAssetPosition("sICX", - "bnUSD"); - BigInteger loanTakerETHPartialDebtSellDebtPositionPre = loanTakerETHPartialSell.getLoansAssetPosition("iETH", - "bnUSD"); - - loanTakerFullDebtSell.loans.sellCollateral(collateralToSellFullDebt, "sICX", minimumReceiveToSellFullDebt); - loanTakerETHFullSell.loans.sellCollateral(ETHCollateralToSellFullDebt, "iETH", minimumReceiveToSellFullDebt); - loanTakerPartialDebtSell.loans.sellCollateral(collateralToSellPartialDebt, "sICX", - minimumReceiveToSellPartialDebt); - loanTakerETHPartialSell.loans.sellCollateral(ETHCollateralToSellPartialDebt, "iETH", - minimumReceiveToSellPartialDebt); - - BigInteger loanTakerFullDebtSellCollateralPost = loanTakerFullDebtSell.getLoansCollateralPosition("sICX"); - BigInteger loanTakerETHFullDebtSellCollateralPost = loanTakerETHFullSell.getLoansCollateralPosition("iETH"); - BigInteger loanTakerPartialDebtSellCollateralPost = loanTakerPartialDebtSell.getLoansCollateralPosition("sICX"); - BigInteger loanTakerETHPartialDebtSellCollateralPost = loanTakerETHPartialSell.getLoansCollateralPosition( - "iETH"); - - BigInteger loanTakerFullDebtSellDebtPositionPost = loanTakerFullDebtSell.getLoansAssetPosition("sICX", "bnUSD"); - BigInteger loanTakerETHFullDebtSellDebtPositionPost = loanTakerETHFullSell.getLoansAssetPosition("iETH", - "bnUSD"); - BigInteger loanTakerPartialDebtSellDebtPositionPost = loanTakerPartialDebtSell.getLoansAssetPosition("sICX", - "bnUSD"); - BigInteger loanTakerETHPartialDebtSellDebtPositionPost = loanTakerETHPartialSell.getLoansAssetPosition("iETH" - , "bnUSD"); - - // Assert - // requested collateral sell more than available collateral - assertThrows(UserRevertedException.class, () -> - loanTakerFullDebtSell.loans.sellCollateral(collateral, "sICX", minimumReceiveToSellFullDebt)); - assertThrows(UserRevertedException.class, () -> - loanTakerFullDebtSell.loans.sellCollateral(collateralETH, "iETH", minimumReceiveToSellFullDebt)); - - assertEquals(loanTakerFullDebtSellCollateralPost, - loanTakerFullDebtSellCollateralPre.subtract(collateralToSellFullDebt)); - assertEquals(loanTakerETHFullDebtSellCollateralPost, - loanTakerETHFullDebtSellCollateralPre.subtract(ETHCollateralToSellFullDebt)); - assertEquals(loanTakerPartialDebtSellCollateralPost, - loanTakerPartialDebtSellCollateralPre.subtract(collateralToSellPartialDebt)); - assertEquals(loanTakerETHPartialDebtSellCollateralPost, - loanTakerETHPartialDebtSellCollateralPre.subtract(ETHCollateralToSellPartialDebt)); - - assertTrue(loanTakerFullDebtSellDebtPositionPost.compareTo(loanTakerFullDebtSellDebtPositionPre) < 0); - assertTrue(loanTakerETHFullDebtSellDebtPositionPost.compareTo(loanTakerETHFullDebtSellDebtPositionPre) < 0); - assertTrue(loanTakerPartialDebtSellDebtPositionPost.compareTo(loanTakerPartialDebtSellDebtPositionPre) < 0); - assertTrue(loanTakerETHPartialDebtSellDebtPositionPost.compareTo(loanTakerETHPartialDebtSellDebtPositionPre) < 0); - } - @Test @Order(4) void rateLimits() throws Exception { @@ -736,7 +635,7 @@ void redeemCollateral_iETH() throws Exception { BalancedClient loanTaker = balanced.newClient(); BigInteger collateral = BigInteger.TEN.multiply(iethDecimals); - BigInteger redeemAmount = BigInteger.TEN.pow(22); + BigInteger redeemAmount = BigInteger.TEN.pow(21); owner.irc2(ethAddress).mintTo(loanTaker.getAddress(), collateral, null); loanTaker.depositAndBorrow(ethAddress, collateral, redeemAmount); diff --git a/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansImpl.java b/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansImpl.java index 4038e95ab..731d9e806 100644 --- a/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansImpl.java +++ b/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansImpl.java @@ -464,6 +464,7 @@ private void _returnAsset(String _from, BigInteger _value, String _collateralSym public void redeemCollateral(Address _collateralAddress, BigInteger _amount) { checkStatus(); loansOn(); + Context.require(!getRedemptionExemption(_collateralAddress), "bnUSD cannot be redeemed for this collateral"); Address caller = Context.getCaller(); String collateralSymbol = CollateralDB.getSymbol(_collateralAddress); BigInteger daofundFee = redemptionDaoFee.getOrDefault(BigInteger.ZERO).multiply(_amount).divide(POINTS); @@ -559,16 +560,6 @@ public void withdrawCollateral(BigInteger _value, @Optional String _collateralSy transferCollateral(collateralSymbol, from, _value, "Collateral withdrawn.", new byte[0]); } - @External - public void sellCollateral(BigInteger collateralAmountToSell, String collateralSymbol, - BigInteger minimumDebtRepaid) { - checkStatus(); - loansOn(); - Address from = Context.getCaller(); - sellUserCollateral(from.toString(), collateralAmountToSell, collateralSymbol, minimumDebtRepaid); - } - - public class LiquidationResult { public BigInteger liquidationAmount; public BigInteger collateralToLiquidate; @@ -703,66 +694,6 @@ private void depositCollateral(String _symbol, BigInteger _amount, String _from) CollateralReceived(_from, _symbol, _amount); } - private void sellUserCollateral(String from, BigInteger collateralToSell, String collateralSymbol, - BigInteger minimumDebtToRepay) { - Context.require(collateralToSell.compareTo(BigInteger.ZERO) > 0, TAG + ": Sell amount must be more than zero."); - Context.require(PositionsDB.hasPosition(from), TAG + ": This address does not have a position on Balanced."); - - Position position = PositionsDB.getPosition(from); - BigInteger userCollateral = position.getCollateral(collateralSymbol); - - Context.require(userCollateral.compareTo(collateralToSell) >= 0, TAG + ": Position holds less " + - "collateral than the requested sell."); - - Address bnUSDAddress = getBnusd(); - BigInteger userDebt = position.getDebt(collateralSymbol); - - Address collateralAddress = CollateralDB.getAddress(collateralSymbol); - Address dexAddress = getDex(); - - amountReceived.set(null); - BigInteger poolID = Context.call(BigInteger.class, dexAddress, "getPoolId", collateralAddress, bnUSDAddress); - Context.require(poolID != null, TAG + ": There doesn't exist a bnUSD pool for " + collateralSymbol + "."); - - Context.require(userDebt.compareTo(minimumDebtToRepay) >= 0, TAG + ": Minimum receive cannot be greater than " + - "your debt."); - expectedToken.set(bnUSDAddress); - - JsonObject swapParams = Json.object() - .add("toToken", bnUSDAddress.toString()) - .add("minimumReceive", minimumDebtToRepay.toString()); - JsonObject swapData = Json.object() - .add("method", "_swap") - .add("params", swapParams); - byte[] data = swapData.toString().getBytes(); - - transferCollateral(collateralSymbol, dexAddress, collateralToSell, - collateralSymbol + " swapped for " + BNUSD_SYMBOL, data); - BigInteger bnUSDReceived = amountReceived.get(); - amountReceived.set(null); - - Context.require(userDebt.compareTo(bnUSDReceived) >= 0, TAG + ": Cannot sell collateral worth more than your " + - "debt."); - - BigInteger remainingDebt = userDebt.subtract(bnUSDReceived); - BigInteger remainingCollateral = userCollateral.subtract(collateralToSell); - - position.setCollateral(collateralSymbol, remainingCollateral); - - if (remainingDebt.compareTo(BigInteger.ZERO) > 0) { - position.setDebt(collateralSymbol, remainingDebt); - } else { - position.setDebt(collateralSymbol, null); - } - - TokenUtils.burnAssetFrom(Context.getAddress(), bnUSDReceived); - - Context.call(getRewards(), "updateBalanceAndSupply", "Loans", DebtDB.getTotalDebt(), from.toString(), position.getTotalDebt()); - - String logMessage = "Loan of " + bnUSDReceived + " " + BNUSD_SYMBOL + " sold for" + collateralToSell + " " + - collateralSymbol + " to Balanced."; - CollateralSold(from, BNUSD_SYMBOL, collateralSymbol, bnUSDReceived, logMessage); - } private void removeCollateral(String from, BigInteger value, String collateralSymbol) { Context.require(value.compareTo(BigInteger.ZERO) > 0, TAG + ": Withdraw amount must be more than zero."); @@ -958,6 +889,17 @@ public BigInteger getRedemptionFee() { return redemptionFee.get(); } + @External + public void setRedemptionExemption(Address token, boolean exempt) { + onlyGovernance(); + redemptionExemptions.set(token, exempt); + } + + @External(readonly = true) + public boolean getRedemptionExemption(Address token) { + return redemptionExemptions.getOrDefault(token, false); + } + @External public void setRedemptionDaoFee(BigInteger _fee) { onlyGovernance(); @@ -1104,11 +1046,6 @@ public void OriginateLoan(String recipient, String symbol, BigInteger amount, St public void LoanRepaid(String account, String symbol, BigInteger amount, String note) { } - @EventLog(indexed = 3) - public void CollateralSold(String account, String assetSymbol, String collateralSymbol, BigInteger amount, - String note) { - } - @EventLog(indexed = 3) public void BadDebtCancelled(String account, String symbol, BigInteger amount) { } diff --git a/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansVariables.java b/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansVariables.java index 70271a9ed..c6ec50817 100644 --- a/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansVariables.java +++ b/core-contracts/Loans/src/main/java/network/balanced/score/core/loans/LoansVariables.java @@ -42,6 +42,7 @@ public class LoansVariables { private static final String REDEMPTION_DAO_FEE = "redemption_dao_fee"; private static final String RETIREMENT_BONUS = "retirement_bonus"; private static final String NEW_LOAN_MINIMUM = "new_loan_minimum"; + private static final String REDEMPTION_EXEMPTIONS = "redemption_exemptions"; private static final String REDEEM_BATCH_SIZE = "redeem_batch_size"; private static final String MAX_RETIRE_PERCENT = "max_retire_percent"; @@ -69,6 +70,7 @@ public class LoansVariables { static final VarDB newLoanMinimum = Context.newVarDB(NEW_LOAN_MINIMUM, BigInteger.class); static final VarDB redeemBatch = Context.newVarDB(REDEEM_BATCH_SIZE, Integer.class); static final VarDB maxRetirePercent = Context.newVarDB(MAX_RETIRE_PERCENT, BigInteger.class); + static final DictDB redemptionExemptions= Context.newDictDB(REDEMPTION_EXEMPTIONS, Boolean.class); static final VarDB
expectedToken = Context.newVarDB(EXPECTED_TOKEN, Address.class); static final VarDB amountReceived = Context.newVarDB(AMOUNT_RECEIVED, BigInteger.class); diff --git a/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTest.java b/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTest.java index 5aef1bff1..238bc010c 100644 --- a/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTest.java +++ b/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTest.java @@ -906,157 +906,6 @@ void returnAsset_AlreadyAboveCeiling() { verifyTotalDebt(expectedDebt.multiply(BigInteger.TWO).subtract(loanToRepay)); } - @Test - void sellCollateral() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(2000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(200).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(100).multiply(EXA); - BigInteger minimumReceiveSicxCollateralSell = BigInteger.valueOf(100).multiply(EXA); - BigInteger iETHCollateralToSell = BigInteger.valueOf(80).multiply(EXA); - BigInteger minimumReceiveiETHCollateralSell = BigInteger.valueOf(80).multiply(EXA); - BigInteger expectedFee = calculateFee(loan); - - takeLoanICX(account, "bnUSD", collateral, loan); - takeLoaniETH(account, collateral, loan); - - BigInteger rate = EXA.divide(BigInteger.TWO); - mockSicxBnusdPrice(rate); - mockiETHBnusdPrice(rate); - - BigInteger expectedBnusdRepaidForSicx = collateralToSell.multiply(BigInteger.TWO); - BigInteger expectedBnusdRepaidForiETH = iETHCollateralToSell.multiply(BigInteger.ONE); - mockSwap(sicx, bnusd, collateralToSell, expectedBnusdRepaidForSicx); - mockSwap(ieth, bnusd, iETHCollateralToSell, expectedBnusdRepaidForiETH); - - // Act - loans.invoke(account, "sellCollateral", collateralToSell, "sICX", minimumReceiveSicxCollateralSell); - loans.invoke(account, "sellCollateral", iETHCollateralToSell, "iETH", minimumReceiveiETHCollateralSell); - - // Assert - verifyPosition(account.getAddress(), collateral.subtract(collateralToSell), - loan.add(expectedFee).subtract(expectedBnusdRepaidForSicx), "sICX"); - verifyPosition(account.getAddress(), collateral.subtract(iETHCollateralToSell), - loan.add(expectedFee).subtract(expectedBnusdRepaidForiETH), "iETH"); - } - - @Test - void sellCollateral_ZeroCollateral() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(1000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(200).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(0).multiply(EXA); - BigInteger minimumReceiveAfterSell = BigInteger.valueOf(50).multiply(EXA); - String expectedErrorMessage = "Reverted(0): " + TAG + "Sell amount must be more than zero."; - - takeLoanICX(account, "bnUSD", collateral, loan); - - // Assert & Act - Executable sellZeroCollateral = () -> loans.invoke(account, "sellCollateral", collateralToSell, - "sICX", minimumReceiveAfterSell); - expectErrorMessage(sellZeroCollateral, expectedErrorMessage); - } - - @Test - void sellCollateral_NoPosition() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateralToSell = BigInteger.valueOf(200).multiply(EXA); - BigInteger minimumReceiveAfterSell = BigInteger.valueOf(50).multiply(EXA); - - String expectedErrorMessage = "Reverted(0): " + TAG + "This address does not have a position on Balanced."; - - // Assert & Act - Executable sellWithNoPosition = () -> loans.invoke(account, "sellCollateral", collateralToSell, - "sICX", minimumReceiveAfterSell); - expectErrorMessage(sellWithNoPosition, expectedErrorMessage); - } - - @Test - void sellCollateral_TooMuchCollateral() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(1000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(200).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(1100).multiply(EXA); - BigInteger minimumReceiveAfterSell = BigInteger.valueOf(50).multiply(EXA); - String expectedErrorMessage = "Reverted(0): " + TAG + "Position holds less collateral than the requested " + - "sell."; - - takeLoanICX(account, "bnUSD", collateral, loan); - - // Assert & Act - Executable sellTooMuchCollateral = () -> loans.invoke(account, "sellCollateral", collateralToSell, "sICX", - minimumReceiveAfterSell); - expectErrorMessage(sellTooMuchCollateral, expectedErrorMessage); - } - - @Test - void sellCollateral_NoAvailablePool() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(1000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(200).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(300).multiply(EXA); - BigInteger minimumReceiveAfterSell = BigInteger.valueOf(300).multiply(EXA); - String collateralSymbol = "sICX"; - String expectedErrorMessage = "Reverted(0): " + TAG + "There doesn't exist a bnUSD pool for " + collateralSymbol - + "."; - - takeLoanICX(account, "bnUSD", collateral, loan); - - // Assert & Act - Executable noAvailablePool = () -> loans.invoke(account, "sellCollateral", collateralToSell, collateralSymbol, - minimumReceiveAfterSell); - expectErrorMessage(noAvailablePool, expectedErrorMessage); - } - - @Test - void sellCollateral_TooMuchDebt() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(1000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(200).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(300).multiply(EXA); - BigInteger minimumReceiveAfterSell = BigInteger.valueOf(300).multiply(EXA); - String expectedErrorMessage = "Reverted(0): " + TAG + "Minimum receive cannot be greater than your debt."; - - BigInteger rate = EXA.divide(BigInteger.TWO); - mockSicxBnusdPrice(rate); - - takeLoanICX(account, "bnUSD", collateral, loan); - - // Assert & Act - Executable sellTooMuchDebt = () -> loans.invoke(account, "sellCollateral", collateralToSell, "sICX", - minimumReceiveAfterSell); - expectErrorMessage(sellTooMuchDebt, expectedErrorMessage); - } - - @Test - void sellCollateral_TooMuchCollateralSell() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(1000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(200).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(400).multiply(EXA); - BigInteger minimumReceiveAfterSell = BigInteger.valueOf(100).multiply(EXA); - String expectedErrorMessage = "Reverted(0): " + TAG + "Cannot sell collateral worth more than your debt."; - - BigInteger rate = EXA.divide(BigInteger.TWO); - mockSicxBnusdPrice(rate); - BigInteger expectedBnusdRepaidForSicx = collateralToSell.multiply(BigInteger.TWO); - mockSwap(sicx, bnusd, collateralToSell, expectedBnusdRepaidForSicx); - - takeLoanICX(account, "bnUSD", collateral, loan); - - // Assert & Act - Executable sellTooMuchCollateral = () -> loans.invoke(account, "sellCollateral", collateralToSell, "sICX", - minimumReceiveAfterSell); - expectErrorMessage(sellTooMuchCollateral, expectedErrorMessage); - } - @Test void withdrawCollateral() { // Arrange @@ -1830,6 +1679,27 @@ void redeemCollateral_redeemAboveMax() { expectErrorMessage(redeemAboveMaxSize, expectedErrorMessage); } + @Test + void redeemCollateral_exemptCollateral() { + // Arrange + Account account1 = sm.createAccount(); + Account redeemer = sm.createAccount(); + BigInteger collateral = BigInteger.valueOf(4000).multiply(EXA); + BigInteger loan = BigInteger.valueOf(400).multiply(EXA); + + BigInteger sICXRate = EXA.divide(BigInteger.TWO); + mockOraclePrice("sICX", sICXRate); + + // Act && Assert + loans.invoke(governance.account, "setRedemptionExemption", sicx.getAddress(), true); + takeLoanICX(account1, "bnUSD", collateral, loan); + + String expectedErrorMessage = "bnUSD cannot be redeemed for this collateral"; + Executable redeemExemptToken = () -> loans.invoke(redeemer, "redeemCollateral", sicx.getAddress(), BigInteger.ONE); + expectErrorMessage(redeemExemptToken, expectedErrorMessage); + } + + @Test void redeemCollateral_iETH() { // Arrange diff --git a/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTestInterest.java b/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTestInterest.java index 3d8f5da97..c45ff2bb1 100644 --- a/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTestInterest.java +++ b/core-contracts/Loans/src/test/java/network/balanced/score/core/loans/LoansTestInterest.java @@ -162,40 +162,6 @@ void returnAsset() { verifyTotalDebt(BigInteger.ZERO); } - @Test - void sellCollateral() { - // Arrange - Account account = sm.createAccount(); - BigInteger collateral = BigInteger.valueOf(1000000).multiply(EXA); - BigInteger loan = BigInteger.valueOf(100000).multiply(EXA); - BigInteger collateralToSell = BigInteger.valueOf(100).multiply(EXA); - BigInteger minimumReceiveSicxCollateralSell = BigInteger.valueOf(10000).multiply(EXA); - BigInteger expectedFee = calculateFee(loan); - BigInteger expectedDebt = loan.add(expectedFee); - BigInteger timePassed = BigInteger.valueOf(20000); - - takeLoanICX(account, "bnUSD", collateral, loan); - sm.getBlock().increase(timePassed.longValue()/2); - loans.invoke(mockBalanced.governance.account, "applyInterest"); - BigInteger interest = expectedDebt.multiply(SICX_INTEREST).multiply(timePassed.multiply(MICRO_SECONDS_IN_A_SECOND)) - .divide(YEAR_IN_MICRO_SECONDS.multiply(POINTS)); - BigInteger rate = EXA.divide(BigInteger.TWO); - mockSicxBnusdPrice(rate); - - BigInteger expectedBnusdRepaidForSicx = collateralToSell.multiply(BigInteger.TWO); - mockSwap(sicx, bnusd, collateralToSell, expectedBnusdRepaidForSicx); - - // Act - loans.invoke(account, "sellCollateral", collateralToSell, "sICX", minimumReceiveSicxCollateralSell); - - // Assert - assertTrue(interest.compareTo(EXA) > 0); - BigInteger debt = getUserDebt(account, "sICX"); - verifyPosition(account.getAddress(), collateral.subtract(collateralToSell), - debt, "sICX"); - assertRoundedEquals(expectedDebt.subtract(expectedBnusdRepaidForSicx).add(interest), debt); - } - @Test void claimInterest() { // Arrange diff --git a/core-contracts/Router/src/main/java/network/balanced/score/core/router/RouterImpl.java b/core-contracts/Router/src/main/java/network/balanced/score/core/router/RouterImpl.java index 6f67d8f1b..a321f6f53 100644 --- a/core-contracts/Router/src/main/java/network/balanced/score/core/router/RouterImpl.java +++ b/core-contracts/Router/src/main/java/network/balanced/score/core/router/RouterImpl.java @@ -261,11 +261,8 @@ private void executeRoute(String _from, byte[] data) { } else { receiver = _from; } - byte[] _data = EMPTY_DATA; - if(routeData.data!=null){ - _data = routeData.data; - } - route(receiver, fromToken, routeData.actions, minimumReceive, _data); + + route(receiver, fromToken, routeData.actions, minimumReceive, EMPTY_DATA); } private void jsonRoute(String _from, byte[] data) { @@ -311,11 +308,7 @@ private void jsonRoute(String _from, byte[] data) { } Address fromToken = Context.getCaller(); - byte[] _data = EMPTY_DATA; - if(params.get("data")!=null){ - _data = params.get("data").asString().getBytes(); - } - route(receiver, fromToken, actions, minimumReceive, _data); + route(receiver, fromToken, actions, minimumReceive, EMPTY_DATA); } @Payable diff --git a/score-lib/src/main/java/network/balanced/score/lib/interfaces/Dex.java b/score-lib/src/main/java/network/balanced/score/lib/interfaces/Dex.java index 78eb9979d..94b745cfc 100644 --- a/score-lib/src/main/java/network/balanced/score/lib/interfaces/Dex.java +++ b/score-lib/src/main/java/network/balanced/score/lib/interfaces/Dex.java @@ -207,5 +207,10 @@ void xAdd(String from, String _baseToken, String _quoteToken, BigInteger _baseVa @External void addLpAddresses(BigInteger _poolId, Address[] _addresses); + @External + void governanceBorrow(Address token, BigInteger amount, Address recipient); + + @External(readonly=true) + BigInteger getGovernanceDebt(Address token); } diff --git a/score-lib/src/main/java/network/balanced/score/lib/interfaces/Loans.java b/score-lib/src/main/java/network/balanced/score/lib/interfaces/Loans.java index 6d94590f9..6e4af666d 100644 --- a/score-lib/src/main/java/network/balanced/score/lib/interfaces/Loans.java +++ b/score-lib/src/main/java/network/balanced/score/lib/interfaces/Loans.java @@ -91,9 +91,6 @@ void depositAndBorrow(@Optional String _asset, @Optional BigInteger _amount, @Op @External void withdrawAndUnstake(BigInteger _value); - @External - void sellCollateral(BigInteger collateralAmountToSell, String collateralSymbol, BigInteger minimumDebtRepaid); - @External void withdrawCollateral(BigInteger _value, @Optional String _collateralSymbol); @@ -151,6 +148,12 @@ void depositAndBorrow(@Optional String _asset, @Optional BigInteger _amount, @Op @External(readonly = true) BigInteger getRedemptionDaoFee(); + @External + void setRedemptionExemption(Address token, boolean exempt); + + @External(readonly = true) + boolean getRedemptionExemption(Address token); + @External void setNewLoanMinimum(BigInteger _minimum); diff --git a/score-lib/src/main/java/network/balanced/score/lib/tokens/HubTokenImpl.java b/score-lib/src/main/java/network/balanced/score/lib/tokens/HubTokenImpl.java index 0da49765b..56ce4d3a4 100644 --- a/score-lib/src/main/java/network/balanced/score/lib/tokens/HubTokenImpl.java +++ b/score-lib/src/main/java/network/balanced/score/lib/tokens/HubTokenImpl.java @@ -87,6 +87,11 @@ public void setSpokeLimit(String networkId, BigInteger limit) { spokeLimits.set(networkId, limit); } + @External(readonly = true) + public BigInteger getSpokeLmit(String networkId) { + return spokeLimits.getOrDefault(networkId, BigInteger.ZERO); + } + @External(readonly = true) public BigInteger xSupply(String net) { return crossChainSupply.getOrDefault(net, BigInteger.ZERO);