Created
January 7, 2024 18:59
-
-
Save vlzhr/2f1ac1c96b618dd66184026ac874965c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# STDLIB_VERSION 6 #-} | |
{-# CONTENT_TYPE DAPP #-} | |
{-# SCRIPT_TYPE ACCOUNT #-} | |
### System functions | |
let Scale8 = 100000000 | |
let Scale16 = 10000000000000000 | |
func getAssetBytes(assetIdStr: String) = if ((assetIdStr == "WAVES")) then unit else fromBase58String(assetIdStr) | |
### Settings | |
func verifyLiquidatorAccess(address: Address) = { getString(this, "setup_liquidators").valueOrElse("").indexOf(address.toString()) != unit } | |
let MaxShareToLiquidate = getInteger(this, "setup_maxShareToLiquidate").valueOrElse(100) # out of 10000 | |
let LiquidationDelay = getInteger(this, "setup_liquidationDelay").valueOrElse(10) # in blocks | |
let LiquidatorReward = getInteger(this, "setup_liquidatorReward").valueOrElse(10) # out of 10000, share of liquidated amount | |
let markets = getString(this, "setup_markets").valueOrElse([ | |
"3P4uA5etnZi4AmBabKinq2bMiWU8KcnHZdH", | |
"3P4DK5VzDwL3vfc5ahUEhtoe5ByZNyacJ3X", | |
"3PHpuQUPVUoR3AYzFeJzeWJfYLsLTmWssVH", | |
"3P8Df2b7ywHtLBHBe8PBVQYd3A5MdEEJAou", | |
"3PAd9Aqg3cQSzu26MFYWQYcjSXPVCJtuf5V" | |
].makeString(",")) | |
### Callables | |
@Callable(i) | |
func proxyLiquidateV2(market: String, userAddress: String, debtAssetIdStr: String, suppliedAssetIdStr: String, amount: Int) = { | |
# can be invoked by anyone in order to use account balance to liquidate negative health and assume the debt to account | |
if indexOf(markets, market) == unit then {throw("given pool address is not a pool")} else { | |
strict inv = invoke(addressFromStringValue(market), "liquidateV2", [false, userAddress, suppliedAssetIdStr], [AttachedPayment(debtAssetIdStr.getAssetBytes(), amount)]) | |
[] | |
} | |
} | |
@Callable(i) | |
func proxyLiquidateV3(market: String, userAddress: String, debtAssetIdStr: String, suppliedAssetIdStr: String, amount: Int, routeStr: String) = { | |
# can be invoked by verified liquidator | |
# no payment needed | |
# if successful, liquidator gets 0.1% of liquidated amount as a reward | |
# | |
# additional conditions: | |
# - routeStr from Puzzle Aggregator has to be used to set a swap route | |
# - not more than 1% of debt position at once | |
# - limit of 1 liquidation per 10 minutes per address to liquidate | |
if indexOf(markets, market) == unit then {throw("given pool address is not a pool")} | |
else if (!verifyLiquidatorAccess(i.caller)) then {throw("no permission to perform liquidation")} | |
else { | |
let marketAddress = addressFromStringValue(market) | |
strict rates = invoke(marketAddress, "calculateTokenRates", [false], []).exactAs[(List[(Int,Int)], String)] | |
let marketAssets = getStringValue(marketAddress, "setup_tokens").split(",") | |
let bAssetNum = marketAssets.indexOf(debtAssetIdStr).valueOrErrorMessage("debtAsset not found in market composition") | |
let bRate = rates._1[bAssetNum]._2 | |
let bAmount = getIntegerValue(marketAddress, userAddress+"_borrowed_"+debtAssetIdStr) | |
let borrowedAmount = fraction(bAmount, bRate, Scale16) | |
let lastLiquidationKeyStr = "history_"+userAddress+"_lastLiquidation" | |
let lastLiquidation = getInteger(this, lastLiquidationKeyStr).valueOrElse(0) | |
if (fraction(amount, borrowedAmount, 10000) > MaxShareToLiquidate) then {throw("should a liquidate smaller part per time")} | |
else if (lastLiquidation + LiquidationDelay < height) then {throw("cannot liquidate yet")} | |
else { | |
strict inv = invoke(marketAddress, "liquidate", [false, userAddress, bAmount, suppliedAssetIdStr, debtAssetIdStr, routeStr], []) | |
let liquidatorReward = fraction(amount, LiquidatorReward, 10000) | |
let statsKeyStr = "reward_"+i.caller.toString()+"_"+debtAssetIdStr | |
[ | |
IntegerEntry(statsKeyStr, getInteger(this, statsKeyStr).valueOrElse(0) + liquidatorReward), | |
IntegerEntry(lastLiquidationKeyStr, height) | |
] | |
} | |
} | |
} | |
@Callable(i) | |
func payoutReward(addressStr: String, assetIdStr: String) = { | |
if (i.caller.toString() != "3PMcMiMEs6w56NRGacksXtFG5zS7doE9fpL") then {throw("no access to this method")} | |
else { | |
let statsKeyStr = "reward_"+addressStr+"_"+assetIdStr | |
let rewardAvailable = getInteger(this, statsKeyStr).valueOrElse(0) | |
[ | |
ScriptTransfer(addressStr.addressFromStringValue(), rewardAvailable, assetIdStr.getAssetBytes()), | |
IntegerEntry(statsKeyStr, 0) | |
] | |
} | |
} | |
@Callable(i) | |
func updateParameter(val: String, key: String) = { | |
if (i.caller.toString() != "3PMcMiMEs6w56NRGacksXtFG5zS7doE9fpL") then {throw("no access to this method")} | |
else { | |
[StringEntry(val, key)] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment