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

Initial full implementation of ZapPoolLiquidity. #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class CMainParams : public CChainParams {
consensus.FortCanningRoadHeight = 1786000; // April 11, 2022.
consensus.FortCanningCrunchHeight = 1936000; // June 2, 2022.
consensus.FortCanningSpringHeight = 2033000; // July 6, 2022.
consensus.ApertureHeight = 2033001; // TODO: update this to a sensible height.
consensus.GreatWorldHeight = std::numeric_limits<int>::max();

consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
Expand Down Expand Up @@ -374,6 +375,7 @@ class CTestNetParams : public CChainParams {
consensus.FortCanningRoadHeight = 893700;
consensus.FortCanningCrunchHeight = 1011600;
consensus.FortCanningSpringHeight = 1086000;
consensus.ApertureHeight = 1086001; // TODO: update this to a sensible value.
consensus.GreatWorldHeight = std::numeric_limits<int>::max();

consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
Expand Down Expand Up @@ -554,6 +556,7 @@ class CDevNetParams : public CChainParams {
consensus.BayfrontHeight = 0;
consensus.BayfrontMarinaHeight = 0;
consensus.BayfrontGardensHeight = 0;
consensus.ApertureHeight = 0;
consensus.ClarkeQuayHeight = 0;
consensus.DakotaHeight = 10;
consensus.DakotaCrescentHeight = 10;
Expand Down Expand Up @@ -739,6 +742,7 @@ class CRegTestParams : public CChainParams {
consensus.BayfrontHeight = 10000000;
consensus.BayfrontMarinaHeight = 10000000;
consensus.BayfrontGardensHeight = 10000000;
consensus.ApertureHeight = 10000000;
consensus.ClarkeQuayHeight = 10000000;
consensus.DakotaHeight = 10000000;
consensus.DakotaCrescentHeight = 10000000;
Expand Down Expand Up @@ -960,6 +964,7 @@ void SetupCommonArgActivationParams(Consensus::Params &consensus) {
UpdateHeightValidation("AMK", "-amkheight", consensus.AMKHeight);
UpdateHeightValidation("Bayfront", "-bayfrontheight", consensus.BayfrontHeight);
UpdateHeightValidation("Bayfront Gardens", "-bayfrontgardensheight", consensus.BayfrontGardensHeight);
UpdateHeightValidation("Aperture", "-apertureheight", consensus.ApertureHeight);
UpdateHeightValidation("Clarke Quay", "-clarkequayheight", consensus.ClarkeQuayHeight);
UpdateHeightValidation("Dakota", "-dakotaheight", consensus.DakotaHeight);
UpdateHeightValidation("Dakota Crescent", "-dakotacrescentheight", consensus.DakotaCrescentHeight);
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct Params {
int FortCanningCrunchHeight;
int FortCanningSpringHeight;
int GreatWorldHeight;
int ApertureHeight;

/** Foundation share after AMK, normalized to COIN = 100% */
CAmount foundationShareDFIP1;
Expand Down
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ void SetupServerArgs()
gArgs.AddArg("-amkheight", "AMK fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-bayfrontheight", "Bayfront fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-bayfrontgardensheight", "Bayfront Gardens fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-apertureheight", "Aperture fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-clarkequayheight", "ClarkeQuay fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-dakotaheight", "Dakota fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-dakotacrescentheight", "DakotaCrescent fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
Expand Down
136 changes: 136 additions & 0 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ std::string ToString(CustomTxType type) {
case CustomTxType::PoolSwapV2: return "PoolSwap";
case CustomTxType::AddPoolLiquidity: return "AddPoolLiquidity";
case CustomTxType::RemovePoolLiquidity: return "RemovePoolLiquidity";
case CustomTxType::ZapPoolLiquidity: return "ZapPoolLiquidity";
case CustomTxType::UtxosToAccount: return "UtxosToAccount";
case CustomTxType::AccountToUtxos: return "AccountToUtxos";
case CustomTxType::AccountToAccount: return "AccountToAccount";
Expand Down Expand Up @@ -143,6 +144,7 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) {
case CustomTxType::PoolSwapV2: return CPoolSwapMessageV2{};
case CustomTxType::AddPoolLiquidity: return CLiquidityMessage{};
case CustomTxType::RemovePoolLiquidity: return CRemoveLiquidityMessage{};
case CustomTxType::ZapPoolLiquidity: return CZapLiquidityMessage{};
case CustomTxType::UtxosToAccount: return CUtxosToAccountMessage{};
case CustomTxType::AccountToUtxos: return CAccountToUtxosMessage{};
case CustomTxType::AccountToAccount: return CAccountToAccountMessage{};
Expand Down Expand Up @@ -202,6 +204,13 @@ class CCustomMetadataParseVisitor
return Res::Ok();
}

Res isPostApertureFork() const {
if(static_cast<int>(height) < consensus.ApertureHeight) {
return Res::Err("called before Aperture height");
}
return Res::Ok();
}

Res isPostBayfrontFork() const {
if(static_cast<int>(height) < consensus.BayfrontHeight) {
return Res::Err("called before Bayfront height");
Expand Down Expand Up @@ -350,6 +359,11 @@ class CCustomMetadataParseVisitor
return !res ? res : serialize(obj);
}

Res operator()(CZapLiquidityMessage& obj) const {
auto res = isPostApertureFork();
return !res ? res : serialize(obj);
}

Res operator()(CUtxosToAccountMessage& obj) const {
auto res = isPostAMKFork();
return !res ? res : serialize(obj);
Expand Down Expand Up @@ -1321,6 +1335,128 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor
return !res ? res : mnview.SetPoolPair(amount.nTokenId, height, pool);
}

Res operator()(const CZapLiquidityMessage& obj) const {
CBalances sumTx = SumAllTransfers(obj.from);
if (sumTx.balances.size() != 1) {
return Res::Err("liquidity zapping requires a single input token");
}
std::pair<DCT_ID, CAmount> inputTokenAmount = *sumTx.balances.begin();

static const DCT_ID DFI_TOKEN_ID = {0};
if (inputTokenAmount.first != DFI_TOKEN_ID || inputTokenAmount.second <= 0) {
return Res::Err("liquidity zapping requires a positive amount of DFI token as input");
}
auto dusdTokenLookupResult = mnview.GetToken("DUSD");
if (!dusdTokenLookupResult)
return Res::Err("Cannot find token DUSD");
const DCT_ID DUSD_TOKEN_ID = dusdTokenLookupResult->first;

std::optional<CPoolPair> pair = mnview.GetPoolPair(obj.poolId);
if (!pair) {
return Res::Err("invalid poolId");
}
DCT_ID nonDUSDToken;
CAmount reserveDUSD, reserveNonDUSD;
if (pair->idTokenA == DUSD_TOKEN_ID) {
nonDUSDToken = pair->idTokenB;
reserveDUSD = pair->reserveA;
reserveNonDUSD = pair->reserveB;
} else if (pair->idTokenB == DUSD_TOKEN_ID) {
nonDUSDToken = pair->idTokenA;
reserveDUSD = pair->reserveB;
reserveNonDUSD = pair->reserveA;
} else {
return Res::Err("liquidity zapping only supports pools where one token is DUSD");
}

// Swap DFI for DUSD.
CAmount dusdAmount{0};
for (const auto& [fromAccount, balances]: obj.from) {
if (!HasAuth(fromAccount)) {
return Res::Err("missing authorization by source account");
}
for (const auto& [dct_id, amount]: balances.balances) {
if (dct_id != DFI_TOKEN_ID || amount <= 0) continue;
auto pool_swap = CPoolSwap(CPoolSwapMessage {
.from = fromAccount,
.to = obj.shareAddress,
.idTokenFrom = DFI_TOKEN_ID,
.idTokenTo = DUSD_TOKEN_ID,
.amountFrom = inputTokenAmount.second,
.maxPrice = POOLPRICE_MAX,
}, height);
Res dfiToDUSDSwapRes = pool_swap.ExecuteSwap(mnview, {});
if (!dfiToDUSDSwapRes) return dfiToDUSDSwapRes;
dusdAmount += pool_swap.GetResult().nValue;
}
}

// Binary search for the optimal DUSD amount to swap for the other token and provide liquidity.
CAmount dusdSwapAmount{0};
for (CAmount a = 0, b = dusdAmount; a + 1 < b; ) {
dusdSwapAmount = (a + b) >> 1;

// Simulate swap on a dummy view.
CCustomCSView dummy(mnview);
auto pool_swap = CPoolSwap(CPoolSwapMessage {
.from = obj.shareAddress,
.to = obj.shareAddress,
.idTokenFrom = DUSD_TOKEN_ID,
.idTokenTo = nonDUSDToken,
.amountFrom = dusdSwapAmount,
.maxPrice = POOLPRICE_MAX,
}, height);
Res simulatedSwapRes = pool_swap.ExecuteSwap(dummy, {});
if (!simulatedSwapRes) return simulatedSwapRes;
CAmount nonDUSDLiquidityAmount = pool_swap.GetResult().nValue;
CAmount dusdLiquidityAmount = dusdAmount - nonDUSDLiquidityAmount;

// Simulate adding liquidity.
CAmount liqNonDUSD = (arith_uint256(nonDUSDLiquidityAmount) * arith_uint256(pair->totalLiquidity) / reserveNonDUSD).GetLow64();
CAmount liqDUSD = (arith_uint256(dusdLiquidityAmount) * arith_uint256(pair->totalLiquidity) / reserveDUSD).GetLow64();
if (liqNonDUSD < liqDUSD) a = dusdSwapAmount;
else b = dusdSwapAmount;
}

// Swap & add liquidity using the calculated amount.
auto pool_swap = CPoolSwap(CPoolSwapMessage {
.from = obj.shareAddress,
.to = obj.shareAddress,
.idTokenFrom = DUSD_TOKEN_ID,
.idTokenTo = nonDUSDToken,
.amountFrom = dusdSwapAmount,
.maxPrice = POOLPRICE_MAX,
}, height);
Res swapRes = pool_swap.ExecuteSwap(mnview, {});
if (!swapRes) return swapRes;
CAmount nonDUSDLiquidityAmount = pool_swap.GetResult().nValue;
CAmount dusdLiquidityAmount = dusdAmount - nonDUSDLiquidityAmount;

// Subtract DUSD and nonDUSD token balances.
mnview.SubBalances(obj.shareAddress, {
.balances = {
{DUSD_TOKEN_ID, dusdLiquidityAmount},
{nonDUSDToken, nonDUSDLiquidityAmount}
}
});

// Add liquidity.
CAmount amountA, amountB;
if (pair->idTokenA == DUSD_TOKEN_ID) {
amountA = dusdLiquidityAmount;
amountB = nonDUSDLiquidityAmount;
} else {
amountA = nonDUSDLiquidityAmount;
amountB = dusdLiquidityAmount;
}
bool slippageProtection = static_cast<int>(height) >= consensus.BayfrontMarinaHeight;
auto res = pair->AddLiquidity(amountA, amountB, [&] /*onMint*/(CAmount liqAmount) {
CBalances balance{TAmounts{{obj.poolId, liqAmount}}};
return addBalanceSetShares(obj.shareAddress, balance);
}, slippageProtection);
return !res ? res : mnview.SetPoolPair(obj.poolId, height, *pair);
}

Res operator()(const CUtxosToAccountMessage& obj) const {
// check enough tokens are "burnt"
const auto burnt = BurntTokens(tx);
Expand Down
3 changes: 3 additions & 0 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ enum class CustomTxType : uint8_t
PoolSwapV2 = 'i',
AddPoolLiquidity = 'l',
RemovePoolLiquidity = 'r',
ZapPoolLiquidity = 'Z',
// accounts
UtxosToAccount = 'U',
AccountToUtxos = 'b',
Expand Down Expand Up @@ -124,6 +125,7 @@ inline CustomTxType CustomTxCodeToType(uint8_t ch) {
case CustomTxType::PoolSwapV2:
case CustomTxType::AddPoolLiquidity:
case CustomTxType::RemovePoolLiquidity:
case CustomTxType::ZapPoolLiquidity:
case CustomTxType::UtxosToAccount:
case CustomTxType::AccountToUtxos:
case CustomTxType::AccountToAccount:
Expand Down Expand Up @@ -347,6 +349,7 @@ using CCustomTxMessage = std::variant<
CPoolSwapMessageV2,
CLiquidityMessage,
CRemoveLiquidityMessage,
CZapLiquidityMessage,
CUtxosToAccountMessage,
CAccountToUtxosMessage,
CAccountToAccountMessage,
Expand Down
15 changes: 15 additions & 0 deletions src/masternodes/poolpairs.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,21 @@ struct CRemoveLiquidityMessage {
}
};

struct CZapLiquidityMessage {
CAccounts from;
DCT_ID poolId;
CScript shareAddress;

ADD_SERIALIZE_METHODS;

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

bool poolInFee(const bool forward, const std::pair<CFeeDir, CFeeDir>& asymmetricFee);
bool poolOutFee(const bool forward, const std::pair<CFeeDir, CFeeDir>& asymmetricFee);

Expand Down
10 changes: 10 additions & 0 deletions src/masternodes/rpc_customtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ class CCustomTxRpcVisitor
rpcInfo.pushKV("amount", obj.amount.ToString());
}

void operator()(const CZapLiquidityMessage& obj) const {
CBalances sumTx = SumAllTransfers(obj.from);
if (sumTx.balances.size() == 1) {
auto amount = *sumTx.balances.begin();
rpcInfo.pushKV(amount.first.ToString(), ValueFromAmount(amount.second));
rpcInfo.pushKV("poolid", obj.poolId.ToString());
rpcInfo.pushKV("shareaddress", ScriptToString(obj.shareAddress));
}
}

void operator()(const CUtxosToAccountMessage& obj) const {
rpcInfo.pushKVs(accountsInfo(obj.to));
}
Expand Down
Loading