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

Refund future swaps on split #1290

Merged
merged 3 commits into from
May 25, 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
3 changes: 2 additions & 1 deletion src/masternodes/accounts.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ class CAccountsView : public virtual CStorageView
ResVal<CFuturesUserValue> GetFuturesUserValues(const CFuturesUserKey& key);
Res EraseFuturesUserValues(const CFuturesUserKey& key);
boost::optional<uint32_t> GetMostRecentFuturesHeight();
void ForEachFuturesUserValues(std::function<bool(const CFuturesUserKey&, const CFuturesUserValue&)> callback, const CFuturesUserKey& start = {});
void ForEachFuturesUserValues(std::function<bool(const CFuturesUserKey&, const CFuturesUserValue&)> callback, const CFuturesUserKey& start =
{std::numeric_limits<uint32_t>::max(), {}, std::numeric_limits<uint32_t>::max()});

// tags
struct ByBalanceKey { static constexpr uint8_t prefix() { return 'a'; } };
Expand Down
7 changes: 3 additions & 4 deletions src/masternodes/govvariables/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,12 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI
static const std::map<uint8_t, std::string>& displayOracleIDs();
static const std::map<uint8_t, std::map<uint8_t, std::string>>& displayKeys();

Res RefundFuturesContracts(CCustomCSView &mnview, const uint32_t height, const uint32_t tokenID = std::numeric_limits<uint32_t>::max());

private:
friend class CGovView;
bool futureBlockUpdated{};
std::set<uint32_t> tokenSplits{};
std::set<CAttributeType> changed;
std::map<CAttributeType, CAttributeValue> attributes;

Expand All @@ -247,10 +250,6 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator<GovVariable, ATTRI

Res ProcessVariable(const std::string& key, const std::string& value,
std::function<Res(const CAttributeType&, const CAttributeValue&)> applyVariable);
Res RefundFuturesContracts(CCustomCSView &mnview, const uint32_t height, const uint32_t tokenID = std::numeric_limits<uint32_t>::max());

private:
std::set<uint32_t> tokenSplits{};
};

#endif // DEFI_MASTERNODES_GOVVARIABLES_ATTRIBUTES_H
8 changes: 8 additions & 0 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1514,11 +1514,19 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
if (!loanToken) {
return Res::Err("Could not get destination loan token %d. Set valid destination.", obj.destination);
}

if (mnview.AreTokensLocked({obj.destination})) {
return Res::Err("Cannot create future swap for locked token");
}
} else {
if (obj.destination != 0) {
return Res::Err("Destination should not be set when source amount is a dToken");
}

if (mnview.AreTokensLocked({obj.source.nTokenId.v})) {
return Res::Err("Cannot create future swap for locked token");
}

CDataStructureV0 tokenKey{AttributeTypes::Token, obj.source.nTokenId.v, TokenKeys::DFIP2203Enabled};
const auto enabled = attributes->GetValue(tokenKey, true);
if (!enabled) {
Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/rpc_accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2262,7 +2262,7 @@ UniValue listpendingfutureswaps(const JSONRPCRequest& request) {
listFutures.push_back(value);

return true;
}, {std::numeric_limits<uint32_t>::max(), {}, std::numeric_limits<uint32_t>::max()});
});

return listFutures;
}
Expand Down
9 changes: 8 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4230,6 +4230,13 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin

auto view{cache};

// Refund affected future swaps
auto res = attributes->RefundFuturesContracts(view, std::numeric_limits<uint32_t>::max(), id);
if (!res) {
LogPrintf("Token split failed on refunding futures: %s\n", res.msg);
continue;
}

const DCT_ID oldTokenId{id};

auto token = view.GetToken(oldTokenId);
Expand All @@ -4239,7 +4246,7 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin
}

std::string newTokenSuffix = "/v";
auto res = GetTokenSuffix(cache, *attributes, oldTokenId.v, newTokenSuffix);
res = GetTokenSuffix(cache, *attributes, oldTokenId.v, newTokenSuffix);
if (!res) {
LogPrintf("Token split failed on GetTokenSuffix %s\n", res.msg);
continue;
Expand Down
73 changes: 73 additions & 0 deletions test/functional/feature_token_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def run_test(self):
self.setup_test_vaults()
self.vault_split()
self.check_govvar_deletion()
self.check_future_swap_refund()

def setup_test_tokens(self):
self.nodes[0].generate(101)
Expand All @@ -40,6 +41,7 @@ def setup_test_tokens(self):
self.symbolTSLA = 'TSLA'
self.symbolGOOGL = 'GOOGL'
self.symbolNVDA = 'NVDA'
self.symbolMSFT = 'MSFT'
self.symbolGD = 'GOOGL-DUSD'

# Store address
Expand All @@ -52,6 +54,7 @@ def setup_test_tokens(self):
{"currency": "USD", "token": self.symbolGOOGL},
{"currency": "USD", "token": self.symbolTSLA},
{"currency": "USD", "token": self.symbolNVDA},
{"currency": "USD", "token": self.symbolMSFT},
]

# Appoint oracle
Expand All @@ -66,6 +69,7 @@ def setup_test_tokens(self):
{"currency": "USD", "tokenAmount": f"1@{self.symbolGOOGL}"},
{"currency": "USD", "tokenAmount": f"1@{self.symbolTSLA}"},
{"currency": "USD", "tokenAmount": f"1@{self.symbolNVDA}"},
{"currency": "USD", "tokenAmount": f"1@{self.symbolMSFT}"},
]
self.nodes[0].setoracledata(oracle, int(time.time()), oracle_prices)
self.nodes[0].generate(10)
Expand Down Expand Up @@ -107,6 +111,15 @@ def setup_test_tokens(self):
})
self.nodes[0].generate(1)

self.nodes[0].setloantoken({
'symbol': self.symbolMSFT,
'name': self.symbolMSFT,
'fixedIntervalPriceId': f"{self.symbolMSFT}/USD",
'mintable': True,
'interest': 0
})
self.nodes[0].generate(1)

# Set collateral tokens
self.nodes[0].setcollateraltoken({
'token': self.symbolDFI,
Expand Down Expand Up @@ -141,6 +154,7 @@ def setup_test_tokens(self):
self.idGOOGL = list(self.nodes[0].gettoken(self.symbolGOOGL).keys())[0]
self.idTSLA = list(self.nodes[0].gettoken(self.symbolTSLA).keys())[0]
self.idNVDA = list(self.nodes[0].gettoken(self.symbolNVDA).keys())[0]
self.idMSFT = list(self.nodes[0].gettoken(self.symbolMSFT).keys())[0]

def setup_test_pools(self):

Expand Down Expand Up @@ -678,6 +692,65 @@ def check_govvar_deletion(self):
# Swap old for new values
self.idTSLA = list(self.nodes[0].gettoken(self.symbolTSLA).keys())[0]

def check_future_swap_refund(self):

# Set all futures attributes but set active to false
self.nodes[0].setgov({"ATTRIBUTES":{
'v0/params/dfip2203/reward_pct':'0.05',
'v0/params/dfip2203/block_period':'2880'
}})
self.nodes[0].generate(1)

self.nodes[0].setgov({"ATTRIBUTES":{
'v0/params/dfip2203/active':'true'
}})
self.nodes[0].generate(1)

# Create addresses for futures
address_msft = self.nodes[0].getnewaddress("", "legacy")
address_locked = self.nodes[0].getnewaddress("", "legacy")

# Fund addresses
self.nodes[0].minttokens([f'2@{self.idMSFT}', f'2@{self.idDUSD}'])
self.nodes[0].generate(1)
self.nodes[0].accounttoaccount(self.address, {address_msft: [f'1@{self.symbolMSFT}', f'1@{self.symbolDUSD}']})
self.nodes[0].accounttoaccount(self.address, {address_locked: [f'1@{self.symbolMSFT}', f'1@{self.symbolDUSD}']})
self.nodes[0].generate(1)

# Create user futures contracts
self.nodes[0].futureswap(address_msft, f'1@{self.symbolDUSD}', int(self.idMSFT))
self.nodes[0].generate(1)
self.nodes[0].futureswap(address_msft, f'1@{self.symbolMSFT}')
self.nodes[0].generate(1)

# Check pending swaps
result = self.nodes[0].listpendingfutureswaps()
assert_equal(result[0]['owner'], address_msft)
assert_equal(result[0]['source'], f'1.00000000@{self.symbolMSFT}')
assert_equal(result[0]['destination'], self.symbolDUSD)
assert_equal(result[1]['owner'], address_msft)
assert_equal(result[1]['source'], f'1.00000000@{self.symbolDUSD}')
assert_equal(result[1]['destination'], self.symbolMSFT)

# Check balance empty
result = self.nodes[0].getaccount(address_msft)
assert_equal(result, [])

# Token split
self.nodes[0].setgov({"ATTRIBUTES":{f'v0/oracles/splits/{str(self.nodes[0].getblockcount() + 2)}':f'{self.idMSFT}/2'}})
self.nodes[0].generate(1)

# Test creating future swaps with locked tokens
assert_raises_rpc_error(-32600, 'Cannot create future swap for locked token', self.nodes[0].futureswap, address_locked, f'1@{self.symbolDUSD}', int(self.idMSFT))
assert_raises_rpc_error(-32600, 'Cannot create future swap for locked token', self.nodes[0].futureswap, address_locked, f'1@{self.symbolMSFT}')

# Move to split block
self.nodes[0].generate(1)

# Check balance returned with multiplier applied
result = self.nodes[0].getaccount(address_msft)
assert_equal(result, [f'1.00000000@{self.symbolDUSD}', f'2.00000000@{self.symbolMSFT}'])

if __name__ == '__main__':
TokenSplitTest().main()