@version 1 condition triggered_by: transaction, on: add_pool(token1_address, token2_address, pool_creation_address), as: [ type: "transfer", content: ( valid? = false token1_address = String.to_uppercase(token1_address) token2_address = String.to_uppercase(token2_address) pool_exists? = get_pool_addresses(token1_address, token2_address) != nil if token1_address != token2_address && !pool_exists? do # Could create a new function Chain.get_transactions(list_of_address) pool_transaction = Chain.get_transaction(pool_creation_address) # Ensure tokens exists and lp token definition is good token1_symbol = get_token_symbol(token1_address) token2_symbol = get_token_symbol(token2_address) valid_definition? = false if token1_symbol != nil && token2_symbol != nil && pool_transaction != nil && pool_transaction.type == "token" do expected_content = get_lp_token_definition(token1_symbol, token2_symbol) valid_definition? = Json.parse(pool_transaction.content) == Json.parse(expected_content) end valid_code? = false pool_genesis_address = nil if valid_definition? do # Ensure code is valid pool_genesis_address = Chain.get_genesis_address(pool_creation_address) expected_code = get_pool_code(token1_address, token2_address, pool_genesis_address, pool_creation_address) valid_code? = Code.is_same?(pool_transaction.code, expected_code) end if valid_code? do # Ensure liquidity is provided to the pool valid? = List.in?(transaction.recipients, pool_genesis_address) end log("valid_definition? #{valid_definition?}") log("valid_code? #{valid_code?}") log("valid? #{valid?}") end valid? ) ] actions triggered_by: transaction, on: add_pool(token1_address, token2_address, pool_creation_address) do token1_address = String.to_uppercase(token1_address) token2_address = String.to_uppercase(token2_address) pool_genesis_address = Chain.get_genesis_address(pool_creation_address) pool_id = get_pool_id(token1_address, token2_address) pools = State.get("pools", Map.new()) pool_data = [ address: pool_genesis_address, lp_token_address: pool_creation_address ] pools = Map.set(pools, pool_id, pool_data) State.set("pools", pools) end condition triggered_by: transaction, on: update_code(new_code), as: [ previous_public_key: ( # Router code can only be updated from the master chain of the dex # Transaction is not yet validated so we need to use previous address # to get the genesis address previous_address = Chain.get_previous_address() Chain.get_genesis_address(previous_address) == 0x0000339c1c65f0b2eb77af8ab68a38cbc6d2f6828b9ad3e90bb2e7fce3a99a428aff ), code: Code.is_valid?(new_code) ] actions triggered_by: transaction, on: update_code(new_code) do Contract.set_type("contract") Contract.set_code(new_code) end condition triggered_by: transaction, on: update_pools_code(), as: [ previous_public_key: ( # Pool code update can only be requested from the master chain of the dex # Transaction is not yet validated so we need to use previous address # to get the genesis address previous_address = Chain.get_previous_address() Chain.get_genesis_address(previous_address) == 0x0000339c1c65f0b2eb77af8ab68a38cbc6d2f6828b9ad3e90bb2e7fce3a99a428aff ) ] actions triggered_by: transaction, on: update_pools_code() do pools = State.get("pools", Map.new()) if Map.size(pools) > 0 do for pool_info in Map.values(pools) do Contract.add_recipient address: pool_info.address, action: "update_code", args: [] end Contract.set_type("transfer") end end fun get_pool_id(token1_address, token2_address) do if token1_address > token2_address do temp = token1_address token1_address = token2_address token2_address = temp end "#{token1_address}/#{token2_address}" end fun get_token_symbol(token_address) do if token_address == "UCO" do "UCO" else tx = Chain.get_transaction(token_address) # Transaction must have type token # Token must by fungible symbol = nil if tx != nil && tx.type == "token" do token_definition = Json.parse(tx.content) if token_definition.type == "fungible" do symbol = Map.get(token_definition, "symbol", nil) end end symbol end end export fun get_pool_addresses(token1_address, token2_address) do token1_address = String.to_uppercase(token1_address) token2_address = String.to_uppercase(token2_address) if token1_address > token2_address do temp = token1_address token1_address = token2_address token2_address = temp end pool_id = "#{token1_address}/#{token2_address}" pools = State.get("pools", Map.new()) Map.get(pools, pool_id, nil) end export fun get_pool_list() do pools = State.get("pools", Map.new()) list = [] for pool_id in Map.keys(pools) do pool = Map.get(pools, pool_id) pool = Map.set(pool, "tokens", pool_id) list = List.prepend(list, pool) end list end export fun get_pool_code(token1_address, token2_address, pool_address, lp_token_address) do code = "" token1_address = String.to_uppercase(token1_address) token2_address = String.to_uppercase(token2_address) pool_address = String.to_uppercase(pool_address) lp_token_address = String.to_uppercase(lp_token_address) if token1_address != token2_address do if token1_address > token2_address do temp = token1_address token1_address = token2_address token2_address = temp end code = """ @version 1 condition triggered_by: transaction, on: add_liquidity(token1_min_amount, token2_min_amount), as: [ token_transfers: ( valid_amounts? = false valid_liquidity? = false user_amounts = get_user_transfers_amount(transaction) if user_transfers.token1 > 0 && user_transfers.token2 > 0 do lp_token_supply = State.get("lp_token_supply", 0) reserves = State.get("reserves", [token1: 0, token2: 0]) final_amounts = nil if lp_token_supply != 0 do # Returns final_amounts.token1 == 0 in case of insufficient funds final_amounts = get_final_amounts(user_amounts, reserves, token1_min_amount, token2_min_amount) else final_amounts = [token1: user_amounts.token1, token2: user_amounts.token2] end if final_amounts.token1 != 0 do valid_amounts? = true pool_balances = get_pool_balances() # Amount = final amounts + potential current balance over reserve token1_amount = final_amounts.token1 + (pool_balances.token1 - reserves.token1) token2_amount = final_amounts.token2 + (pool_balances.token2 - reserves.token2) lp_token_to_mint = get_lp_token_to_mint(token1_amount, token2_amount) valid_liquidity? = lp_token_to_mint > 0 end end valid_amounts? && valid_liquidity? ) ] actions triggered_by: transaction, on: add_liquidity(token1_min_amount, token2_min_amount) do pool_balances = get_pool_balances() user_amounts = get_user_transfers_amount(transaction) lp_token_supply = State.get("lp_token_supply", 0) reserves = State.get("reserves", [token1: 0, token2: 0]) final_amounts = get_final_amounts(user_amounts, reserves, token1_min_amount, token2_min_amount) token1_to_refund = user_amounts.token1 - final_amounts.token1 token2_to_refund = user_amounts.token2 - final_amounts.token2 token1_amount = pool_balances.token1 - reserves.token1 - token1_to_refund token2_amount = pool_balances.token2 - reserves.token2 - token2_to_refund lp_token_to_mint = get_lp_token_to_mint(token1_amount, token2_amount) lp_token_to_mint_bigint = Math.trunc(lp_token_to_mint * 100_000_000) # Remove minimum liquidity if this is the first liquidity if the pool # First liquidity minted and burned on pool creation if lp_token_supply == 0 do lp_token_to_mint_bigint = lp_token_to_mint_bigint - 10 end token_specification = [ aeip: [8, 18, 19], supply: lp_token_to_mint_bigint, token_reference: 0x#{lp_token_address}, recipients: [ [to: transaction.address, amount: lp_token_to_mint_bigint] ] ] new_token1_reserve = pool_balances.token1 - token1_to_refund new_token2_reserve = pool_balances.token2 - token2_to_refund State.set("lp_token_supply", lp_token_supply + lp_token_to_mint) State.set("reserves", [token1: new_token1_reserve, token2: new_token2_reserve]) if token1_to_refund > 0 do Contract.add_token_transfer(to: transaction.address, amount: token1_to_refund, token_address: "#{token1_address}") end if token2_to_refund > 0 do if "#{token2_address}" == "UCO" do Contract.add_uco_transfer(to: transaction.address, amount: token2_to_refund) else Contract.add_token_transfer(to: transaction.address, amount: token2_to_refund, token_address: "#{token2_address}") end end Contract.set_type("token") Contract.set_content(Json.to_string(token_specification)) end condition triggered_by: transaction, on: remove_liquidity(), as: [ token_transfers: ( valid? = false user_amount = get_user_lp_amount(transaction.token_transfers) lp_token_supply = State.get("lp_token_supply", 0) if user_amount > 0 && lp_token_supply > 0 do pool_balances = get_pool_balances() token1_to_remove = (user_amount * pool_balances.token1) / lp_token_supply token2_to_remove = (user_amount * pool_balances.token2) / lp_token_supply valid? = token1_to_remove > 0 && token2_to_remove > 0 end valid? ) ] actions triggered_by: transaction, on: remove_liquidity() do user_amount = get_user_lp_amount(transaction.token_transfers) pool_balances = get_pool_balances() lp_token_supply = State.get("lp_token_supply") token1_to_remove = (user_amount * pool_balances.token1) / lp_token_supply token2_to_remove = (user_amount * pool_balances.token2) / lp_token_supply new_token1_reserve = pool_balances.token1 - token1_to_remove new_token2_reserve = pool_balances.token2 - token2_to_remove State.set("lp_token_supply", lp_token_supply - user_amount) State.set("reserves", [token1: new_token1_reserve, token2: new_token2_reserve]) Contract.set_type("transfer") Contract.add_token_transfer(to: transaction.address, amount: token1_to_remove, token_address: "#{token1_address}") if "#{token2_address}" == "UCO" do Contract.add_uco_transfer(to: transaction.address, amount: token2_to_remove) else Contract.add_token_transfer(to: transaction.address, amount: token2_to_remove, token_address: "#{token2_address}") end end condition triggered_by: transaction, on: swap(min_to_receive), as: [ token_transfers: ( valid? = false transfer = get_user_transfer(transaction) if transfer != nil do swap = get_swap_infos(transfer.token_address, transfer.amount) valid? = swap.output_amount > 0 && swap.output_amount >= min_to_receive end valid? ) ] actions triggered_by: transaction, on: swap(_min_to_receive) do transfer = get_user_transfer(transaction) swap = get_swap_infos(transfer.token_address, transfer.amount) pool_balances = get_pool_balances() token_to_send = nil if transfer.token_address == "#{token1_address}" do pool_balances = Map.set(pool_balances, "token2", pool_balances.token2 - swap.output_amount) token_to_send = "#{token2_address}" else pool_balances = Map.set(pool_balances, "token1", pool_balances.token1 - swap.output_amount) token_to_send = "#{token1_address}" end State.set("reserves", [token1: pool_balances.token1, token2: pool_balances.token2]) Contract.set_type("transfer") if token_to_send == "UCO" do Contract.add_uco_transfer(to: transaction.address, amount: swap.output_amount) else Contract.add_token_transfer(to: transaction.address, amount: swap.output_amount, token_address: token_to_send) end end condition triggered_by: transaction, on: update_code(), as: [ previous_public_key: ( # Pool code can only be updated from the router contract of the dex # Transaction is not yet validated so we need to use previous address # to get the genesis address previous_address = Chain.get_previous_address() Chain.get_genesis_address(previous_address) == 0x00000dd757ac0a67fd619c1d1c400037a38cdb0471e6496807d8b850dc422e5ca3aa ) ] actions triggered_by: transaction, on: update_code() do params = [ "#{token1_address}", "#{token2_address}", 0x#{pool_address}, 0x#{lp_token_address} ] new_code = Contract.call_function(0x00000dd757ac0a67fd619c1d1c400037a38cdb0471e6496807d8b850dc422e5ca3aa, "get_pool_code", params) if Code.is_valid?(new_code) && !Code.is_same?(new_code, contract.code) do Contract.set_type("contract") Contract.set_code(new_code) end end export fun get_ratio(token_address) do reserves = State.get("reserves", [token1: 0, token2: 0]) ratio = 0 token_address = String.to_uppercase(token_address) if reserves.token1 > 0 && reserves.token2 > 0 do if token_address == "#{token1_address}" do ratio = reserves.token2 / reserves.token1 else ratio = reserves.token1 / reserves.token2 end end ratio end export fun get_equivalent_amount(token_address, amount) do reserves = State.get("reserves", [token1: 0, token2: 0]) ratio = 0 token_address = String.to_uppercase(token_address) if reserves.token1 > 0 && reserves.token2 > 0 do if token_address == "#{token1_address}" do ratio = reserves.token2 / reserves.token1 else ratio = reserves.token1 / reserves.token2 end end amount * ratio end export fun get_lp_token_to_mint(token1_amount, token2_amount) do lp_token_supply = State.get("lp_token_supply", 0) reserves = State.get("reserves", [token1: 0, token2: 0]) if lp_token_supply == 0 || reserves.token1 == 0 || reserves.token2 == 0 do # First liquidity Math.sqrt(token1_amount * token2_amount) else mint_amount1 = (token1_amount * lp_token_supply) / reserves.token1 mint_amount2 = (token2_amount * lp_token_supply) / reserves.token2 if mint_amount1 < mint_amount2 do mint_amount1 else mint_amount2 end end end export fun get_swap_infos(token_address, amount) do output_amount = 0 fee = 0 price_impact = 0 reserves = State.get("reserves", [token1: 0, token2: 0]) token_address = String.to_uppercase(token_address) if reserves.token1 > 0 && reserves.token2 > 0 do fee = amount * 0.003 amount_with_fee = amount - fee market_price = 0 if token_address == "#{token1_address}" do market_price = amount_with_fee * (reserves.token2 / reserves.token1) amount = (amount_with_fee * reserves.token2) / (amount_with_fee + reserves.token1) if amount < reserves.token2 do output_amount = amount end else market_price = amount_with_fee * (reserves.token1 / reserves.token2) amount = (amount_with_fee * reserves.token1) / (amount_with_fee + reserves.token2) if amount < reserves.token1 do output_amount = amount end end if output_amount > 0 do price_impact = ((market_price / output_amount) - 1) * 100 end end [ output_amount: output_amount, fee: fee, price_impact: price_impact ] end export fun get_remove_amounts(lp_token_amount) do reserves = State.get("reserves", [token1: 0, token2: 0]) lp_token_supply = State.get("lp_token_supply", 0) token1_to_remove = 0 token2_to_remove = 0 if lp_token_supply > 0 && lp_token_amount < lp_token_supply do token1_to_remove = (lp_token_amount * reserves.token1) / lp_token_supply token2_to_remove = (lp_token_amount * reserves.token2) / lp_token_supply end [token1: token1_to_remove, token2: token2_to_remove] end export fun get_pool_infos() do reserves = State.get("reserves", [token1: 0, token2: 0]) [ token1: [ address: "#{token1_address}", reserve: reserves.token1 ], token2: [ address: "#{token2_address}", reserve: reserves.token2 ], lp_token: [ address: 0x#{lp_token_address}, supply: State.get("lp_token_supply", 0) ], fee: 0.3 ] end fun get_final_amounts(user_amounts, reserves, token1_min_amount, token2_min_amount) do final_token1_amount = 0 final_token2_amount = 0 if reserves.token1 > 0 && reserves.token2 > 0 do token2_ratio = reserves.token2 / reserves.token1 token2_equivalent_amount = user_amounts.token1 * token2_ratio if token2_equivalent_amount <= user_amounts.token2 && token2_equivalent_amount >= token2_min_amount do final_token1_amount = user_amounts.token1 final_token2_amount = token2_equivalent_amount else token1_ratio = reserves.token1 / reserves.token2 token1_equivalent_amount = user_amounts.token2 * token1_ratio if token1_equivalent_amount <= user_amounts.token1 && token1_equivalent_amount >= token1_min_amount do final_token1_amount = token1_equivalent_amount final_token2_amount = user_amounts.token2 end end else # No reserve final_token1_amount = user_amounts.token1 final_token2_amount = user_amounts.token2 end [token1: final_token1_amount, token2: final_token2_amount] end fun get_user_transfers_amount(tx) do contract_address = 0x#{pool_address} token1_amount = 0 token2_amount = 0 transfers = Map.get(tx.token_transfers, contract_address) uco_amount = Map.get(tx.uco_transfers, contract_address) if uco_amount != nil do transfers = List.prepend(transfers, [token_address: "UCO", amount: uco_amount]) end if List.size(transfers) == 2 do for transfer in transfers do if transfer.token_address == "#{token1_address}" do token1_amount = transfer.amount end if transfer.token_address == "#{token2_address}" do token2_amount = transfer.amount end end end [token1: token1_amount, token2: token2_amount] end fun get_user_transfer(tx) do contract_address = 0x#{pool_address} token_transfer = nil transfers = Map.get(tx.token_transfers, contract_address, []) uco_amount = Map.get(tx.uco_transfers, contract_address) if uco_amount != nil do transfers = List.prepend(transfers, [token_address: "UCO", amount: uco_amount]) end transfer = List.at(transfers, 0) tokens = [ "#{token1_address}", "#{token2_address}" ] if List.size(transfers) == 1 && List.in?(tokens, transfer.token_address) do token_transfer = transfer end token_transfer end fun get_user_lp_amount(token_transfers) do lp_token = 0x#{lp_token_address} lp_amount = 0 transfers = Map.get(token_transfers, Chain.get_burn_address(), []) for transfer in transfers do if transfer.token_address == lp_token do lp_amount = transfer.amount end end lp_amount end fun get_pool_balances() do balances = Chain.get_balance(contract.address) token2_balance = 0 if "#{token2_address}" == "UCO" do token2_balance = balances.uco else token2_id = [token_address: "#{token2_address}", token_id: 0] token2_balance = Map.get(balances.tokens, token2_id, 0) end token1_id = [token_address: "#{token1_address}", token_id: 0] [ token1: Map.get(balances.tokens, token1_id, 0), token2: token2_balance ] end """ end code end export fun get_lp_token_definition(token1_symbol, token2_symbol) do if token1_symbol > token2_symbol do temp = token1_symbol token1_symbol = token2_symbol token2_symbol = temp end lp_token = "lp_#{token1_symbol}_#{token2_symbol}" Json.to_string([ aeip: [2, 8, 18, 19], supply: 10, type: "fungible", symbol: lp_token, name: lp_token, allow_mint: true, properties: [ description: "LP token of AESwap" ], recipients: [ [ to: Chain.get_burn_address(), amount: 10 ] ] ]) end
Content (0 B)
{ "pools": { "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1/0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827": { "address": "00008526199282354F0CD6C3ACA8BB0E2946235ADBC5355C40137E68E8298C2D281C", "lp_token_address": "000065EF4CA7A6467C6915FE0EEAE5E4792F3FD9C69F6B356C88E3CBC74C0F7D9640" }, "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1/UCO": { "address": "0000C5618CA1340ECFAA24E7B1DD750A43E807BEB0DDDD878203716E5BEE2D68B434", "lp_token_address": "0000D4B08F7C754731C7B2316AC173CA7AF2567D2F351706BA9E4789E63B7283D26C" }, "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827/UCO": { "address": "0000151C7079B2344874DFDE4B7B982F127A4071E36C4726346A75E78319F83DB989", "lp_token_address": "00005CC1FEA9F0E49CF34CB6E37230EDFB9FB8949963EFF7E3171F37BFFDE7D64A25" }, "00003DF600E329199BF3EE8FBE2B8223413D70BCDD97E15089E6A74D94DE3F1173B4/UCO": { "address": "0000BD2E4C70AA0996096FB3F949FC011159D0314365692047C5DA2D0153B13EAC2D", "lp_token_address": "0000E2A41863FE1FA00F8E4C8CCA07C6735B79640D51FFD25FE35EA78C7389D1484F" }, "00006E0EAF729C9CC2197DD12F705C04F32BBAB0B326DC261BA7D12E7C391324B94D/UCO": { "address": "000026897C5E8ADE60CECFD5FD1B10F20B7F8E484A062A47EDA73F169157911FFC7A", "lp_token_address": "0000B47F113ECE5966F245D770B218F95E676262B82A91804393A5FA6138C1578170" } } }
-
Secret shared with 1 key
Encoded secret
8B1499306493758D19AEF74510F04ECBF9F38E19AAB296F3F58201896E06F6C87ED6B68D97B16E9F5AE2B2961D2E10DFD82D938594DA052B8EFCCBDC
Authorized keys
- 00017877BCF4122095926A49489009649603AB129822A19EF9D573B8FD714911ED7F
Contract recipients (0)
Inputs (0)
Contract inputs (0)
Unspent outputs (1)
-
From 000030BB...7B40At 2023-11-29 20:00:25 UTCAmount 0.00243166 UCO
Previous public key
00019C0B0760A79CB1849E7E2A84ED9CB03BEB99FA37600A362A34FBFA52DFFBB109
Previous signature
CF2190896E208DF8CE4F5C1E44CF5FBAEEA99ABF3D93D3642F5E033C55BEEDE34FCEC059C0324F81962BB1892BA094E2A72B790355467D07D2D73F4015B16903
Origin signature
3046022100813FD08C05BA8432D7931EE5583BEBD7ED5F5D0B8CAFEB7D2E0723A2C08C7736022100E4CB009EB06DA983B4876E9BBCAE717E8BFED07293C389E6B25479EFA799CD24
Proof of work
010204B3B2A53580086B9F36919CF40ABC55904729F78BF43673E216FAC1EB2451DD1E521879C6588F0CB09B150A103A39A73E2816B5ADF51F0721348BA3A66C33023B
Proof of integrity
007EDBFD2589F2328A16EAAB0BD22636948897FD42ED1F8F5A2FC09B3A00C9AF7D
Coordinator signature
2C9667655FD78961B584C43432EA4E7791CB1D978E5B59A1673A991AA9970F3553DA60EF0E8A1E690AE672E7ECC8166ED240112081147988ADF222E14DF8EC07
Validator #1 public key
0001B0A94804BF8ECC9897075C6207FF63EF4D339F57A0349888E6B77CD47DB53EF3
Validator #1 signature
DB8E1C61D5A833A8CBB518780A6A3121716C8886D1BC04B53DDD0EE41466E24F31ABCE59B19A40A99C65A16A8CB821A41239B6574CE4D70B17B8390414300D09
Validator #2 public key
00011B58ED42235461734EAF253BD97A80B92899ABCC3BE680D44B6825DD2A88A947
Validator #2 signature
1B01D184AAA32E2F66B187C8304B6F13AF70DA300BE83CE617C44E3A376880C1A33333C860A39D4C4682C94FBFC7F5BC26CACE08BC5A1FBCA838BEA9B90C2207