Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: icon bridge migration #872

Merged
merged 6 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,20 @@ public BigInteger getSn() {
return sn.get();
}

/**
* Transfer ownership of token contract to somewhere else
*
* @param _name Reegistered name of the token
* @param to New owner
*/
@External
public void transferOwnership(String _name, Address to) {
requireOwnerAccess();
Address tokenAddr = coinAddresses.get(_name);
Context.require(tokenAddr != null, "Token not registered");
Context.call(ZERO_SCORE_ADDRESS, "setScoreOwner", tokenAddr, to);
}

/**
* Add users to blacklist on certain networks
* Maintains information of all addresses blacklisted on all chains
Expand Down Expand Up @@ -484,6 +498,12 @@ public void tokenFallback(Address _from, BigInteger _value, byte[] _data) {
checkUintLimit(_value);
String _coinName = coinAddressName.get(Context.getCaller());
if (_coinName != null && !Context.getAddress().equals(_from)) {

// icon bridge migration checks
requireNotEth(_coinName);
Coin coin = coinDb.get(_coinName);
require(coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE, "Cannnot transfer icon tokens anymore.");

Context.require(coinAddresses.get(_coinName) != null, "CoinNotExists");
Balance _userBalance = getBalance(_coinName, _from);
_userBalance.setUsable(_userBalance.getUsable().add(_value));
Expand Down Expand Up @@ -547,6 +567,10 @@ public void reclaim(String _coinName, BigInteger _value) {
@Payable
@External
public void transferNativeCoin(String _to) {

// icon bridge migration checks
Context.revert("Cannot transfer ICX.");

Context.require(_to.length() < 100, "Length Check");

BigInteger value = Context.getValue();
Expand Down Expand Up @@ -580,6 +604,11 @@ public void transfer(String _coinName, BigInteger _value, String _to) {
require(isRegistered(_coinName), "Not supported Token");
Context.require(_to.length() < 100, "Length Check");

// icon bridge migration checks
requireNotEth(_coinName);
Coin coin = coinDb.get(_coinName);
require(coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE, "Cannnot transfer icon tokens anymore.");

Address owner = Context.getCaller();
BTPAddress to = BTPAddress.valueOf(_to);
checkRestrictions(_coinName, Context.getCaller().toString(), to, _value);
Expand Down Expand Up @@ -613,6 +642,7 @@ public void transferBatch(String[] _coinNames, BigInteger[] _values, String _to)

int tempLen = len;
if (icxValue != null && icxValue.compareTo(BigInteger.ZERO) > 0) {
Context.revert("Cannot transfer ICX.");
tempLen = tempLen + 1;
checkTokenLimit(name, icxValue);
coinNameList.add(name);
Expand All @@ -629,6 +659,12 @@ public void transferBatch(String[] _coinNames, BigInteger[] _values, String _to)
for (int i = 0; i < len; i++) {
String coinName = _coinNames[i];
BigInteger value = _values[i];

// icon bridge migration checks
requireNotEth(coinName);
Coin coin = coinDb.get(coinName);
require(coin.getCoinType() == NATIVE_WRAPPED_COIN_TYPE, "Cannnot transfer icon tokens anymore.");

require(!name.equals(coinName) && this.coinNames.contains(coinName), "Not supported Token");
require(value != null && value.compareTo(BigInteger.ZERO) > 0, "Invalid amount");
checkUintLimit(value);
Expand All @@ -637,6 +673,8 @@ public void transferBatch(String[] _coinNames, BigInteger[] _values, String _to)
checkTokenLimit(coinName, value);
}

System.out.println("yaha aayo?");

transferFromBatch(owner, Context.getAddress(), _coinNames, _values);

sendRequest(owner, to, coinNameList, values);
Expand Down Expand Up @@ -1501,4 +1539,8 @@ private void checkUintLimit(BigInteger value) {
require(UINT_CAP.compareTo(value) >= 0, "Value cannot exceed uint(256)-1");
}

private void requireNotEth(String name) {
String ethName = "btp-0x38.bsc-ETH";
require(!name.equals(ethName), "NotETH");
}
}
198 changes: 198 additions & 0 deletions javascore/bts/src/test/java/foundation/icon/btp/bts/BTSTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.function.Executable;
Expand Down Expand Up @@ -281,6 +282,7 @@ public void operationsOnUnRegisteredToken() {

@Test
@Order(7)
@Disabled
public void operationOnRegisteredTokens() {
register();

Expand All @@ -298,6 +300,7 @@ public void operationOnRegisteredTokens() {

@Test
@Order(8)
@Disabled
public void reclaimDepositedTokens() {
// for IRC2 tokens
register();
Expand Down Expand Up @@ -338,6 +341,7 @@ public void reclaimDepositedTokens() {

@Test
@Order(9)
@Disabled
public void transferNativeCoin() {
sendBTPMessageMock();
String btpAddress = generateBTPAddress(METACHAIN, owner.getAddress().toString());
Expand Down Expand Up @@ -381,6 +385,7 @@ public void transferNativeCoin() {

@Test
@Order(9)
@Disabled
public void transferNativeCoinResponse() {
sendBTPMessageMock();
String btpAddress = generateBTPAddress(METACHAIN, owner.getAddress().toString());
Expand Down Expand Up @@ -451,6 +456,7 @@ public void transferNativeCoinResponse() {

@Test
@Order(10)
@Disabled
public void transfer() {
String btpaddr = generateBTPAddress("harmony", ETH_ADDR);

Expand Down Expand Up @@ -488,6 +494,7 @@ public void transfer() {

@Test
@Order(10)
@Disabled
public void transferResponseBack() {
sendBTPMessageMock();
String btpAddress = generateBTPAddress(METACHAIN, owner.getAddress().toString());
Expand Down Expand Up @@ -559,6 +566,7 @@ public void transferResponseBack() {

@Test
@Order(11)
@Disabled
public void transferBatch() {
String[] coinNames = new String[]{"Token1", "Token2", "Token3", "Token4", PARA};
BigInteger val = BigInteger.valueOf(10);
Expand Down Expand Up @@ -718,6 +726,7 @@ public void handleBTPMessage1() {

@Test
@Order(13)
@Disabled
public void handleBTPMessage2() {

// request plus response
Expand Down Expand Up @@ -1086,6 +1095,7 @@ public void coinDetails() {

@Test
@Order(20)
@Disabled
public void transferBatchNativecoinTokenLimit() {
tokenLimitBTPMessage();
score.invoke(owner, "setTokenLimit", new String[]{ICON}, new BigInteger[]{BigInteger.valueOf(1000)});
Expand Down Expand Up @@ -1121,6 +1131,7 @@ public void transferBatchNativecoinTokenLimit() {

@Test
@Order(20)
@Disabled
public void handleFeeGathering() {
String feeAggregator = generateBTPAddress("icon", "hx0000000000000000000000000000000000000000");

Expand Down Expand Up @@ -1301,4 +1312,191 @@ void setTokenLimitResponse() {
assertEquals( true, score.call("tokenLimitStatus", "icon", ICON));
assertEquals( true, score.call("tokenLimitStatus", "icon", PARA));
}

// ICON BRIDGE MIGRATION TESTS

@Test
public void preventICXTransfer() {
sendBTPMessageMock();
String btpAddress = generateBTPAddress(METACHAIN, owner.getAddress().toString());
// general condition
contextMock.when(sendICX()).thenReturn(BigInteger.valueOf(100));

Executable call = () -> score.invoke(owner, "transferNativeCoin", btpAddress);
expectErrorMessage(call, "Reverted(0): Cannot transfer ICX.");
}

@Test
@Disabled
public void preventIconTokenTransfer() {
sendBTPMessageMock();
String btpaddr = generateBTPAddress(METACHAIN, owner.getAddress().toString());

register();

// should not transfer native-coin
Executable call = () -> score.invoke(nonOwner, "transfer", TEST_TOKEN, BigInteger.TEN, btpaddr);
expectErrorMessage(call, "Cannnot transfer icon tokens anymore.");

}

@Test
public void preventEthTokenTransfer() {
sendBTPMessageMock();
String btpaddr = generateBTPAddress(METACHAIN, owner.getAddress().toString());

String tokenName = "btp-0x38.bsc-ETH";

Verification deployWrappedToken = () -> Context.deploy(any(), eq(tokenName),
eq(tokenName),eq(18));
contextMock.when(deployWrappedToken).thenReturn(wrappedIRC2.getAddress());

score.invoke(owner, "register",tokenName, tokenName, 18, BigInteger.ZERO, BigInteger.TWO,
Address.fromString("cx0000000000000000000000000000000000000000"));


// should not transfer native-coin
Executable call = () -> score.invoke(nonOwner, "transfer", tokenName, BigInteger.TEN, btpaddr);
expectErrorMessage(call, "NotETH");

}

@Test
public void migrationRestrictionOnTransferBatch_includeICX() {

String[] coinNames = new String[]{"Token1", "Token2", "Token3", "Token4", PARA};
BigInteger val = BigInteger.valueOf(10);
BigInteger[] values = new BigInteger[]{val, val, val, val, val};
String destination = generateBTPAddress("harmony", ETH_ADDR);

Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), eq("sendMessage"), eq("harmony"),
eq("bts"), eq(BigInteger.ONE), any());
contextMock.when(sendMessage).thenReturn(null);


contextMock.when(sendICX()).thenReturn(BigInteger.valueOf(100));

// register tokens
Account token1 = Account.newScoreAccount(10);
Account token2 = Account.newScoreAccount(11);
Account token3 = Account.newScoreAccount(12);
Account token4 = Account.newScoreAccount(13);

// register irc2 token
register(coinNames[0], token1.getAddress());
register(coinNames[1], token2.getAddress());
register(coinNames[2], token3.getAddress());
register(coinNames[3], token4.getAddress());


Executable call = () -> score.invoke(nonOwner, "transferBatch", coinNames, values, destination);
expectErrorMessage(call, "Reverted(0): Cannot transfer ICX.");

}

@Test
public void migrationRestrictionOnTransferBatch_includeETHTokens() {
String tokenName = "btp-0x38.bsc-ETH";

String[] coinNames = new String[]{tokenName,"Token1", "Token2", "Token3", "Token4", PARA};
BigInteger val = BigInteger.valueOf(10);
BigInteger[] values = new BigInteger[]{val, val, val, val, val, val};
String destination = generateBTPAddress("harmony", ETH_ADDR);

Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), eq("sendMessage"), eq("harmony"),
eq("bts"), eq(BigInteger.ONE), any());
contextMock.when(sendMessage).thenReturn(null);


// register tokens
Account token1 = Account.newScoreAccount(10);
Account token2 = Account.newScoreAccount(11);
Account token3 = Account.newScoreAccount(12);
Account token4 = Account.newScoreAccount(13);

// register irc2 token
register(coinNames[1], token1.getAddress());
register(coinNames[2], token2.getAddress());
register(coinNames[3], token3.getAddress());
register(coinNames[4], token4.getAddress());

Verification deployWrappedToken = () -> Context.deploy(any(), eq(tokenName),
eq(tokenName),eq(18));
contextMock.when(deployWrappedToken).thenReturn(wrappedIRC2.getAddress());

score.invoke(owner, "register",tokenName, tokenName, 18, BigInteger.ZERO, BigInteger.TWO,
Address.fromString("cx0000000000000000000000000000000000000000"));


Executable call = () -> score.invoke(nonOwner, "transferBatch", coinNames, values, destination);
expectErrorMessage(call, "NotETH");
}

@Test
public void migrationRestrictionOnTransferBatch_includeWrappedTokensOnly() {
String tokenName = "META";

String[] coinNames = new String[]{tokenName, PARA};
BigInteger val = BigInteger.valueOf(10);
BigInteger[] values = new BigInteger[]{val, val};
String destination = generateBTPAddress("harmony", ETH_ADDR);

Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), eq("sendMessage"), eq("harmony"),
eq("bts"), eq(BigInteger.ONE), any());
contextMock.when(sendMessage).thenReturn(null);

registerWrapped();

Verification deployWrappedToken = () -> Context.deploy(any(), eq(tokenName),
eq(tokenName),eq(18));
contextMock.when(deployWrappedToken).thenReturn(wrappedIRC2.getAddress());

score.invoke(owner, "register",tokenName, tokenName, 18, BigInteger.ZERO, BigInteger.TWO,
Address.fromString("cx0000000000000000000000000000000000000000"));

// PARA and META registered

Verification transferFromMock = () -> Context.call(eq(Boolean.class), any(), eq("transferFrom"),
eq(nonOwner.getAddress()), eq(score.getAddress()), eq(BigInteger.valueOf(10)), any());
contextMock.when(transferFromMock).thenReturn(true);


score.invoke(nonOwner, "transferBatch", coinNames, values, destination);
}

@Test
public void migrationRestrictionOnTransferBatch_includeIconTokens() {

String[] coinNames = new String[]{"Token1", "Token2", "Token3", "Token4", PARA};
BigInteger val = BigInteger.valueOf(10);
BigInteger[] values = new BigInteger[]{val, val, val, val, val};
String destination = generateBTPAddress("harmony", ETH_ADDR);

Verification sendMessage = () -> Context.call(eq(bmcMock.getAddress()), eq("sendMessage"), eq("harmony"),
eq("bts"), eq(BigInteger.ONE), any());
contextMock.when(sendMessage).thenReturn(null);


// register tokens
Account token1 = Account.newScoreAccount(10);
Account token2 = Account.newScoreAccount(11);
Account token3 = Account.newScoreAccount(12);
Account token4 = Account.newScoreAccount(13);

// register irc2 token
register(coinNames[0], token1.getAddress());
register(coinNames[1], token2.getAddress());
register(coinNames[2], token3.getAddress());
register(coinNames[3], token4.getAddress());


Executable call = () -> score.invoke(nonOwner, "transferBatch", coinNames, values, destination);
expectErrorMessage(call, "Cannnot transfer icon tokens anymore.");
}






}
Loading
Loading