Skip to content

Commit

Permalink
Implementing the dToken restart (#2976)
Browse files Browse the repository at this point in the history
* started test file
TD not working yet, no idea why

* fixed test setup

* fmt

* adapted test

* added LockToken function to validation and trigger on fork height

already doing paybackwithcollateral

* added SC address for TokenLock

* added DB entries for TokenLock UserBalances

* split up ProcessTokenLock for easier reading

* splitting loantokens

leads to EVM-block hash mismatch. no idea why yet.

* first shot on adding coinbase txs and pool splits.

* got pool "splits" and tokens working

EVM hash still wrong. no idea why.

* added collateral to tokenSplit

* added todos

* locking away tokens from pools, collateral and balances

* refactor: extract lockup method

* added rpc calls, improved amount minted

* added function to payback loans directly or from collateral

DUSD is burned directly for dToken loans

* refactor and added swapping of collateral if there is a pool

* fmt

* fmt

* improved tests

* fix payback in DUSD case with DUSD collateral

* added further testcase, fix missing interest on paybackswaps

* first shot on lock-release tx

* tx working, tests not updated yet.

need to figure out why locked tokens sometimes have fi difference

* improved poolsplit logic

fixed tests

* added composite swaps on payback, also considering fees now

* added cancelation of Auctions

* added lock during TD

* added handling of locks to stocksplits

* fix TD lock to only lock when old token is transferred

* fix: locked tokens must not be upgraded on EVM

* refactor and fmt

* improvements from review

* review refactor, updated test values

* prevent circular dep warning

* refactor: remove dependency and mark circular as expected

* reenable blockhash error

still fails. still no idea why.

* moved restart logic to DF25

* lint fixes

* fmt

* revert unneeded changes

* Fix compilation error. Use snapshots.

* remove new poolsplit logic into seperate PR

* Miner logic, increment ID for each token and add rename for DUSD

* adapted test data to seperated split logic

* Remove code repetition

* Remove copy pasted comment

* Move USDD creation to be last miner side

* Remove rename of DUSD. Unlock new tokens.

* Update closing of vault auctions

* Trigger dToken restart via attributes

* Allow dToken restart to fail

* Format CPP

* added check to only allow lock if not locked before

* Fix broken attribute logic

* fix payback return value, added early returns on failure

* Add lock ratio to attributes

* added attribute check and improved logs and comments

* Add missing setting of attributes. Format CPP.

* Print subLoan without debug

* Payback balance available

* SubLoanToken after amount calculated

* added check for owner balance at the start to reduce complexity later

* fix handling of owner balance in case of multiple vaults per owner

* improved logging

* refactor to consolidate rewards before locking and cache pools

and better logging

* fix handling of payback if no DUSD collateral left

* fixed floating errors on payback, removed unnecessary logging

* fail if loans left after payback

* fix calc of rewards before token split

* add reward consolidation before loan payback

* remove unnecessary log

* move to DF24

* fix logs

* Add missing forks to test

* Add prefix checks and fix overlaps

* Add another missing fork

* fixed tests

* lint: format CPP

* added helper for fork params in tests

added fixme with weird double-commission reference

* fix: added ensure uniqueness of owners in ConsolidateRewards

* fix cncurrency issue of ConsolidateRewards

fixes #3001

* improve history: only 1 entry per address for tokenlock

added tests for history entries

* Post merge changes

* Do not add EVM TXs if prices are invalid

* added test checks for history. currently weird behaviour

* fix comment in test

* Update test

* lint: format Python

* lint: format CPP

* Calculate static rewards by getting start balance from the block before

* lint: format Python

* lint: Format CPP

* Migrate commission on split

* Get tokens direct from attributes

* Fix commission bug

* Push failing test

* fix rounding bug on collateral swap estimation

* fix test values after rounding fix

* Push failing minimal balances test

* adapt locking logic: in case of rounding errors, add the extra fi to lock.

* Add interest test

* lint: remove unused import

* Avoid clash with other pending changes

* Update member outside of static function

---------

Co-authored-by: Prasanna Loganathar <[email protected]>
Co-authored-by: Bushstar <[email protected]>
  • Loading branch information
3 people authored Sep 6, 2024
1 parent ca158f5 commit 8e1b75c
Show file tree
Hide file tree
Showing 30 changed files with 4,842 additions and 229 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ libdefi_server_a_SOURCES = \
dfi/consensus/proposals.cpp \
dfi/consensus/smartcontracts.cpp \
dfi/consensus/tokens.cpp \
dfi/consensus/tokenlock.cpp \
dfi/consensus/txvisitor.cpp \
dfi/consensus/vaults.cpp \
dfi/consensus/xvm.cpp \
Expand Down
4 changes: 4 additions & 0 deletions src/amount.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ inline ResVal<CAmount> SafeAdd(CAmount _a, CAmount _b) {
return {(CAmount) sum, Res::Ok()};
}

inline CAmount MultiplyDivideAmounts(CAmount a, CAmount b, CAmount c) {
return (arith_uint256(a) * arith_uint256(b) / arith_uint256(c)).GetLow64();
}

inline CAmount MultiplyAmounts(CAmount a, CAmount b)
{
return (arith_uint256(a) * arith_uint256(b) / arith_uint256(COIN)).GetLow64();
Expand Down
5 changes: 5 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ class CMainParams : public CChainParams {
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2203] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1})));
consensus.smartContracts[SMART_CONTRACT_DFIP2206F] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2})));
consensus.smartContracts[SMART_CONTRACT_TOKENLOCK] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3})));

// owner base58, operator base58
vMasternodes.push_back({"8PuErAcazqccCVzRcc8vJ3wFaZGm4vFbLe", "8J846CKFF83Jcj5m4EReJmxiaJ6Jy1Y6Ea"});
Expand Down Expand Up @@ -556,6 +557,7 @@ class CTestNetParams : public CChainParams {
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2203] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1})));
consensus.smartContracts[SMART_CONTRACT_DFIP2206F] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2})));
consensus.smartContracts[SMART_CONTRACT_TOKENLOCK] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3})));

// owner base58, operator base58
vMasternodes.push_back({"7LMorkhKTDjbES6DfRxX2RiNMbeemUkxmp", "7KEu9JMKCx6aJ9wyg138W3p42rjg19DR5D"});
Expand Down Expand Up @@ -774,6 +776,7 @@ class CChangiParams : public CChainParams {
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2203] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1})));
consensus.smartContracts[SMART_CONTRACT_DFIP2206F] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2})));
consensus.smartContracts[SMART_CONTRACT_TOKENLOCK] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3})));

// owner base58, operator base58
vMasternodes.push_back({"7LMorkhKTDjbES6DfRxX2RiNMbeemUkxmp", "7KEu9JMKCx6aJ9wyg138W3p42rjg19DR5D"});
Expand Down Expand Up @@ -990,6 +993,7 @@ class CDevNetParams : public CChainParams {
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2203] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1})));
consensus.smartContracts[SMART_CONTRACT_DFIP2206F] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2})));
consensus.smartContracts[SMART_CONTRACT_TOKENLOCK] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3})));

// owner base58, operator base58
vMasternodes.push_back({"7LMorkhKTDjbES6DfRxX2RiNMbeemUkxmp", "7KEu9JMKCx6aJ9wyg138W3p42rjg19DR5D"});
Expand Down Expand Up @@ -1215,6 +1219,7 @@ class CRegTestParams : public CChainParams {
consensus.smartContracts[SMART_CONTRACT_DFIP_2201] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})));
consensus.smartContracts[SMART_CONTRACT_DFIP_2203] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1})));
consensus.smartContracts[SMART_CONTRACT_DFIP2206F] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2})));
consensus.smartContracts[SMART_CONTRACT_TOKENLOCK] = GetScriptForDestination(CTxDestination(WitnessV0KeyHash(std::vector<unsigned char>{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3})));


struct KeyPairString {
Expand Down
1 change: 1 addition & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class CChainParams
const auto SMART_CONTRACT_DFIP_2201 = "DFIP2201";
const auto SMART_CONTRACT_DFIP_2203 = "DFIP2203";
const auto SMART_CONTRACT_DFIP2206F = "DFIP2206F";
const auto SMART_CONTRACT_TOKENLOCK = "TokenLock";

constexpr auto ETH_ADDR_PREFIX = "0x";
constexpr auto ETH_ADDR_LENGTH_INC_PREFIX = 42;
Expand Down
28 changes: 28 additions & 0 deletions src/dfi/accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,34 @@ uint32_t CAccountsView::GetBalancesHeight(const CScript &owner) {
return ok ? height : 0;
}

CTokenLockUserValue CAccountsView::GetTokenLockUserValue(const CTokenLockUserKey &key) const {
CTokenLockUserValue value;
bool ok = ReadBy<ByTokenLockKey>(key, value);
return ok ? value : (CTokenLockUserValue){};
}

Res CAccountsView::StoreTokenLockUserValues(const CTokenLockUserKey &key, const CTokenLockUserValue &lockedValue) {
auto res = WriteBy<ByTokenLockKey>(key, lockedValue);
if (!res) {
return DeFiErrors::AccountsTokenLockStore();
}
return Res::Ok();
}

void CAccountsView::ForEachTokenLockUserValues(
std::function<bool(const CTokenLockUserKey &, const CTokenLockUserValue &)> callback,
const CTokenLockUserKey &start) {
ForEach<ByTokenLockKey, CTokenLockUserKey, CTokenLockUserValue>(callback, start);
}

Res CAccountsView::EraseTokenLockUserValues(const CTokenLockUserKey &key) {
auto res = EraseBy<ByTokenLockKey>(key);
if (!res) {
return DeFiErrors::AccountsTokenLockErase();
}
return Res::Ok();
}

Res CAccountsView::StoreFuturesUserValues(const CFuturesUserKey &key, const CFuturesUserValue &futures) {
auto res = WriteBy<ByFuturesSwapKey>(key, futures);
if (!res) {
Expand Down
36 changes: 36 additions & 0 deletions src/dfi/accounts.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,32 @@ struct CFuturesUserValue {
}
};

struct CReleaseLockMessage {
CAmount releasePart;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(releasePart);
}
};

struct CTokenLockUserKey {
CScript owner;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(owner);
}

bool operator<(const CFuturesUserKey &o) const { return owner < o.owner; }
};

struct CTokenLockUserValue : CBalances {};

class CAccountsView : public virtual CStorageView {
public:
void ForEachAccount(std::function<bool(const CScript &)> callback, const CScript &start = {});
Expand Down Expand Up @@ -197,6 +223,13 @@ class CAccountsView : public virtual CStorageView {
{},
std::numeric_limits<uint32_t>::max()});

CTokenLockUserValue GetTokenLockUserValue(const CTokenLockUserKey &key) const;
Res StoreTokenLockUserValues(const CTokenLockUserKey &key, const CTokenLockUserValue &futures);
Res EraseTokenLockUserValues(const CTokenLockUserKey &key);
void ForEachTokenLockUserValues(
std::function<bool(const CTokenLockUserKey &, const CTokenLockUserValue &)> callback,
const CTokenLockUserKey &start = {{}});

// tags
struct ByBalanceKey {
static constexpr uint8_t prefix() { return 'a'; }
Expand All @@ -210,6 +243,9 @@ class CAccountsView : public virtual CStorageView {
struct ByFuturesDUSDKey {
static constexpr uint8_t prefix() { return 'm'; }
};
struct ByTokenLockKey {
static constexpr uint8_t prefix() { return '8'; }
};

private:
Res SetBalance(const CScript &owner, CTokenAmount amount);
Expand Down
96 changes: 96 additions & 0 deletions src/dfi/consensus/tokenlock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) 2023 The DeFi Blockchain Developers
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.

#include <consensus/params.h>
#include <dfi/accountshistory.h>
#include <dfi/consensus/tokenlock.h>
#include <dfi/govvariables/attributes.h>
#include <dfi/historywriter.h>
#include <dfi/masternodes.h>
#include <dfi/mn_checks.h>

Res CTokenLockConsensus::operator()(const CReleaseLockMessage &obj) const {
if (!HasFoundationAuth()) {
return Res::Err("tx not from foundation member");
}
if (obj.releasePart == 0) {
return Res::Err("release ratio can not be 0");
}
const auto &tx = txCtx.GetTransaction();
auto &mnview = blockCtx.GetView();

// get current ratio from attributes
auto attributes = mnview.GetAttributes();

CDataStructureV0 releaseKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::TokenLockRatio};
auto currentRatio = attributes->GetValue(releaseKey, CAmount{});
if (currentRatio < obj.releasePart) {
return Res::Err("can't release more than locked %s vs %s",
GetDecimalString(currentRatio),
GetDecimalString(obj.releasePart));
}

auto newRatio = currentRatio - obj.releasePart;
// part of locked funds to be released
auto releaseAmount = [&](const CAmount &amount) {
if (currentRatio <= obj.releasePart) {
return amount;
} else {
return MultiplyDivideAmounts(amount, obj.releasePart, currentRatio);
}
};
LogPrintf("releasing locked tokens, current ratio %s, releasing %s, resulting ratio %s. \n",
GetDecimalString(currentRatio),
GetDecimalString(obj.releasePart),
GetDecimalString(newRatio));

// calc part of current funds that should be released
// cap to all funds
// for each tokenlock: release funds and update values
const auto contractAddressValue = blockCtx.GetConsensus().smartContracts.at(SMART_CONTRACT_TOKENLOCK);
std::vector<CTokenLockUserKey> todelete{};

CBalances totalReleasedFunds;
mnview.ForEachTokenLockUserValues([&](const auto &key, const auto &value) {
const auto &owner = key.owner;
auto newBalance = CTokenLockUserValue{};
bool gotNew = false;
CAccountsHistoryWriter view(
mnview, blockCtx.GetHeight(), txCtx.GetTxn(), tx.GetHash(), uint8_t(CustomTxType::TokenLockRelease));

for (const auto &[tokenId, amount] : value.balances) {
const CTokenAmount moved = {tokenId, releaseAmount(amount)};

view.AddBalance(owner, moved);
totalReleasedFunds.Add(moved);
auto updated = amount - moved.nValue;
if (updated > 0) {
newBalance.Add({tokenId, updated});
gotNew = true;
}
}
view.Flush();
if (gotNew) {
mnview.StoreTokenLockUserValues(key, newBalance);
} else {
todelete.emplace_back(key);
}
return true;
});

CAccountsHistoryWriter view(
mnview, blockCtx.GetHeight(), txCtx.GetTxn(), tx.GetHash(), uint8_t(CustomTxType::TokenLockRelease));
for (const auto &[tokenId, amount] : totalReleasedFunds.balances) {
view.SubBalance(contractAddressValue, {tokenId, amount});
}
view.Flush();

attributes->SetValue(releaseKey, newRatio);
mnview.SetVariable(*attributes);
for (const auto &key : todelete) {
mnview.EraseTokenLockUserValues(key);
}

return Res::Ok();
}
18 changes: 18 additions & 0 deletions src/dfi/consensus/tokenlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2023 The DeFi Blockchain Developers
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.

#ifndef DEFI_DFI_CONSENSUS_TOKENLOCK_H
#define DEFI_DFI_CONSENSUS_TOKENLOCK_H

#include <dfi/consensus/txvisitor.h>

struct CReleaseLockMessage;

class CTokenLockConsensus : public CCustomTxVisitor {
public:
using CCustomTxVisitor::CCustomTxVisitor;
Res operator()(const CReleaseLockMessage &obj) const;
};

#endif // DEFI_DFI_CONSENSUS_TOKENLOCK_H
10 changes: 9 additions & 1 deletion src/dfi/consensus/xvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,18 @@ Res CXVMConsensus::operator()(const CTransferDomainMessage &obj) const {

// Process TokenSplit
if (height >= consensus.DF23Height) {
res = ExecuteTokenMigrationTransferDomain(mnview, destAmount);
bool needToLock = false;
res = ExecuteTokenMigrationTransferDomain(mnview, destAmount, needToLock);
if (!res) {
return res;
}
// Process TokenLock
if (needToLock && height >= consensus.DF24Height) {
res = ExecuteLockTransferDomain(mnview, height, tx.GetHash(), dst.address, destAmount);
if (!res) {
return res;
}
}
}

// Add balance to DFI address
Expand Down
6 changes: 6 additions & 0 deletions src/dfi/customtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ CustomTxType CustomTxCodeToType(uint8_t ch) {
case CustomTxType::FutureSwapExecution:
case CustomTxType::FutureSwapRefund:
case CustomTxType::TokenSplit:
case CustomTxType::TokenLock:
case CustomTxType::TokenLockRelease:
case CustomTxType::Reject:
case CustomTxType::CreateCfp:
case CustomTxType::ProposalFeeRedistribution:
Expand Down Expand Up @@ -185,6 +187,10 @@ std::string ToString(CustomTxType type) {
return "FutureSwapRefund";
case CustomTxType::TokenSplit:
return "TokenSplit";
case CustomTxType::TokenLock:
return "TokenLock";
case CustomTxType::TokenLockRelease:
return "TokenLockRelease";
case CustomTxType::Reject:
return "Reject";
case CustomTxType::CreateCfp:
Expand Down
2 changes: 2 additions & 0 deletions src/dfi/customtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ enum class CustomTxType : uint8_t {
FutureSwapExecution = 'q',
FutureSwapRefund = 'w',
TokenSplit = 'P',
TokenLock = '?',
TokenLockRelease = '!',
// On-Chain-Gov
CreateCfp = 'z',
Vote = 'O', // NOTE: Check whether this overlapping with CreateOrder above is fine
Expand Down
12 changes: 12 additions & 0 deletions src/dfi/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ class DeFiErrors {

static Res GovVarValidateDF23Height() { return Res::Err("Cannot be set before DF23Height"); }

static Res GovVarValidateDF24Height() { return Res::Err("Cannot be set before DF24Height"); }

static Res GovVarValidateToken(const uint32_t token) { return Res::Err("No such token (%d)", token); }

static Res GovVarValidateTokenExist(const uint32_t token) { return Res::Err("Token (%d) does not exist", token); }
Expand Down Expand Up @@ -292,6 +294,12 @@ class DeFiErrors {

static Res GovVarValidateBlockPeriod() { return Res::Err("Block period must be more than sampling period"); }

static Res GovVarValidateBlockHeight() { return Res::Err("Block height must be more than current height"); }

static Res GovVarValidateRestartExecuted() {
return Res::Err("dToken restart has already been executed and cannot be set again");
}

static Res GovVarValidateUnsupportedKey() { return Res::Err("Unsupported key"); }

static Res GovVarValidateSplitDFI() { return Res::Err("Tokenised DFI cannot be split"); }
Expand Down Expand Up @@ -333,6 +341,10 @@ class DeFiErrors {

static Res AccountsFuturesErase() { return Res::Err("Failed to erase futures"); }

static Res AccountsTokenLockStore() { return Res::Err("Failed to store futures"); }

static Res AccountsTokenLockErase() { return Res::Err("Failed to erase futures"); }

static Res TransferDomainNotEnoughBalance(const std::string address) {
return Res::Err("Not enough balance in %s to cover \"EVM\" domain transfer", address);
}
Expand Down
Loading

0 comments on commit 8e1b75c

Please sign in to comment.