Smart Contract Details
Open on Dero ExplorerInternal ID:
48216
Hash / Transaction:
Block:
Timestamp:
2024-03-12 00:55:43 UTC (1.5 years ago)
Creator:
marco!
(+9 more)
(1771597)
Raw Data:
[{"name":"SC_ACTION","datatype":"U","value":1},{"name":"SC_CODE","datatype":"S","value":"\n// .-.\n// (o o) \n// | O \\ boo!\n// | \\\n// '~~~~'\n// ?\n// /\\ ___\n// (oo) |RIP|\n// /||\\ |___|\n//////////////////////////////////////\n// Ghost Exchange v1.1\n// Use at your own risk\n// For more info visit: ghost.trading\n//////////////////////////////////////\n\nFunction Initialize() Uint64\n 10 STORE(\"owner\", SIGNER())\n 20 STORE(\"feeTo\", SIGNER())\n 30 STORE(\"nameHdr\", \"Ghost Exchange\")\n 40 STORE(\"descrHdr\", \"Phantom-powered finance\") \n 50 RETURN 0\nEnd Function \n\n// Provided liquidity will be tracked using BOO tokens. \n// BOO TOKENS ARE NOT ASSETS. Each account's BOO balance per token pair is stored \n// inside the Smart Contract as a variable with the following schema:\n// \u003cprovider_address\u003e:BOO:\u003casset_address\u003e\n// The total liquidity provided by all LPs of a pair at a given moment is denominated supply.\n// supply is tracked inside the Smart Contract as a variable with the following schema:\n// \u003casset_address\u003e:BOO\n// Deposit assets and DERO at current ratio to get BOO tokens. \n// {asset_address} The deposited asset's SCID\n// {min_liquidity} Minimum number of BOO sender will get if total BOO supply is greater than 0.\n// {min_liquidity} does nothing when total BOO supply is 0.\nFunction AddLiquidity(asset_address String, min_liquidity Uint64) Uint64\n 01 DIM total_liquidity, dero_reserve, asset_reserve, asset_amount, liquidity_minted, dero_deposited, asset_deposited, SYS_MAX_VALUE as Uint64\n 02 LET SYS_MAX_VALUE = 18446744073709551 // Uint64.Max/1000 to protect from infinite supply tokens\n 03 LET dero_deposited = DEROVALUE()\n 04 LET asset_deposited = ASSETVALUE(HEXDECODE(asset_address))\n 10 IF (asset_deposited \u003e 0 \u0026 dero_deposited \u003e 0 ) THEN GOTO 30\n 20 GOTO 666\n 30 LET total_liquidity = get_supply_per_asset(asset_address) \n // Pair already exists\n 40 IF total_liquidity \u003e 0 THEN GOTO 41 ELSE GOTO 60\n 41 IF min_liquidity \u003e 0 THEN GOTO 43\n 42 GOTO 666 \n 43 LET dero_reserve = get_dero_reserve_per_asset(asset_address) \n 44 LET asset_reserve = get_asset_reserve(asset_address)\n 45 IF will_addition_overflow(asset_reserve, asset_deposited) == 1 THEN GOTO 666\n 46 mintFee(dero_reserve, asset_reserve, asset_address)\n 47 LET total_liquidity = get_supply_per_asset(asset_address) // Must be defined again since totalSupply can update in mintFee\n 48 LET asset_amount = mult_div(dero_deposited, asset_reserve , dero_reserve + 1)\n 49 LET liquidity_minted = mult_div(dero_deposited, total_liquidity, dero_reserve)\n 50 IF asset_deposited \u003e= asset_amount \u0026 liquidity_minted \u003e= min_liquidity \u0026 (asset_reserve + asset_deposited \u003c= SYS_MAX_VALUE) THEN GOTO 52\n 51 GOTO 666\n 52 increase_provider_liquidity_by(SIGNER(), liquidity_minted, asset_address) \n 53 set_supply_per_asset(total_liquidity + liquidity_minted, asset_address)\n 54 IF asset_deposited == asset_amount THEN GOTO 56\n // Return any reminding assets\n 55 SEND_ASSET_TO_ADDRESS(SIGNER(), asset_deposited - asset_amount, HEXDECODE(asset_address))\n // Increase the dero reserve record for this asset\n 56 set_dero_reserve_per_asset(dero_reserve + dero_deposited, asset_address)\n // Increase the asset reserve record\n 57 set_asset_reserve(asset_reserve + asset_amount, asset_address)\n 58 STORE(asset_address+\":rootKLast\", sqrt(dero_reserve + dero_deposited) * sqrt(asset_reserve + asset_amount))\n // Return gracefully\n 59 GOTO 70\n // else\n // Creating pair \n 60 IF dero_deposited \u003e= 1000 \u0026 asset_deposited \u003c= SYS_MAX_VALUE THEN GOTO 62\n 61 GOTO 666\n // Initialize the asset reserve record for this asset\n 62 set_asset_reserve(asset_deposited, asset_address)\n 63 DIM initial_liquidity as Uint64\n 64 LET initial_liquidity = dero_deposited\n 65 set_supply_per_asset(initial_liquidity, asset_address)\n // Initialize the dero reserve record for this asset\n 66 set_dero_reserve_per_asset(dero_deposited, asset_address)\n 67 STORE(asset_address+\":rootKLast\", sqrt(asset_deposited) * sqrt(dero_deposited))\n 68 increase_provider_liquidity_by(SIGNER(), initial_liquidity, asset_address)\n 70 RETURN 0\n\n 666 RETURN 1\nEnd Function\n\n// Burn BOO tokens to withdraw Dero \u0026 assets at current ratio.\n// {amount} Amount of BOO burned.\n// {min_dero} Minimum DERO withdrawn.\n// {min_assets} Minimum assets withdrawn.\n// {asset_address} The deposited asset's SCID\nFunction RemoveLiquidity(amount Uint64, min_dero Uint64, min_assets Uint64, asset_address String) Uint64\n 01 DIM total_liquidity, dero_reserve, asset_reserve, dero_amount, asset_amount as Uint64\n 10 IF amount \u003e 0 \u0026 (min_dero \u003e 0 \u0026 min_assets \u003e 0) \u0026 ( get_provider_liquidity(SIGNER(), asset_address) \u003e= amount ) THEN GOTO 30\n 20 GOTO 666\n 30 LET total_liquidity = get_supply_per_asset(asset_address)\n 40 IF total_liquidity \u003e 0 THEN GOTO 60\n 50 GOTO 666\n 60 LET asset_reserve = get_asset_reserve(asset_address)\n 70 LET dero_reserve = get_dero_reserve_per_asset(asset_address)\n 80 mintFee(dero_reserve, asset_reserve, asset_address)\n 90 LET total_liquidity = get_supply_per_asset(asset_address) // Must be defined again since totalSupply can update in mintFee\n 100 LET dero_amount = mult_div(amount, dero_reserve, total_liquidity)\n 110 LET asset_amount = mult_div(amount, asset_reserve, total_liquidity)\n 120 IF dero_amount \u003e= min_dero \u0026 asset_amount \u003e= min_assets THEN GOTO 140\n 130 GOTO 666\n 140 decrease_provider_liquidity_by(SIGNER(), amount, asset_address)\n 150 set_supply_per_asset(total_liquidity - amount, asset_address)\n 160 set_dero_reserve_per_asset(dero_reserve - dero_amount, asset_address)\n 170 set_asset_reserve(asset_reserve - asset_amount, asset_address)\n 180 STORE(asset_address+\":rootKLast\", sqrt(dero_reserve - dero_amount) * sqrt(asset_reserve - asset_amount) )\n 190 SEND_DERO_TO_ADDRESS(SIGNER(), dero_amount)\n 200 SEND_ASSET_TO_ADDRESS(SIGNER(),asset_amount, HEXDECODE(asset_address))\n 210 RETURN 0\n\n 666 RETURN 1\nEnd Function\n\n// Pricing function for converting between DERO \u0026 Assets.\n// {input_amount} Amount of DERO or Assets being sold.\n// {input_reserve} Amount of DERO or Assets (input type) in exchange reserves.\n// {output_reserve} Amount of DERO or Assets (output type) in exchange reserves.\nFunction getInputPrice(input_amount Uint64, input_reserve Uint64, output_reserve Uint64) Uint64\n 01 DIM SYS_MAX_VALUE as Uint64\n 02 LET SYS_MAX_VALUE = 18446744073709551\n 10 IF input_reserve \u003e 0 \u0026 output_reserve \u003e 0 \u0026 input_amount \u003c= SYS_MAX_VALUE THEN GOTO 30\n 20 PANIC\n 30 DIM input_amount_with_fee as Uint64\n 40 LET input_amount_with_fee = input_amount * 997\n 50 IF will_addition_overflow(input_reserve * 1000, input_amount * 1000) == 0 THEN GOTO 70\n 60 PANIC // Infinite supply token attack\n 70 RETURN mult_div(input_amount_with_fee, output_reserve, (input_reserve * 1000) + input_amount_with_fee)\nEnd Function\n\n// Pricing function for converting between DERO \u0026 Assets.\n// {output_amount} Amount of DERO or Assets being bought.\n// {input_reserve} Amount of DERO or Assets (input type) in exchange reserves.\n// {output_reserve} Amount of DERO or Assets (output type) in exchange reserves.\nFunction getOutputPrice(output_amount Uint64, input_reserve Uint64, output_reserve Uint64) Uint64\n 10 IF input_reserve \u003e 0 \u0026\u0026 output_reserve \u003e 0 \u0026\u0026 output_reserve \u003e output_amount THEN GOTO 30\n 20 PANIC\n 30 RETURN mult_div(input_reserve * 1000, output_amount, (output_reserve - output_amount) * 997 + 1)\nEnd Function\n\nFunction deroToAssetInput(dero_sold Uint64, min_assets Uint64, asset_address String) Uint64\n 10 IF (dero_sold \u003e 0 \u0026 min_assets \u003e 0) THEN GOTO 30\n 20 PANIC\n 30 DIM assets_bought, asset_reserve, dero_reserve as Uint64\n 40 LET asset_reserve = get_asset_reserve(asset_address)\n 50 LET dero_reserve = get_dero_reserve_per_asset(asset_address)\n 60 LET assets_bought = getInputPrice(dero_sold, dero_reserve, asset_reserve)\n 70 IF assets_bought \u003e= min_assets THEN GOTO 90\n 80 PANIC\n 90 SEND_ASSET_TO_ADDRESS(SIGNER(), assets_bought, HEXDECODE(asset_address))\n 100 set_asset_reserve(asset_reserve - assets_bought, asset_address)\n 110 set_dero_reserve_per_asset(dero_reserve + dero_sold, asset_address)\n 120 RETURN 0\nEnd Function\n\n// Convert DERO to Assets.\n// User specifies exact input (DEROVALUE()).\n// User cannot specify minimum output.\n// {asset_address} Address of the asset to trade.\nFunction DeroToAssetSwapInput(asset_address String) Uint64\n 10 RETURN deroToAssetInput(DEROVALUE(), 1, asset_address)\nEnd Function\n\n// Convert DERO to Assets.\n// User specifies exact input (DEROVALUE()) \u0026 minimum output.\n// {min_assets} Minimum Assets bought.\n// {asset_address} Address of the asset to trade.\nFunction DeroToAssetSwapInputMin(min_assets Uint64, asset_address String) Uint64\n 10 RETURN deroToAssetInput(DEROVALUE(), min_assets, asset_address)\nEnd Function\n\nFunction deroToAssetOutput(assets_bought Uint64, max_dero Uint64, asset_address String) Uint64\n 10 IF (assets_bought \u003e 0 \u0026 max_dero \u003e 0) THEN GOTO 30\n 20 PANIC\n 30 DIM asset_reserve, dero_reserve, dero_sold, dero_refund as Uint64\n 40 LET asset_reserve = get_asset_reserve(asset_address)\n 41 LET dero_reserve = get_dero_reserve_per_asset(asset_address)\n 50 LET dero_sold = getOutputPrice(assets_bought, dero_reserve, asset_reserve)\n 60 IF dero_sold \u003e max_dero THEN GOTO 61 ELSE GOTO 70\n 61 PANIC\n 70 LET dero_refund = max_dero - dero_sold\n 80 IF dero_refund == 0 THEN GOTO 100\n 90 SEND_DERO_TO_ADDRESS(SIGNER(), dero_refund)\n 100 SEND_ASSET_TO_ADDRESS(SIGNER(), assets_bought, HEXDECODE(asset_address))\n 110 set_asset_reserve(asset_reserve - assets_bought, asset_address)\n 120 set_dero_reserve_per_asset(dero_reserve + dero_sold, asset_address)\n 130 RETURN 0\nEnd Function\n\n// Convert DERO to Assets.\n// User specifies maximum input (DEROVALUE()) \u0026 exact output.\n// {assets_bought} Amount of Assets bought.\n// {asset_address} Address of the asset to trade.\nFunction DeroToAssetSwapOutput(assets_bought Uint64, asset_address String) Uint64\n 10 RETURN deroToAssetOutput(assets_bought, DEROVALUE(), asset_address)\nEnd Function\n\nFunction assetToDeroInput(assets_sold Uint64, min_dero Uint64, asset_address String) Uint64\n 10 IF (assets_sold \u003e 0 \u0026 min_dero \u003e 0) THEN GOTO 30\n 20 PANIC\n 30 DIM asset_reserve, dero_bought as Uint64\n 40 LET asset_reserve = get_asset_reserve(asset_address)\n 50 LET dero_bought = getInputPrice(assets_sold, asset_reserve, get_dero_reserve_per_asset(asset_address))\n 60 IF dero_bought \u003e= min_dero THEN GOTO 80\n 70 PANIC\n 80 SEND_DERO_TO_ADDRESS(SIGNER(),dero_bought)\n 90 set_dero_reserve_per_asset(get_dero_reserve_per_asset(asset_address) - dero_bought, asset_address)\n 100 set_asset_reserve(asset_reserve + assets_sold, asset_address)\n 110 RETURN 0\nEnd Function\n\n// Convert Assets to DERO.\n// User specifies exact input (ASSETVALUE(asset_address)) \u0026 minimum output.\n// {min_dero} Minimum DERO purchased.\n// {asset_address} Address of the asset to trade.\nFunction AssetToDeroSwapInput(min_dero Uint64, asset_address String) Uint64\n 10 RETURN assetToDeroInput(ASSETVALUE(HEXDECODE(asset_address)), min_dero, asset_address)\nEnd Function\n\n\nFunction assetToDeroOutput(dero_bought Uint64, max_assets Uint64 , asset_address String) Uint64\n 10 IF dero_bought \u003e 0 THEN GOTO 30\n 20 PANIC\n 30 DIM asset_reserve, assets_sold, asset_refund as Uint64\n 40 LET asset_reserve = get_asset_reserve(asset_address)\n 50 LET assets_sold = getOutputPrice(dero_bought, asset_reserve, get_dero_reserve_per_asset(asset_address))\n // assets_sold is always \u003e zero\n 60 IF max_assets \u003e= assets_sold THEN GOTO 80\n 70 PANIC\n 80 SEND_DERO_TO_ADDRESS(SIGNER(), dero_bought)\n 90 LET asset_refund = max_assets - assets_sold\n 100 IF asset_refund == 0 THEN GOTO 120\n 110 SEND_ASSET_TO_ADDRESS(SIGNER(), asset_refund, HEXDECODE(asset_address))\n 120 set_dero_reserve_per_asset(get_dero_reserve_per_asset(asset_address) - dero_bought, asset_address)\n 130 set_asset_reserve(asset_reserve + assets_sold, asset_address)\n 140 RETURN 0\nEnd Function\n\n// Convert Assets to DERO.\n// User specifies maximum input (ASSETVALUE(asset_address)) \u0026 exact output.\n// {dero_bought} Amount of DERO purchased.\n// {asset_address} Address of the asset to trade.\nFunction AssetToDeroSwapOutput(dero_bought Uint64, asset_address String) Uint64\n 10 RETURN assetToDeroOutput(dero_bought, ASSETVALUE(HEXDECODE(asset_address)), asset_address)\nEnd Function\n\n// Public price function for DERO to Asset trades with an exact input.\n// {dero_sold} Amount of DERO sold.\n// {asset_address} Address of the asset to trade.\n// @returns Amount of Assets that can be bought with input DERO.\nFunction GetDeroToAssetInputPrice(dero_sold Uint64, asset_address String) Uint64\n 10 IF dero_sold \u003e 0 THEN GOTO 30\n 20 RETURN 0\n 30 RETURN getInputPrice(dero_sold, get_dero_reserve_per_asset(asset_address), get_asset_reserve(asset_address))\nEnd Function\n\n// Public price function for DERO to Asset trades with an exact output.\n// {assets_bought} Amount of Assets bought.\n// {asset_address} Address of the asset to trade.\n// @returns Amount of DERO needed to buy output Assets.\nFunction GetDeroToAssetOutputPrice(assets_bought Uint64, asset_address String) Uint64\n 10 IF assets_bought \u003e 0 THEN GOTO 30\n 20 RETURN 0\n 30 RETURN getOutputPrice(assets_bought, get_dero_reserve_per_asset(asset_address), get_asset_reserve(asset_address))\nEnd Function\n\n// Public price function for Asset to DERO trades with an exact input.\n// {assets_sold} Amount of Assets sold.\n// {asset_address} Address of the asset to trade.\n// @returns Amount of DERO that can be bought with input Assets.\nFunction GetAssetToDeroInputPrice(assets_sold Uint64, asset_address String) Uint64\n 10 IF assets_sold \u003e 0 THEN GOTO 30\n 20 RETURN 0\n 30 RETURN getInputPrice(assets_sold, get_asset_reserve(asset_address), get_dero_reserve_per_asset(asset_address))\nEnd Function\n\n// Public price function for Asset to DERO trades with an exact output.\n// {dero_bought} Amount of output DERO.\n// {asset_address} Address of the asset to trade.\n// @returns Amount of Assets needed to buy output DERO.\nFunction GetAssetToDeroOutputPrice(dero_bought Uint64, asset_address String) Uint64\n 10 IF dero_bought \u003e 0 THEN GOTO 30\n 20 RETURN 0\n 30 RETURN getOutputPrice(dero_bought, get_asset_reserve(asset_address), get_dero_reserve_per_asset(asset_address))\nEnd Function\n\nFunction mintFee(reserve0 Uint64, reserve1 Uint64, asset_address String) \n 10 DIM feeTo as String\n 11 DIM rootKLast as Uint64\n 20 LET feeTo = LOAD(\"feeTo\") \n // Get the last root k for the asset\n 30 LET rootKLast = LOAD(asset_address+\":rootKLast\")\n 40 IF rootKLast != 0 THEN GOTO 50\n 41 RETURN \n 50 DIM rootK as Uint64\n 60 LET rootK = sqrt(reserve0) * sqrt(reserve1)\n 70 IF rootK \u003e rootKLast THEN GOTO 80\n 71 RETURN \n 80 DIM supply, liquidity_minted as Uint64\n 90 LET supply = get_supply_per_asset(asset_address)\n 100 LET liquidity_minted = mult_div(supply, rootK - rootKLast, rootK * 5 + rootKLast)\n 110 IF liquidity_minted \u003e 0 THEN GOTO 111 ELSE GOTO 120\n 111 increase_provider_liquidity_by(feeTo, liquidity_minted, asset_address)\n 112 set_supply_per_asset(supply + liquidity_minted, asset_address)\n 120 RETURN \nEnd Function\n\n// Helper functions \n// Encapsulated to better understand what's happening and absctract BOO storage schema\n// from main code.\n\nFunction set_supply_per_asset(amount Uint64, asset_address String) \n 10 STORE(asset_address+\":BOO\", amount)\n 20 RETURN\nEnd Function\n\nFunction get_supply_per_asset(asset_address String) Uint64\n 10 IF EXISTS(asset_address+\":BOO\") THEN GOTO 30\n 20 RETURN 0\n 30 RETURN LOAD(asset_address+\":BOO\")\nEnd Function\n\nFunction set_dero_reserve_per_asset(amount Uint64, asset_address String)\n 10 STORE(asset_address+\":DERO\", amount)\n 20 RETURN \nEnd Function\n\nFunction get_dero_reserve_per_asset(asset_address String) Uint64\n 10 RETURN LOAD(asset_address+\":DERO\")\nEnd Function\n\nFunction set_asset_reserve(amount Uint64, asset_address String) \n 10 STORE(asset_address, amount)\n 20 RETURN \nEnd Function\n\nFunction get_asset_reserve(asset_address String) Uint64\n 20 RETURN LOAD(asset_address)\nEnd Function\n\nFunction increase_provider_liquidity_by(provider_address String, amount Uint64, asset_address String)\n 10 IF EXISTS(ADDRESS_STRING(provider_address)+\":BOO:\"+asset_address) THEN GOTO 40\n 20 STORE(ADDRESS_STRING(provider_address)+\":BOO:\"+asset_address, amount)\n 30 RETURN\n 40 STORE(ADDRESS_STRING(provider_address)+\":BOO:\"+asset_address, get_provider_liquidity(provider_address, asset_address) + amount)\n 50 RETURN\nEnd Function\n\nFunction decrease_provider_liquidity_by(provider_address String, amount Uint64, asset_address String)\n 10 STORE(ADDRESS_STRING(provider_address)+\":BOO:\"+asset_address, get_provider_liquidity(provider_address, asset_address) - amount)\n 20 RETURN\nEnd Function\n\nFunction get_provider_liquidity(provider_address String, asset_address String) Uint64\n 10 RETURN LOAD(ADDRESS_STRING(provider_address)+\":BOO:\"+asset_address)\nEnd Function\n\nFunction will_addition_overflow(a Uint64, b Uint64) Uint64\n 10 IF (a \u003e 18446744073709551615 - b) THEN GOTO 100\n 20 RETURN 0 // No overflow\n 100 RETURN 1 // Overflow\nEnd Function\n\n// From Pieswap\n// lossless (a * b ) / c\nFunction mult_div(a Uint64, b Uint64, c Uint64) Uint64\n\t10 DIM base, maxdiv AS Uint64\n\t20 LET base = 4294967296\t// (1\u003c\u003c32)\n\t30 LET maxdiv = (base-1)*base + (base-1)\n\n\t50 DIM res AS Uint64\n\t60 LET res = (a/c) * b + (a%c) * (b/c)\n\t70 LET a = a % c\n\t80 LET b = b % c\n\t90 IF (a == 0 || b == 0) THEN GOTO 1000\n\n\t100 IF (c \u003e= base) THEN GOTO 200\n\t110 LET res = res + (a*b/c)\n\t120 GOTO 1000\n\n\t200 DIM norm AS Uint64\n\t210 LET norm = maxdiv/c\n\t220 LET c = c * norm\n\t230 LET a = a * norm\n\n\t300 DIM ah, al, bh, bl, ch, cl AS Uint64\n\t310 LET ah = a / base\n\t320 LET al = a % base\n\t330 LET bh = b / base\n\t340 LET bl = b % base\n\t350 LET ch = c / base\n\t360 LET cl = c % base\n\n\t400 DIM p0, p1, p2 AS Uint64\n\t410 LET p0 = al*bl\n\t420 LET p1 = p0 / base + al*bh\n\t430 LET p0 = p0 % base\n\t440 LET p2 = p1 / base + ah*bh\n\t450 LET p1 = (p1 % base) + ah*bl\n\t460 LET p2 = p2 + p1 / base\n\t470 LET p1 = p1 % base\n\n\t500 DIM q0, q1, rhat AS Uint64\n\t510 LET p2 = p2 % c\n\t520 LET q1 = p2 / ch\n\t530 LET rhat = p2 % ch\n\n\t600 IF (q1 \u003c base \u0026\u0026 (rhat \u003e= base || q1*cl \u003c= rhat*base+p1)) THEN GOTO 700\n\t610 LET q1 = q1 - 1\n\t620 LET rhat = rhat + ch\n\t630 GOTO 600\n\n\t700 LET p1 = ((p2 % base) * base + p1) - q1 * cl\n\t710 LET p2 = (p2 / base * base + p1 / base) - q1 * ch\n\t720 LET p1 = (p1 % base) + (p2 % base) * base\n\t730 LET q0 = p1 / ch\n\t740 LET rhat = p1 % ch\n\n\t800 IF (q0 \u003c base \u0026\u0026 (rhat \u003e= base || q0*cl \u003c= rhat*base+p0)) THEN GOTO 900\n\t810 LET q0 = q0 - 1\n\t820 LET rhat = rhat + ch\n\t830 GOTO 800\n\n\t900 LET res = res + q0 + q1 * base\n\n\t1000 RETURN res\nEnd Function\n \n// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)\nFunction sqrt(y Uint64) Uint64\n 10 IF y \u003e 3 THEN GOTO 20 ELSE GOTO 90\n 20 DIM x,z as Uint64\n 30 LET z = y \n 40 LET x = y / 2 + 1\n 50 IF x \u003c z THEN GOTO 60 ELSE GOTO 130\n 60 LET z = x\n 70 LET x = (y / x + x) / 2\n 80 GOTO 50\n 90 IF y != 0 THEN GOTO 110 ELSE GOTO 120\n 110 RETURN 1\n 120 RETURN 0\n 130 RETURN z\nEnd Function\n\nFunction UpdateCode(code String) Uint64 \n 10 IF LOAD(\"owner\") == SIGNER() THEN GOTO 30 \n 20 RETURN 1\n 30 UPDATE_SC_CODE(code)\n 40 RETURN 0\nEnd Function\n"}]
Code:
// .-.
// (o o)
// | O \ boo!
// | \
// '~~~~'
// ?
// /\ ___
// (oo) |RIP|
// /||\ |___|
//////////////////////////////////////
// Ghost Exchange v1.1
// Use at your own risk
// For more info visit: ghost.trading
//////////////////////////////////////
Function Initialize() Uint64
10 STORE("owner", SIGNER())
20 STORE("feeTo", SIGNER())
30 STORE("nameHdr", "Ghost Exchange")
40 STORE("descrHdr", "Phantom-powered finance")
50 RETURN 0
End Function
// Provided liquidity will be tracked using BOO tokens.
// BOO TOKENS ARE NOT ASSETS. Each account's BOO balance per token pair is stored
// inside the Smart Contract as a variable with the following schema:
// <provider_address>:BOO:<asset_address>
// The total liquidity provided by all LPs of a pair at a given moment is denominated supply.
// supply is tracked inside the Smart Contract as a variable with the following schema:
// <asset_address>:BOO
// Deposit assets and DERO at current ratio to get BOO tokens.
// {asset_address} The deposited asset's SCID
// {min_liquidity} Minimum number of BOO sender will get if total BOO supply is greater than 0.
// {min_liquidity} does nothing when total BOO supply is 0.
Function AddLiquidity(asset_address String, min_liquidity Uint64) Uint64
01 DIM total_liquidity, dero_reserve, asset_reserve, asset_amount, liquidity_minted, dero_deposited, asset_deposited, SYS_MAX_VALUE as Uint64
02 LET SYS_MAX_VALUE = 18446744073709551 // Uint64.Max/1000 to protect from infinite supply tokens
03 LET dero_deposited = DEROVALUE()
04 LET asset_deposited = ASSETVALUE(HEXDECODE(asset_address))
10 IF (asset_deposited > 0 & dero_deposited > 0 ) THEN GOTO 30
20 GOTO 666
30 LET total_liquidity = get_supply_per_asset(asset_address)
// Pair already exists
40 IF total_liquidity > 0 THEN GOTO 41 ELSE GOTO 60
41 IF min_liquidity > 0 THEN GOTO 43
42 GOTO 666
43 LET dero_reserve = get_dero_reserve_per_asset(asset_address)
44 LET asset_reserve = get_asset_reserve(asset_address)
45 IF will_addition_overflow(asset_reserve, asset_deposited) == 1 THEN GOTO 666
46 mintFee(dero_reserve, asset_reserve, asset_address)
47 LET total_liquidity = get_supply_per_asset(asset_address) // Must be defined again since totalSupply can update in mintFee
48 LET asset_amount = mult_div(dero_deposited, asset_reserve , dero_reserve + 1)
49 LET liquidity_minted = mult_div(dero_deposited, total_liquidity, dero_reserve)
50 IF asset_deposited >= asset_amount & liquidity_minted >= min_liquidity & (asset_reserve + asset_deposited <= SYS_MAX_VALUE) THEN GOTO 52
51 GOTO 666
52 increase_provider_liquidity_by(SIGNER(), liquidity_minted, asset_address)
53 set_supply_per_asset(total_liquidity + liquidity_minted, asset_address)
54 IF asset_deposited == asset_amount THEN GOTO 56
// Return any reminding assets
55 SEND_ASSET_TO_ADDRESS(SIGNER(), asset_deposited - asset_amount, HEXDECODE(asset_address))
// Increase the dero reserve record for this asset
56 set_dero_reserve_per_asset(dero_reserve + dero_deposited, asset_address)
// Increase the asset reserve record
57 set_asset_reserve(asset_reserve + asset_amount, asset_address)
58 STORE(asset_address+":rootKLast", sqrt(dero_reserve + dero_deposited) * sqrt(asset_reserve + asset_amount))
// Return gracefully
59 GOTO 70
// else
// Creating pair
60 IF dero_deposited >= 1000 & asset_deposited <= SYS_MAX_VALUE THEN GOTO 62
61 GOTO 666
// Initialize the asset reserve record for this asset
62 set_asset_reserve(asset_deposited, asset_address)
63 DIM initial_liquidity as Uint64
64 LET initial_liquidity = dero_deposited
65 set_supply_per_asset(initial_liquidity, asset_address)
// Initialize the dero reserve record for this asset
66 set_dero_reserve_per_asset(dero_deposited, asset_address)
67 STORE(asset_address+":rootKLast", sqrt(asset_deposited) * sqrt(dero_deposited))
68 increase_provider_liquidity_by(SIGNER(), initial_liquidity, asset_address)
70 RETURN 0
666 RETURN 1
End Function
// Burn BOO tokens to withdraw Dero & assets at current ratio.
// {amount} Amount of BOO burned.
// {min_dero} Minimum DERO withdrawn.
// {min_assets} Minimum assets withdrawn.
// {asset_address} The deposited asset's SCID
Function RemoveLiquidity(amount Uint64, min_dero Uint64, min_assets Uint64, asset_address String) Uint64
01 DIM total_liquidity, dero_reserve, asset_reserve, dero_amount, asset_amount as Uint64
10 IF amount > 0 & (min_dero > 0 & min_assets > 0) & ( get_provider_liquidity(SIGNER(), asset_address) >= amount ) THEN GOTO 30
20 GOTO 666
30 LET total_liquidity = get_supply_per_asset(asset_address)
40 IF total_liquidity > 0 THEN GOTO 60
50 GOTO 666
60 LET asset_reserve = get_asset_reserve(asset_address)
70 LET dero_reserve = get_dero_reserve_per_asset(asset_address)
80 mintFee(dero_reserve, asset_reserve, asset_address)
90 LET total_liquidity = get_supply_per_asset(asset_address) // Must be defined again since totalSupply can update in mintFee
100 LET dero_amount = mult_div(amount, dero_reserve, total_liquidity)
110 LET asset_amount = mult_div(amount, asset_reserve, total_liquidity)
120 IF dero_amount >= min_dero & asset_amount >= min_assets THEN GOTO 140
130 GOTO 666
140 decrease_provider_liquidity_by(SIGNER(), amount, asset_address)
150 set_supply_per_asset(total_liquidity - amount, asset_address)
160 set_dero_reserve_per_asset(dero_reserve - dero_amount, asset_address)
170 set_asset_reserve(asset_reserve - asset_amount, asset_address)
180 STORE(asset_address+":rootKLast", sqrt(dero_reserve - dero_amount) * sqrt(asset_reserve - asset_amount) )
190 SEND_DERO_TO_ADDRESS(SIGNER(), dero_amount)
200 SEND_ASSET_TO_ADDRESS(SIGNER(),asset_amount, HEXDECODE(asset_address))
210 RETURN 0
666 RETURN 1
End Function
// Pricing function for converting between DERO & Assets.
// {input_amount} Amount of DERO or Assets being sold.
// {input_reserve} Amount of DERO or Assets (input type) in exchange reserves.
// {output_reserve} Amount of DERO or Assets (output type) in exchange reserves.
Function getInputPrice(input_amount Uint64, input_reserve Uint64, output_reserve Uint64) Uint64
01 DIM SYS_MAX_VALUE as Uint64
02 LET SYS_MAX_VALUE = 18446744073709551
10 IF input_reserve > 0 & output_reserve > 0 & input_amount <= SYS_MAX_VALUE THEN GOTO 30
20 PANIC
30 DIM input_amount_with_fee as Uint64
40 LET input_amount_with_fee = input_amount * 997
50 IF will_addition_overflow(input_reserve * 1000, input_amount * 1000) == 0 THEN GOTO 70
60 PANIC // Infinite supply token attack
70 RETURN mult_div(input_amount_with_fee, output_reserve, (input_reserve * 1000) + input_amount_with_fee)
End Function
// Pricing function for converting between DERO & Assets.
// {output_amount} Amount of DERO or Assets being bought.
// {input_reserve} Amount of DERO or Assets (input type) in exchange reserves.
// {output_reserve} Amount of DERO or Assets (output type) in exchange reserves.
Function getOutputPrice(output_amount Uint64, input_reserve Uint64, output_reserve Uint64) Uint64
10 IF input_reserve > 0 && output_reserve > 0 && output_reserve > output_amount THEN GOTO 30
20 PANIC
30 RETURN mult_div(input_reserve * 1000, output_amount, (output_reserve - output_amount) * 997 + 1)
End Function
Function deroToAssetInput(dero_sold Uint64, min_assets Uint64, asset_address String) Uint64
10 IF (dero_sold > 0 & min_assets > 0) THEN GOTO 30
20 PANIC
30 DIM assets_bought, asset_reserve, dero_reserve as Uint64
40 LET asset_reserve = get_asset_reserve(asset_address)
50 LET dero_reserve = get_dero_reserve_per_asset(asset_address)
60 LET assets_bought = getInputPrice(dero_sold, dero_reserve, asset_reserve)
70 IF assets_bought >= min_assets THEN GOTO 90
80 PANIC
90 SEND_ASSET_TO_ADDRESS(SIGNER(), assets_bought, HEXDECODE(asset_address))
100 set_asset_reserve(asset_reserve - assets_bought, asset_address)
110 set_dero_reserve_per_asset(dero_reserve + dero_sold, asset_address)
120 RETURN 0
End Function
// Convert DERO to Assets.
// User specifies exact input (DEROVALUE()).
// User cannot specify minimum output.
// {asset_address} Address of the asset to trade.
Function DeroToAssetSwapInput(asset_address String) Uint64
10 RETURN deroToAssetInput(DEROVALUE(), 1, asset_address)
End Function
// Convert DERO to Assets.
// User specifies exact input (DEROVALUE()) & minimum output.
// {min_assets} Minimum Assets bought.
// {asset_address} Address of the asset to trade.
Function DeroToAssetSwapInputMin(min_assets Uint64, asset_address String) Uint64
10 RETURN deroToAssetInput(DEROVALUE(), min_assets, asset_address)
End Function
Function deroToAssetOutput(assets_bought Uint64, max_dero Uint64, asset_address String) Uint64
10 IF (assets_bought > 0 & max_dero > 0) THEN GOTO 30
20 PANIC
30 DIM asset_reserve, dero_reserve, dero_sold, dero_refund as Uint64
40 LET asset_reserve = get_asset_reserve(asset_address)
41 LET dero_reserve = get_dero_reserve_per_asset(asset_address)
50 LET dero_sold = getOutputPrice(assets_bought, dero_reserve, asset_reserve)
60 IF dero_sold > max_dero THEN GOTO 61 ELSE GOTO 70
61 PANIC
70 LET dero_refund = max_dero - dero_sold
80 IF dero_refund == 0 THEN GOTO 100
90 SEND_DERO_TO_ADDRESS(SIGNER(), dero_refund)
100 SEND_ASSET_TO_ADDRESS(SIGNER(), assets_bought, HEXDECODE(asset_address))
110 set_asset_reserve(asset_reserve - assets_bought, asset_address)
120 set_dero_reserve_per_asset(dero_reserve + dero_sold, asset_address)
130 RETURN 0
End Function
// Convert DERO to Assets.
// User specifies maximum input (DEROVALUE()) & exact output.
// {assets_bought} Amount of Assets bought.
// {asset_address} Address of the asset to trade.
Function DeroToAssetSwapOutput(assets_bought Uint64, asset_address String) Uint64
10 RETURN deroToAssetOutput(assets_bought, DEROVALUE(), asset_address)
End Function
Function assetToDeroInput(assets_sold Uint64, min_dero Uint64, asset_address String) Uint64
10 IF (assets_sold > 0 & min_dero > 0) THEN GOTO 30
20 PANIC
30 DIM asset_reserve, dero_bought as Uint64
40 LET asset_reserve = get_asset_reserve(asset_address)
50 LET dero_bought = getInputPrice(assets_sold, asset_reserve, get_dero_reserve_per_asset(asset_address))
60 IF dero_bought >= min_dero THEN GOTO 80
70 PANIC
80 SEND_DERO_TO_ADDRESS(SIGNER(),dero_bought)
90 set_dero_reserve_per_asset(get_dero_reserve_per_asset(asset_address) - dero_bought, asset_address)
100 set_asset_reserve(asset_reserve + assets_sold, asset_address)
110 RETURN 0
End Function
// Convert Assets to DERO.
// User specifies exact input (ASSETVALUE(asset_address)) & minimum output.
// {min_dero} Minimum DERO purchased.
// {asset_address} Address of the asset to trade.
Function AssetToDeroSwapInput(min_dero Uint64, asset_address String) Uint64
10 RETURN assetToDeroInput(ASSETVALUE(HEXDECODE(asset_address)), min_dero, asset_address)
End Function
Function assetToDeroOutput(dero_bought Uint64, max_assets Uint64 , asset_address String) Uint64
10 IF dero_bought > 0 THEN GOTO 30
20 PANIC
30 DIM asset_reserve, assets_sold, asset_refund as Uint64
40 LET asset_reserve = get_asset_reserve(asset_address)
50 LET assets_sold = getOutputPrice(dero_bought, asset_reserve, get_dero_reserve_per_asset(asset_address))
// assets_sold is always > zero
60 IF max_assets >= assets_sold THEN GOTO 80
70 PANIC
80 SEND_DERO_TO_ADDRESS(SIGNER(), dero_bought)
90 LET asset_refund = max_assets - assets_sold
100 IF asset_refund == 0 THEN GOTO 120
110 SEND_ASSET_TO_ADDRESS(SIGNER(), asset_refund, HEXDECODE(asset_address))
120 set_dero_reserve_per_asset(get_dero_reserve_per_asset(asset_address) - dero_bought, asset_address)
130 set_asset_reserve(asset_reserve + assets_sold, asset_address)
140 RETURN 0
End Function
// Convert Assets to DERO.
// User specifies maximum input (ASSETVALUE(asset_address)) & exact output.
// {dero_bought} Amount of DERO purchased.
// {asset_address} Address of the asset to trade.
Function AssetToDeroSwapOutput(dero_bought Uint64, asset_address String) Uint64
10 RETURN assetToDeroOutput(dero_bought, ASSETVALUE(HEXDECODE(asset_address)), asset_address)
End Function
// Public price function for DERO to Asset trades with an exact input.
// {dero_sold} Amount of DERO sold.
// {asset_address} Address of the asset to trade.
// @returns Amount of Assets that can be bought with input DERO.
Function GetDeroToAssetInputPrice(dero_sold Uint64, asset_address String) Uint64
10 IF dero_sold > 0 THEN GOTO 30
20 RETURN 0
30 RETURN getInputPrice(dero_sold, get_dero_reserve_per_asset(asset_address), get_asset_reserve(asset_address))
End Function
// Public price function for DERO to Asset trades with an exact output.
// {assets_bought} Amount of Assets bought.
// {asset_address} Address of the asset to trade.
// @returns Amount of DERO needed to buy output Assets.
Function GetDeroToAssetOutputPrice(assets_bought Uint64, asset_address String) Uint64
10 IF assets_bought > 0 THEN GOTO 30
20 RETURN 0
30 RETURN getOutputPrice(assets_bought, get_dero_reserve_per_asset(asset_address), get_asset_reserve(asset_address))
End Function
// Public price function for Asset to DERO trades with an exact input.
// {assets_sold} Amount of Assets sold.
// {asset_address} Address of the asset to trade.
// @returns Amount of DERO that can be bought with input Assets.
Function GetAssetToDeroInputPrice(assets_sold Uint64, asset_address String) Uint64
10 IF assets_sold > 0 THEN GOTO 30
20 RETURN 0
30 RETURN getInputPrice(assets_sold, get_asset_reserve(asset_address), get_dero_reserve_per_asset(asset_address))
End Function
// Public price function for Asset to DERO trades with an exact output.
// {dero_bought} Amount of output DERO.
// {asset_address} Address of the asset to trade.
// @returns Amount of Assets needed to buy output DERO.
Function GetAssetToDeroOutputPrice(dero_bought Uint64, asset_address String) Uint64
10 IF dero_bought > 0 THEN GOTO 30
20 RETURN 0
30 RETURN getOutputPrice(dero_bought, get_asset_reserve(asset_address), get_dero_reserve_per_asset(asset_address))
End Function
Function mintFee(reserve0 Uint64, reserve1 Uint64, asset_address String)
10 DIM feeTo as String
11 DIM rootKLast as Uint64
20 LET feeTo = LOAD("feeTo")
// Get the last root k for the asset
30 LET rootKLast = LOAD(asset_address+":rootKLast")
40 IF rootKLast != 0 THEN GOTO 50
41 RETURN
50 DIM rootK as Uint64
60 LET rootK = sqrt(reserve0) * sqrt(reserve1)
70 IF rootK > rootKLast THEN GOTO 80
71 RETURN
80 DIM supply, liquidity_minted as Uint64
90 LET supply = get_supply_per_asset(asset_address)
100 LET liquidity_minted = mult_div(supply, rootK - rootKLast, rootK * 5 + rootKLast)
110 IF liquidity_minted > 0 THEN GOTO 111 ELSE GOTO 120
111 increase_provider_liquidity_by(feeTo, liquidity_minted, asset_address)
112 set_supply_per_asset(supply + liquidity_minted, asset_address)
120 RETURN
End Function
// Helper functions
// Encapsulated to better understand what's happening and absctract BOO storage schema
// from main code.
Function set_supply_per_asset(amount Uint64, asset_address String)
10 STORE(asset_address+":BOO", amount)
20 RETURN
End Function
Function get_supply_per_asset(asset_address String) Uint64
10 IF EXISTS(asset_address+":BOO") THEN GOTO 30
20 RETURN 0
30 RETURN LOAD(asset_address+":BOO")
End Function
Function set_dero_reserve_per_asset(amount Uint64, asset_address String)
10 STORE(asset_address+":DERO", amount)
20 RETURN
End Function
Function get_dero_reserve_per_asset(asset_address String) Uint64
10 RETURN LOAD(asset_address+":DERO")
End Function
Function set_asset_reserve(amount Uint64, asset_address String)
10 STORE(asset_address, amount)
20 RETURN
End Function
Function get_asset_reserve(asset_address String) Uint64
20 RETURN LOAD(asset_address)
End Function
Function increase_provider_liquidity_by(provider_address String, amount Uint64, asset_address String)
10 IF EXISTS(ADDRESS_STRING(provider_address)+":BOO:"+asset_address) THEN GOTO 40
20 STORE(ADDRESS_STRING(provider_address)+":BOO:"+asset_address, amount)
30 RETURN
40 STORE(ADDRESS_STRING(provider_address)+":BOO:"+asset_address, get_provider_liquidity(provider_address, asset_address) + amount)
50 RETURN
End Function
Function decrease_provider_liquidity_by(provider_address String, amount Uint64, asset_address String)
10 STORE(ADDRESS_STRING(provider_address)+":BOO:"+asset_address, get_provider_liquidity(provider_address, asset_address) - amount)
20 RETURN
End Function
Function get_provider_liquidity(provider_address String, asset_address String) Uint64
10 RETURN LOAD(ADDRESS_STRING(provider_address)+":BOO:"+asset_address)
End Function
Function will_addition_overflow(a Uint64, b Uint64) Uint64
10 IF (a > 18446744073709551615 - b) THEN GOTO 100
20 RETURN 0 // No overflow
100 RETURN 1 // Overflow
End Function
// From Pieswap
// lossless (a * b ) / c
Function mult_div(a Uint64, b Uint64, c Uint64) Uint64
10 DIM base, maxdiv AS Uint64
20 LET base = 4294967296 // (1<<32)
30 LET maxdiv = (base-1)*base + (base-1)
50 DIM res AS Uint64
60 LET res = (a/c) * b + (a%c) * (b/c)
70 LET a = a % c
80 LET b = b % c
90 IF (a == 0 || b == 0) THEN GOTO 1000
100 IF (c >= base) THEN GOTO 200
110 LET res = res + (a*b/c)
120 GOTO 1000
200 DIM norm AS Uint64
210 LET norm = maxdiv/c
220 LET c = c * norm
230 LET a = a * norm
300 DIM ah, al, bh, bl, ch, cl AS Uint64
310 LET ah = a / base
320 LET al = a % base
330 LET bh = b / base
340 LET bl = b % base
350 LET ch = c / base
360 LET cl = c % base
400 DIM p0, p1, p2 AS Uint64
410 LET p0 = al*bl
420 LET p1 = p0 / base + al*bh
430 LET p0 = p0 % base
440 LET p2 = p1 / base + ah*bh
450 LET p1 = (p1 % base) + ah*bl
460 LET p2 = p2 + p1 / base
470 LET p1 = p1 % base
500 DIM q0, q1, rhat AS Uint64
510 LET p2 = p2 % c
520 LET q1 = p2 / ch
530 LET rhat = p2 % ch
600 IF (q1 < base && (rhat >= base || q1*cl <= rhat*base+p1)) THEN GOTO 700
610 LET q1 = q1 - 1
620 LET rhat = rhat + ch
630 GOTO 600
700 LET p1 = ((p2 % base) * base + p1) - q1 * cl
710 LET p2 = (p2 / base * base + p1 / base) - q1 * ch
720 LET p1 = (p1 % base) + (p2 % base) * base
730 LET q0 = p1 / ch
740 LET rhat = p1 % ch
800 IF (q0 < base && (rhat >= base || q0*cl <= rhat*base+p0)) THEN GOTO 900
810 LET q0 = q0 - 1
820 LET rhat = rhat + ch
830 GOTO 800
900 LET res = res + q0 + q1 * base
1000 RETURN res
End Function
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
Function sqrt(y Uint64) Uint64
10 IF y > 3 THEN GOTO 20 ELSE GOTO 90
20 DIM x,z as Uint64
30 LET z = y
40 LET x = y / 2 + 1
50 IF x < z THEN GOTO 60 ELSE GOTO 130
60 LET z = x
70 LET x = (y / x + x) / 2
80 GOTO 50
90 IF y != 0 THEN GOTO 110 ELSE GOTO 120
110 RETURN 1
120 RETURN 0
130 RETURN z
End Function
Function UpdateCode(code String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 UPDATE_SC_CODE(code)
40 RETURN 0
End Function