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

Update token related calls for Fort Canning Crunch #1265

Merged
merged 2 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
87 changes: 73 additions & 14 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,18 +527,12 @@ class CCustomMetadataParseVisitor : public boost::static_visitor<Res>

Res operator()(CLoanSetCollateralTokenMessage& obj) const {
auto res = isPostFortCanningFork();
if (!res)
return res;
res = isPostFortCanningSpiceGardenFork();
return res ? Res::Err("called after FortCanningSpiceGarden height") : serialize(obj);
return !res ? res : serialize(obj);
}

Res operator()(CLoanSetLoanTokenMessage& obj) const {
auto res = isPostFortCanningFork();
if (!res)
return res;
res = isPostFortCanningSpiceGardenFork();
return res ? Res::Err("called after FortCanningSpiceGarden height") : serialize(obj);
return !res ? res : serialize(obj);
}

Res operator()(CLoanUpdateLoanTokenMessage& obj) const {
Expand Down Expand Up @@ -2310,15 +2304,41 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
if (!res)
return res;

if (!HasFoundationAuth())
return Res::Err("tx not from foundation member!");

if (height >= static_cast<uint32_t>(consensus.FortCanningSpiceGardenHeight))
{
const auto& tokenId = obj.idToken.v;

auto attributes = mnview.GetAttributes();
attributes->time = time;

CDataStructureV0 collateralEnabled{AttributeTypes::Token, tokenId, TokenKeys::LoanCollateralEnabled};
CDataStructureV0 collateralFactor{AttributeTypes::Token, tokenId, TokenKeys::LoanCollateralFactor};
CDataStructureV0 pairKey{AttributeTypes::Token, tokenId, TokenKeys::FixedIntervalPriceId};

attributes->SetValue(collateralEnabled, true);
attributes->SetValue(collateralFactor, obj.factor);
attributes->SetValue(pairKey, obj.fixedIntervalPriceId);

res = attributes->Validate(mnview);
if (!res)
return res;

res = attributes->Apply(mnview, height);
if (!res)
return res;

return mnview.SetVariable(*attributes);
}

CLoanSetCollateralTokenImplementation collToken;
static_cast<CLoanSetCollateralToken&>(collToken) = obj;

collToken.creationTx = tx.GetHash();
collToken.creationHeight = height;

if (!HasFoundationAuth())
return Res::Err("tx not from foundation member!");

auto token = mnview.GetToken(collToken.idToken);
if (!token)
return Res::Err("token %s does not exist!", collToken.idToken.ToString());
Expand Down Expand Up @@ -2356,6 +2376,48 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
if (!res)
return res;

if (!HasFoundationAuth())
return Res::Err("tx not from foundation member!");

if (height >= static_cast<uint32_t>(consensus.FortCanningSpiceGardenHeight))
{
CTokenImplementation token;
token.symbol = trim_ws(obj.symbol).substr(0, CToken::MAX_TOKEN_SYMBOL_LENGTH);
token.name = trim_ws(obj.name).substr(0, CToken::MAX_TOKEN_NAME_LENGTH);
token.creationTx = tx.GetHash();
token.creationHeight = height;
token.flags = obj.mintable ? static_cast<uint8_t>(CToken::TokenFlags::Default) : static_cast<uint8_t>(CToken::TokenFlags::Tradeable);
token.flags |= (uint8_t)CToken::TokenFlags::LoanToken | (uint8_t)CToken::TokenFlags::DAT;

auto resVal = mnview.CreateToken(token);
if ( !resVal) {
return res;
}

const auto& tokenId = resVal.val->v;

auto attributes = mnview.GetAttributes();
attributes->time = time;

CDataStructureV0 mintEnabled{AttributeTypes::Token, tokenId, TokenKeys::LoanMintingEnabled};
CDataStructureV0 mintInterest{AttributeTypes::Token, tokenId, TokenKeys::LoanMintingInterest};
CDataStructureV0 pairKey{AttributeTypes::Token, tokenId, TokenKeys::FixedIntervalPriceId};

attributes->SetValue(mintEnabled, obj.mintable);
attributes->SetValue(mintInterest, obj.interest);
attributes->SetValue(pairKey, obj.fixedIntervalPriceId);

res = attributes->Validate(mnview);
if (!res)
return res;

res = attributes->Apply(mnview, height);
if (!res)
return res;

return mnview.SetVariable(*attributes);
}

CLoanSetLoanTokenImplementation loanToken;
static_cast<CLoanSetLoanToken&>(loanToken) = obj;

Expand All @@ -2377,9 +2439,6 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
if (!resSetFixedPrice)
return Res::Err(resSetFixedPrice.msg);

if (!HasFoundationAuth())
return Res::Err("tx not from foundation member!");

if (!OraclePriceFeed(mnview, loanToken.fixedIntervalPriceId))
return Res::Err("Price feed %s/%s does not belong to any oracle", loanToken.fixedIntervalPriceId.first, loanToken.fixedIntervalPriceId.second);

Expand Down
59 changes: 46 additions & 13 deletions test/functional/feature_loan_setcollateraltoken.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,37 @@ def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-fortcanningheight=50', '-eunosheight=50', '-txindex=1']]
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-eunosheight=50', '-fortcanningheight=50', '-fortcanninghillheight=50', '-fortcanningspicegardenheight=150', '-txindex=1']]

def run_test(self):
assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI

print("Generating initial chain...")
self.nodes[0].generate(101)

symbolDFI = "DFI"
symbolBTC = "BTC"
symbolGOOGL = "GOOGL"

self.nodes[0].createtoken({
"symbol": "BTC",
"name": "BTC token",
"symbol": symbolBTC,
"name": symbolBTC,
"isDAT": True,
"collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress
})

self.nodes[0].generate(1)

symbolDFI = "DFI"
symbolBTC = "BTC"
self.nodes[0].createtoken({
"symbol": symbolGOOGL,
"name": symbolGOOGL,
"isDAT": True,
"collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress
})
self.nodes[0].generate(1)

idDFI = list(self.nodes[0].gettoken(symbolDFI).keys())[0]
idBTC = list(self.nodes[0].gettoken(symbolBTC).keys())[0]
idGOOGL = list(self.nodes[0].gettoken(symbolGOOGL).keys())[0]

try:
self.nodes[0].setcollateraltoken({
Expand All @@ -62,8 +71,10 @@ def run_test(self):

oracle_address1 = self.nodes[0].getnewaddress("", "legacy")
price_feeds1 = [
{"currency": "USD", "token": "DFI"},
{"currency": "USD", "token": "BTC"}]
{"currency": "USD", "token": symbolDFI},
{"currency": "USD", "token": symbolBTC},
{"currency": "USD", "token": symbolGOOGL},
]
oracle_id1 = self.nodes[0].appointoracle(oracle_address1, price_feeds1, 10)
self.nodes[0].generate(1)

Expand All @@ -77,8 +88,10 @@ def run_test(self):
assert("no live oracles for specified request" in errorString)

oracle1_prices = [
{"currency": "USD", "tokenAmount": "1@DFI"},
{"currency": "USD", "tokenAmount": "1@BTC"}]
{"currency": "USD", "tokenAmount": f'1@{symbolDFI}'},
{"currency": "USD", "tokenAmount": f'1@{symbolBTC}'},
{"currency": "USD", "tokenAmount": f'1@{symbolGOOGL}'},
]
timestamp = calendar.timegm(time.gmtime())
self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices)
self.nodes[0].generate(1)
Expand Down Expand Up @@ -123,6 +136,8 @@ def run_test(self):

self.nodes[0].generate(1)

dfi_activation_height = self.nodes[0].getblockcount()

collTokens = self.nodes[0].listcollateraltokens()
assert_equal(len(collTokens), 2)

Expand All @@ -138,6 +153,8 @@ def run_test(self):

self.nodes[0].generate(1)

btc_activation_height = self.nodes[0].getblockcount()

collTokens = self.nodes[0].listcollateraltokens()
assert_equal(len(collTokens), 3)

Expand All @@ -159,13 +176,13 @@ def run_test(self):

assert_equal(collTokens["token"], symbolDFI)
assert_equal(collTokens["factor"], Decimal('0.5'))
assert_equal(collTokens["activateAfterBlock"], 105)
assert_equal(collTokens["activateAfterBlock"], dfi_activation_height)

collTokens = self.nodes[0].getcollateraltoken(idBTC)

assert_equal(collTokens["token"], symbolBTC)
assert_equal(collTokens["factor"], Decimal('0.9'))
assert_equal(collTokens["activateAfterBlock"], 106)
assert_equal(collTokens["activateAfterBlock"], btc_activation_height)

self.nodes[0].generate(30)

Expand All @@ -179,7 +196,7 @@ def run_test(self):

assert_equal(collTokens["token"], symbolBTC)
assert_equal(collTokens["factor"], Decimal('0.9'))
assert_equal(collTokens["activateAfterBlock"], 106)
assert_equal(collTokens["activateAfterBlock"], btc_activation_height)

self.nodes[0].setcollateraltoken({
'token': idBTC,
Expand All @@ -191,5 +208,21 @@ def run_test(self):
collTokens = self.nodes[0].listcollateraltokens()
assert_equal(len(collTokens), 4)

# Move to fork height
self.nodes[0].generate(150 - self.nodes[0].getblockcount())

# Create collateral token
self.nodes[0].setcollateraltoken({
'token': idGOOGL,
'factor': 0.5,
'fixedIntervalPriceId': "GOOGL/USD"})
self.nodes[0].generate(1)

# Check attributess
result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES']
assert_equal(result[f'v0/token/{idGOOGL}/loan_collateral_enabled'], 'true')
assert_equal(result[f'v0/token/{idGOOGL}/loan_collateral_factor'], '0.5')
assert_equal(result[f'v0/token/{idGOOGL}/fixed_interval_price_id'], 'GOOGL/USD')

if __name__ == '__main__':
LoanSetCollateralTokenTest().main()
66 changes: 63 additions & 3 deletions test/functional/feature_loan_setloantoken.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.extra_args = [
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-fortcanningheight=50', '-eunosheight=50', '-txindex=1']]
['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-eunosheight=50', '-fortcanningheight=50', '-fortcanninghillheight=50', '-fortcanningspicegardenheight=110', '-txindex=1']]

def run_test(self):
assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI
Expand Down Expand Up @@ -48,7 +48,11 @@ def run_test(self):
assert("no live oracles for specified request" in errorString)

oracle_address1 = self.nodes[0].getnewaddress("", "legacy")
price_feeds1 = [{"currency": "USD", "token": "TSLA"}]
price_feeds1 = [
{"currency": "USD", "token": "TSLA"},
{"currency": "USD", "token": "GOOGL"},
{"currency": "USD", "token": "AMZN"},
]
oracle_id1 = self.nodes[0].appointoracle(oracle_address1, price_feeds1, 10)
self.nodes[0].generate(1)

Expand All @@ -74,7 +78,11 @@ def run_test(self):
errorString = e.error['message']
assert("price feed not in valid format - token/currency" in errorString)

oracle1_prices = [{"currency": "USD", "tokenAmount": "1@TSLA"}]
oracle1_prices = [
{"currency": "USD", "tokenAmount": "1@TSLA"},
{"currency": "USD", "tokenAmount": "1@GOOGL"},
{"currency": "USD", "tokenAmount": "1@AMZN"},
]
timestamp = calendar.timegm(time.gmtime())
self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices)
self.nodes[0].generate(1)
Expand Down Expand Up @@ -142,5 +150,57 @@ def run_test(self):
'mintable': True,
'interest': 0})

# Move to fork height
self.nodes[0].generate(110 - self.nodes[0].getblockcount())

# Create loan tokens
self.nodes[0].setloantoken({
'symbol': "GOOGL",
'name': "Google",
'fixedIntervalPriceId': "GOOGL/USD",
'mintable': True,
'interest': 0.01})
self.nodes[0].generate(1)

self.nodes[0].setloantoken({
'symbol': "AMZN",
'name': "Amazon",
'fixedIntervalPriceId': "AMZN/USD",
'mintable': False,
'interest': 0.01})
self.nodes[0].generate(1)

# Check tokens
result = self.nodes[0].gettoken("GOOGL")['4']
assert_equal(result['symbol'], 'GOOGL')
assert_equal(result['symbolKey'], 'GOOGL')
assert_equal(result['name'], 'Google')
assert_equal(result['mintable'], True)
assert_equal(result['tradeable'], True)
assert_equal(result['isDAT'], True)
assert_equal(result['isLPS'], False)
assert_equal(result['finalized'], False)
assert_equal(result['isLoanToken'], True)

result = self.nodes[0].gettoken("AMZN")['5']
assert_equal(result['symbol'], 'AMZN')
assert_equal(result['symbolKey'], 'AMZN')
assert_equal(result['name'], 'Amazon')
assert_equal(result['mintable'], False)
assert_equal(result['tradeable'], True)
assert_equal(result['isDAT'], True)
assert_equal(result['isLPS'], False)
assert_equal(result['finalized'], False)
assert_equal(result['isLoanToken'], True)

# Check attributess
result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES']
assert_equal(result['v0/token/4/loan_minting_enabled'], 'true')
assert_equal(result['v0/token/4/loan_minting_interest'], '0.01')
assert_equal(result['v0/token/4/fixed_interval_price_id'], 'GOOGL/USD')
assert_equal(result['v0/token/5/loan_minting_enabled'], 'false')
assert_equal(result['v0/token/5/loan_minting_interest'], '0.01')
assert_equal(result['v0/token/5/fixed_interval_price_id'], 'AMZN/USD')

if __name__ == '__main__':
LoanSetLoanTokenTest().main()
10 changes: 0 additions & 10 deletions test/functional/feature_loan_vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,22 +750,12 @@ def loan_and_collateral_token_to_govvar(self):
self.move_to_gw_fork()

# Try and call disabled RPC calls
assert_raises_rpc_error(-32600, 'called after FortCanningSpiceGarden height', self.nodes[0].setloantoken, {
'symbol': "DUSD",
'name': "DUSD stable token",
'fixedIntervalPriceId': "DUSD/USD",
'mintable': True,
'interest': 1})
assert_raises_rpc_error(-32600, 'called after FortCanningSpiceGarden height', self.nodes[0].updateloantoken, "DUSD", {
'symbol': "DUSD",
'name': "DUSD stable token",
'fixedIntervalPriceId': "DUSD/USD",
'mintable': True,
'interest': 1})
assert_raises_rpc_error(-32600, 'called after FortCanningSpiceGarden height', self.nodes[0].setcollateraltoken, {
'token': self.idDFI,
'factor': 1,
'fixedIntervalPriceId': "DFI/USD"})

# Test setting collateral token partially
self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{self.idETH}/fixed_interval_price_id':'ETH/USD', f'v0/token/{self.idETH}/loan_collateral_enabled':'true'}})
Expand Down