diff --git a/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/CPFTreasury.java b/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/CPFTreasury.java index 28a60e1..55d82b2 100644 --- a/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/CPFTreasury.java +++ b/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/CPFTreasury.java @@ -26,10 +26,14 @@ public class CPFTreasury extends SetterGetter implements CPFTreasuryInterface { public static final VarDB
sICXScore = Context.newVarDB(SICX_SCORE, Address.class); public static final VarDB
routerScore = Context.newVarDB(ROUTER_SCORE, Address.class); public static final VarDB
oracleAddress = Context.newVarDB(ORACLE_ADDRESS, Address.class); + private final ArrayDB proposalsKeys = Context.newArrayDB(PROPOSALS_KEYS, String.class); private final DictDB proposalBudgets = Context.newDictDB(PROPOSAL_BUDGETS, BigInteger.class); + private final VarDB treasuryFund = Context.newVarDB(TREASURY_FUND, BigInteger.class); private final VarDB emergencyFund = Context.newVarDB(EMERGENCY_FUND, BigInteger.class); + private final DictDB rewardPool = Context.newDictDB(REWARD_POOL, BigInteger.class); + private final VarDB treasuryFundbnUSD = Context.newVarDB(TREASURY_FUND_BNUSD, BigInteger.class); private final VarDB swapState = Context.newVarDB(SWAP_STATE, Integer.class); @@ -41,6 +45,7 @@ public class CPFTreasury extends SetterGetter implements CPFTreasuryInterface { public static final VarDB councilFlag = Context.newVarDB(COUNCIL_FLAG, Boolean.class); public static final ArrayDB
councilManagers = Context.newArrayDB(COUNCIL_MANAGERS, Address.class); + public static final DictDB councilManagersReward = Context.newDictDB(COUNCIL_MANAGERS_REWARD, BigInteger.class); public CPFTreasury(@Optional Address cpsScore) { @@ -278,7 +283,7 @@ private void swapIcxBnusd(BigInteger amount, BigInteger _minReceive) { } Context.call(amount, routerScore.get(), "route", path, _minReceive); } catch (Exception e) { - Context.println("Ignoring Errors from Router. Error Message: " + e.getMessage()); + Context.println("Ignoring Errors from Router. Error Message: " + e.getMessage()); } } } @@ -336,7 +341,7 @@ public void withdrawFromEmergencyFund(BigInteger value, Address address, String Address balancedDollar = CPFTreasury.balancedDollar.get(); Context.call(balancedDollar, TRANSFER, address, value, "".getBytes()); - EmergencyFundTransferred(address, value,purpose); + EmergencyFundTransferred(address, value, purpose); } @@ -399,6 +404,70 @@ public void setOraclePercentageDifference(int value) { oraclePerDiff.set(value); } + @External + public void setRewardPool(String key) { + validateCpsScore(); + Context.require(key.equals(INITIAL_FUND) || key.equals(FINAL_FUND), TAG + ": incorrectKeyForPool"); + rewardPool.set(key, getTotalFundBNUSD().get(AVAILABLE_BALANCE)); + } + + @External(readonly = true) + public Map getRewardPool() { + BigInteger finalFund = rewardPool.getOrDefault(FINAL_FUND, BigInteger.ZERO); + BigInteger initialFund = rewardPool.getOrDefault(INITIAL_FUND, BigInteger.ZERO); + BigInteger rewardPool = finalFund.subtract(initialFund).multiply(BigInteger.valueOf(2).divide(BigInteger.valueOf(100))); + return Map.of(INITIAL_FUND, initialFund, + FINAL_FUND, finalFund, + "rewardPool", rewardPool); + } + + @External + public void distributeRewardToFundManagers() { + validateCpsScore(); + + try { + Map rewardPool = getRewardPool(); + BigInteger initialFund = rewardPool.get(INITIAL_FUND); + BigInteger finalFund = rewardPool.get(FINAL_FUND); + + Context.require((initialFund.compareTo(BigInteger.ZERO) > 0 && finalFund.compareTo(BigInteger.ZERO) > 0), + TAG + ": RewardPoolIsEmpty"); + + BigInteger rewardPoolAmount = rewardPool.get("rewardPool"); + + + int len = councilManagers.size(); + BigInteger rewardAmount = rewardPoolAmount.divide(BigInteger.valueOf(len)); + + for (int i = 0; i < len; i++) { + Address manager = councilManagers.get(i); + councilManagersReward.set(manager, councilManagersReward.getOrDefault(manager, BigInteger.ZERO).add(rewardAmount)); + FundManagerRewardSet(manager, rewardAmount); + } + + } catch (Exception e) { + Context.println("Error in distributeRewardToFundManagers: " + e.getMessage()); + } + setRewardPool(INITIAL_FUND); + } + + @External(readonly = true) + public BigInteger getRewardAmountForManager(Address manager) { + return councilManagersReward.getOrDefault(manager, BigInteger.ZERO); + } + + @External + public void claimFundManagerReward() { + Address manager = Context.getCaller(); + BigInteger rewardAmount = councilManagersReward.getOrDefault(manager, BigInteger.ZERO); + Context.require(rewardAmount.compareTo(BigInteger.ZERO) > 0, TAG + ": No reward to claim."); + + councilManagersReward.set(manager, BigInteger.ZERO); + Context.call(balancedDollar.get(), TRANSFER, manager, rewardAmount, "".getBytes()); + FundManagerRewardClaimed(manager, rewardAmount); + + } + @Override @External(readonly = true) public Map getProposalDetails(@Optional int startIndex, @Optional int endIndex) { @@ -489,7 +558,7 @@ public void fallback() { } @External - public void migrateOldHashToNewHash(String oldHash, String newHash){ + public void migrateOldHashToNewHash(String oldHash, String newHash) { validateCpsScore(); int size = proposalsKeys.size(); for (int i = 0; i < size; i++) { @@ -499,12 +568,26 @@ public void migrateOldHashToNewHash(String oldHash, String newHash){ } BigInteger totalBudget = proposalBudgets.get(oldHash); - proposalBudgets.set(oldHash,null); - proposalBudgets.set(newHash,totalBudget); + proposalBudgets.set(oldHash, null); + proposalBudgets.set(newHash, totalBudget); + } + + private static boolean containsInArrayDb(T value, ArrayDB array) { + boolean contains = false; + if (array == null || value == null) { + return contains; + } + + for (int i = 0; i < array.size(); i++) { + if (array.get(i) != null && array.get(i).equals(value)) { + contains = true; + break; + } + } + return contains; } - //EventLogs @Override @EventLog(indexed = 1) @@ -529,4 +612,12 @@ public void FundReceived(Address _sponsor_address, String note) { @EventLog(indexed = 1) public void EmergencyFundTransferred(Address _address, BigInteger _value, String _purpose) { } + + @EventLog(indexed = 1) + public void FundManagerRewardSet(Address _address, BigInteger _value) { + } + + @EventLog(indexed = 1) + public void FundManagerRewardClaimed(Address _address, BigInteger _value) { + } } diff --git a/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/Constants.java b/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/Constants.java index 0645ebc..072d01b 100644 --- a/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/Constants.java +++ b/CPFTreasury/src/main/java/community/icon/cps/score/cpftreasury/Constants.java @@ -17,6 +17,11 @@ public class Constants { public static final String PROPOSALS_KEYS = "_proposals_keys"; public static final String TREASURY_FUND = "treasury_fund"; public static final String EMERGENCY_FUND = "emergencyFund"; + public static final String REWARD_POOL = "rewardPool"; + + public static final String INITIAL_FUND = "initialFund"; + public static final String FINAL_FUND = "finalFund"; + public static final String AVAILABLE_BALANCE = "availableBalance"; public static final String TREASURY_FUND_BNUSD = "treasury_fund_bnusd"; @@ -51,6 +56,7 @@ public class Constants { public static final String COUNCIL_FLAG = "council_flag"; public static final String COUNCIL_MANAGERS = "council_managers"; + public static final String COUNCIL_MANAGERS_REWARD = "councilManagersReward"; public static final int sICXICXPoolID = 1; public static final int sICXBNUSDPoolID = 2; diff --git a/CPSCore/src/main/java/community/icon/cps/score/cpscore/CPSCore.java b/CPSCore/src/main/java/community/icon/cps/score/cpscore/CPSCore.java index 1adcb30..3687cab 100644 --- a/CPSCore/src/main/java/community/icon/cps/score/cpscore/CPSCore.java +++ b/CPSCore/src/main/java/community/icon/cps/score/cpscore/CPSCore.java @@ -1260,6 +1260,7 @@ public void updatePeriod() { period.periodName.set(TRANSITION_PERIOD); period.previousPeriodName.set(APPLICATION_PERIOD); period.updatePeriodIndex.set(updateIndex + 1); + callScore(getCpfTreasuryScore(), "setRewardPool", "finalFund"); updateProposalsResult(); PeriodUpdate("Period Update State 1/4. Period Updated to Transition Period. " + @@ -1292,6 +1293,8 @@ public void updatePeriod() { period.periodCount.set(period.periodCount.getOrDefault(0) + 1); burn(proposalFees.get(), null); proposalFees.set(BigInteger.ZERO); + callScore(getCpfTreasuryScore(), "distributeRewardToFundManagers"); + } } @@ -1430,16 +1433,16 @@ private void updateProgressReportResult() { boolean extended = MilestoneDb.extensionFlag.at(milestonePrefix).getOrDefault(false); int finalPeriodToSubmit = proposalPeriod + completionPeriod; - if (getPeriodCount() < finalPeriodToSubmit){ - milestonePassed +=1; - }else if (getPeriodCount() >= finalPeriodToSubmit && !extended) { - milestonePassed += 1; - String proposalPrefix = proposalPrefix(_ipfs_hash); - int project_duration = (int) _proposal_details.get(PROJECT_DURATION); - MilestoneDb.extensionFlag.at(milestonePrefix).set(true); - ProposalDataDb.projectDuration.at(proposalPrefix).set(project_duration + 1); - MilestoneDb.completionPeriod.at(milestonePrefix).set(completionPeriod + 1); - } + if (getPeriodCount() < finalPeriodToSubmit) { + milestonePassed += 1; + } else if (getPeriodCount() >= finalPeriodToSubmit && !extended) { + milestonePassed += 1; + String proposalPrefix = proposalPrefix(_ipfs_hash); + int project_duration = (int) _proposal_details.get(PROJECT_DURATION); + MilestoneDb.extensionFlag.at(milestonePrefix).set(true); + ProposalDataDb.projectDuration.at(proposalPrefix).set(project_duration + 1); + MilestoneDb.completionPeriod.at(milestonePrefix).set(completionPeriod + 1); + } updateMilestoneDB(milestonePrefix); } } else { @@ -2676,24 +2679,24 @@ public Boolean hasTwoThirdsMajority(String key, boolean isMilestone) { //error: milestonedb doesnt have total votes or voters Map milestoneDbData = MilestoneDb.getDataFromMilestoneDB(key); - BigInteger totalVotes = isMilestone ? (BigInteger)milestoneDbData.get(TOTAL_VOTES) : ProgressReportDataDb.totalVotes.at(key).getOrDefault(BigInteger.ZERO); - int totalVoters = isMilestone ? (Integer)milestoneDbData.get(TOTAL_VOTERS) : ProgressReportDataDb.totalVoters.at(key).getOrDefault(0); - + BigInteger totalVotes = isMilestone ? (BigInteger) milestoneDbData.get(TOTAL_VOTES) : ProgressReportDataDb.totalVotes.at(key).getOrDefault(BigInteger.ZERO); + int totalVoters = isMilestone ? (Integer) milestoneDbData.get(TOTAL_VOTERS) : ProgressReportDataDb.totalVoters.at(key).getOrDefault(0); + BigInteger approveVotes = isMilestone ? MilestoneDb.approvedVotes.at(key).getOrDefault(BigInteger.ZERO) : ProgressReportDataDb.approvedVotes.at(key).getOrDefault(BigInteger.ZERO); int approveVoters = isMilestone ? MilestoneDb.approveVoters.at(key).size() : ProgressReportDataDb.approveVoters.at(key).size(); - + //need to give vote weights = 100 (arbitrary) boolean voteWeightCheck = approveVotes.multiply(BigInteger.valueOf(3)).compareTo(totalVotes.multiply(BigInteger.valueOf(2))) >= 0; boolean voterCountCheck = approveVoters * 3 >= totalVoters * 2; - + return voteWeightCheck && voterCountCheck; } - + public List
getCouncilManagers() { return callScore(List.class, getCpfTreasuryScore(), "getCouncilManagers"); } - + public boolean getCouncilFlag() { return callScore(boolean.class, getCpfTreasuryScore(), "getCouncilFlag"); }