diff --git a/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/AddressBranchDictDB.java b/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/AddressBranchDictDB.java new file mode 100644 index 000000000..2898156d1 --- /dev/null +++ b/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/AddressBranchDictDB.java @@ -0,0 +1,50 @@ +package network.balanced.score.core.stakedlp; + +import foundation.icon.xcall.NetworkAddress; +import score.Address; +import score.BranchDB; +import score.Context; +import score.DictDB; + +import java.math.BigInteger; + +public class AddressBranchDictDB { + + protected BranchDB> legacyAddressDictDB; + protected BranchDB> addressDictDB; + protected BranchDB> migrationDB; + + public AddressBranchDictDB(String id) { + this.legacyAddressDictDB = Context.newBranchDB(id, BigInteger.class); + this.addressDictDB = Context.newBranchDB(id + "_migrated", BigInteger.class); + this.migrationDB = Context.newBranchDB(id + "_migration_db", Boolean.class); + } + + private BigInteger getLegacy(Address address, BigInteger key) { + return legacyAddressDictDB.at(address).getOrDefault(key, BigInteger.ZERO); + } + + public Boolean isMigrated(Address address, BigInteger key) { + return migrationDB.at(address).getOrDefault(key, false); + } + + public BigInteger get(NetworkAddress address, BigInteger id, boolean readonly) { + BigInteger total = addressDictDB.at(address.toString()).getOrDefault(id, BigInteger.ZERO); + if (address.account().startsWith("hx") || address.account().startsWith("cx")) { + Address iconAddr = Address.fromString(address.account()); + if (!isMigrated(iconAddr, id)) { + total = total.add(getLegacy(iconAddr, id)); + if (!readonly) { + migrationDB.at(iconAddr).set(id, true); + } + } + } + + return total; + } + + public void set(NetworkAddress address, BigInteger id, BigInteger value) { + addressDictDB.at(address.toString()).set(id, value); + } + +} diff --git a/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/StakedLPImpl.java b/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/StakedLPImpl.java index 4d3366ef5..3e38292ec 100644 --- a/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/StakedLPImpl.java +++ b/core-contracts/StakedLP/src/main/java/network/balanced/score/core/stakedlp/StakedLPImpl.java @@ -35,8 +35,7 @@ public class StakedLPImpl implements StakedLP { - final static AddressBranchDictDB poolStakedDetails = new AddressBranchDictDB<>("poolStakeDetails", - BigInteger.class); + final static AddressBranchDictDB poolStakedDetails = new AddressBranchDictDB("poolStakeDetails"); private static final DictDB totalStakedAmount = Context.newDictDB("totalStaked", BigInteger.class); @@ -125,13 +124,13 @@ public void setRewards(Address rewards) { @External(readonly = true) public BigInteger balanceOf(Address _owner, BigInteger _id) { NetworkAddress owner = new NetworkAddress(NATIVE_NID, _owner.toString()); - return poolStakedDetails.at(owner).getOrDefault(_id, BigInteger.ZERO); + return poolStakedDetails.get(owner, _id, true); } @External(readonly = true) public BigInteger xBalanceOf(String _owner, BigInteger _id) { NetworkAddress owner = NetworkAddress.valueOf(_owner, NATIVE_NID); - return poolStakedDetails.at(owner).getOrDefault(_id, BigInteger.ZERO); + return poolStakedDetails.get(owner, _id, true); } @External(readonly = true) @@ -160,7 +159,8 @@ public void unstake(BigInteger id, BigInteger value) { private void unstake(BigInteger id, NetworkAddress user, BigInteger value) { Context.require(value.compareTo(BigInteger.ZERO) > 0, "StakedLP: Cannot unstake less than zero value"); - BigInteger previousBalance = poolStakedDetails.at(user).getOrDefault(id, BigInteger.ZERO); + BigInteger previousBalance = poolStakedDetails.get(user, id, false); + BigInteger previousTotal = totalStaked(id); String poolName = getSourceName(id); @@ -171,7 +171,7 @@ private void unstake(BigInteger id, NetworkAddress user, BigInteger value) { BigInteger newTotal = previousTotal.subtract(value); Context.require(newBalance.signum() >= 0 && newTotal.signum() >= 0, "StakedLP: New staked balance of user and" + " total amount can't be negative"); - poolStakedDetails.at(user).set(id, newBalance); + poolStakedDetails.set(user, id, newBalance); totalStakedAmount.set(id, newTotal); Unstake(user.toString(), id, value); @@ -263,11 +263,11 @@ private void stake(NetworkAddress user, BigInteger id, BigInteger value) { "StakedLP: Cannot stake less than zero, value to stake " + value); String poolName = getSourceName(id); // Compute and store changes - BigInteger previousBalance = poolStakedDetails.at(user).getOrDefault(id, BigInteger.ZERO); + BigInteger previousBalance = poolStakedDetails.get(user, id, false); BigInteger previousTotal = totalStaked(id); BigInteger newBalance = previousBalance.add(value); BigInteger newTotal = previousTotal.add(value); - poolStakedDetails.at(user).set(id, newBalance); + poolStakedDetails.set(user, id, newBalance); totalStakedAmount.set(id, newTotal); Stake(user.toString(), id, value); diff --git a/core-contracts/StakedLP/src/test/java/network/balanced/score/core/stakedlp/DBTest.java b/core-contracts/StakedLP/src/test/java/network/balanced/score/core/stakedlp/DBTest.java new file mode 100644 index 000000000..d7295dd85 --- /dev/null +++ b/core-contracts/StakedLP/src/test/java/network/balanced/score/core/stakedlp/DBTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022-2022 Balanced.network. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package network.balanced.score.core.stakedlp; + +import com.iconloop.score.test.Account; +import com.iconloop.score.test.Score; +import com.iconloop.score.test.ServiceManager; +import com.iconloop.score.test.TestBase; + +import foundation.icon.xcall.NetworkAddress; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import score.Address; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.*; + +public class DBTest extends TestBase { + + private static final ServiceManager sm = getServiceManager(); + private static final Account owner = sm.createAccount(); + private static Score dummyScore; + + public static class DummyScore extends AddressBranchDictDB { + + public DummyScore() { + super("test"); + } + + public void setLegacy(Address address, BigInteger key, BigInteger value) { + legacyAddressDictDB.at(address).set(key, value); + } + } + + @BeforeAll + public static void setup() throws Exception { + dummyScore = sm.deploy(owner, DummyScore.class); + } + + @Test + public void onlyLegacyValue() { + // Arrange + Address user = sm.createAccount().getAddress(); + BigInteger id = BigInteger.ONE; + BigInteger value = BigInteger.TEN; + dummyScore.invoke(owner, "setLegacy", user, id, value); + + // Act + BigInteger balance = (BigInteger) dummyScore.call("get", new NetworkAddress("test", user), id, true); + + // Assert + assertEquals(balance, value); + } + + @Test + public void bothValues() { + // Arrange + Address user = sm.createAccount().getAddress(); + BigInteger id = BigInteger.ONE; + BigInteger legacyValue = BigInteger.TEN; + BigInteger value = BigInteger.TWO; + dummyScore.invoke(owner, "setLegacy", user, id, legacyValue); + dummyScore.invoke(owner, "set", new NetworkAddress("test", user), id, value); + + // Act + BigInteger balance = (BigInteger) dummyScore.call("get", new NetworkAddress("test", user), id, true); + + // Assert + assertEquals(balance, legacyValue.add(value)); + } + + @Test + public void migrateValue() { + // Arrange + Address user = sm.createAccount().getAddress(); + BigInteger id = BigInteger.ONE; + BigInteger legacyValue = BigInteger.TEN; + BigInteger value = BigInteger.TWO; + dummyScore.invoke(owner, "setLegacy", user, id, legacyValue); + dummyScore.invoke(owner, "set", new NetworkAddress("test", user), id, value); + + // Act + dummyScore.invoke(owner, "get", new NetworkAddress("test", user), id, false); + + // Assert + // In a real scenario set would be called, but for this the value is simply + // marked as migrated and wont be used anymore + BigInteger balance = (BigInteger) dummyScore.call("get", new NetworkAddress("test", user), id, true); + assertEquals(balance, value); + } + + @Test + public void migrateContractValue() { + // Arrange + Address user = dummyScore.getAddress(); + BigInteger id = BigInteger.ONE; + BigInteger legacyValue = BigInteger.TEN; + BigInteger value = BigInteger.TWO; + dummyScore.invoke(owner, "setLegacy", user, id, legacyValue); + dummyScore.invoke(owner, "set", new NetworkAddress("test", user), id, value); + + // Act + dummyScore.invoke(owner, "get", new NetworkAddress("test", user), id, false); + + // Assert + // In a real scenario set would be called, but for this the value is simply + // marked as migrated and wont be used anymore + BigInteger balance = (BigInteger) dummyScore.call("get", new NetworkAddress("test", user), id, true); + assertEquals(balance, value); + } + + @Test + public void onlyNew() { + // Arrange + Address user = sm.createAccount().getAddress(); + BigInteger id = BigInteger.ONE; + BigInteger value = BigInteger.TWO; + dummyScore.invoke(owner, "set", new NetworkAddress("test", user), id, value); + + // Act + BigInteger balance = (BigInteger) dummyScore.call("get", new NetworkAddress("test", user), id, true); + + // Assert + assertEquals(balance, value); + } + + @Test + public void networkAddress() { + // Arrange + NetworkAddress user = new NetworkAddress("test", "test"); + BigInteger id = BigInteger.ONE; + BigInteger value = BigInteger.TWO; + dummyScore.invoke(owner, "set", user, id, value); + + // Act + BigInteger balance = (BigInteger) dummyScore.call("get", user, id, true); + + // Assert + assertEquals(balance, value); + } + +} diff --git a/score-lib/src/main/java/network/balanced/score/lib/utils/AddressBranchDictDB.java b/score-lib/src/main/java/network/balanced/score/lib/utils/AddressBranchDictDB.java deleted file mode 100644 index c2d097f23..000000000 --- a/score-lib/src/main/java/network/balanced/score/lib/utils/AddressBranchDictDB.java +++ /dev/null @@ -1,35 +0,0 @@ -package network.balanced.score.lib.utils; - -import foundation.icon.xcall.NetworkAddress; -import score.Address; -import score.BranchDB; -import score.Context; -import score.DictDB; - - -public class AddressBranchDictDB { - - private BranchDB> legacyAddressDictDB; - private BranchDB> addressDictDB; - - public AddressBranchDictDB(String id, Class valueClass) { - this.legacyAddressDictDB = Context.newBranchDB(id, valueClass); - this.addressDictDB = Context.newBranchDB(id + "_migrated", valueClass); - } - - - public DictDB at(NetworkAddress key) { - DictDB value = addressDictDB.at(key.toString()); - if (value != null) { - return value; - } - - if (key.account().startsWith("hx") || key.account().startsWith("cx")) { - value = legacyAddressDictDB.at(Address.fromString(key.account())); - } - - - return value; - } - -} diff --git a/score-lib/src/main/java/network/balanced/score/lib/utils/Versions.java b/score-lib/src/main/java/network/balanced/score/lib/utils/Versions.java index 4603f0633..38bfaa68d 100644 --- a/score-lib/src/main/java/network/balanced/score/lib/utils/Versions.java +++ b/score-lib/src/main/java/network/balanced/score/lib/utils/Versions.java @@ -17,7 +17,7 @@ package network.balanced.score.lib.utils; public class Versions { - public final static String BALN = "v1.1.0"; + public final static String BALN = "v1.1.2"; public final static String DIVIDENDS = "v1.0.0"; public final static String LOANS = "v1.2.3"; public final static String RESERVE = "v1.0.0"; @@ -34,7 +34,7 @@ public class Versions { public final static String GOVERNANCE = "v1.0.2"; public final static String REBALANCING = "v1.0.0"; public final static String ROUTER = "v1.1.8"; - public final static String STAKEDLP = "v1.0.8"; + public final static String STAKEDLP = "v1.0.9"; public final static String BOOSTED_BALN = "v1.1.0"; public final static String BRIBING = "v1.0.1"; public final static String BALANCED_OTC = "v1.0.0";