Skip to content

Commit

Permalink
Update token related calls for Fort Canning Crunch (#1265)
Browse files Browse the repository at this point in the history
* Update setloantoken for Spice Garden

* Update setcollateraltoken for Spice Garden
  • Loading branch information
Bushstar authored May 23, 2022
1 parent e4686b1 commit d9f3a2f
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 40 deletions.
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

0 comments on commit d9f3a2f

Please sign in to comment.