@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_amounts.token1 > 0 && user_amounts.token2 > 0
&& user_amounts.token1 >= token1_min_amount && user_amounts.token2 >= token2_min_amount 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 && final_amounts.token2 > 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: 0x0000A4CAB2362A97002EE0A6DD2013BEC3AF02C4D8C392712CFBC38F3E4809B9314C,
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: "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827")
end
if token2_to_refund > 0 do
if "UCO" == "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: "UCO")
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: "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827")
if "UCO" == "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: "UCO")
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
token1_volume = 0
token2_volume = 0
token1_fee = 0
token2_fee = 0
token1_protocol_fee = 0
token2_protocol_fee = 0
if transfer.token_address == "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827" do
pool_balances = Map.set(pool_balances, "token2", pool_balances.token2 - swap.output_amount)
token_to_send = "UCO"
token1_volume = transfer.amount
token1_fee = swap.fee
token1_protocol_fee = swap.protocol_fee
else
pool_balances = Map.set(pool_balances, "token1", pool_balances.token1 - swap.output_amount)
token_to_send = "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827"
token2_volume = transfer.amount
token2_fee = swap.fee
token2_protocol_fee = swap.protocol_fee
end
State.set("reserves", [token1: pool_balances.token1, token2: pool_balances.token2])
stats = State.get("stats", [
token1_total_fee: 0,
token2_total_fee: 0,
token1_total_volume: 0,
token2_total_volume: 0,
token1_total_protocol_fee: 0,
token2_total_protocol_fee: 0,
])
token1_total_fee = Map.get(stats, "token1_total_fee") + token1_fee
token2_total_fee = Map.get(stats, "token2_total_fee") + token2_fee
token1_total_volume = Map.get(stats, "token1_total_volume") + token1_volume
token2_total_volume = Map.get(stats, "token2_total_volume") + token2_volume
token1_total_protocol_fee = Map.get(stats, "token1_total_protocol_fee") + token1_protocol_fee
token2_total_protocol_fee = Map.get(stats, "token2_total_protocol_fee") + token2_protocol_fee
stats = Map.set(stats, "token1_total_fee", token1_total_fee)
stats = Map.set(stats, "token2_total_fee", token2_total_fee)
stats = Map.set(stats, "token1_total_volume", token1_total_volume)
stats = Map.set(stats, "token2_total_volume", token2_total_volume)
stats = Map.set(stats, "token1_total_protocol_fee", token1_total_protocol_fee)
stats = Map.set(stats, "token2_total_protocol_fee", token2_total_protocol_fee)
State.set("stats", stats)
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
if swap.protocol_fee > 0 do
if transfer.token_address == "UCO" do
Contract.add_uco_transfer(to: 0x0000CFC185E5836BDC967B1DF7E8C0D3D61A0F7B1101AA077A7B8E307C33310D0D2B, amount: swap.protocol_fee)
else
Contract.add_token_transfer(to: 0x0000CFC185E5836BDC967B1DF7E8C0D3D61A0F7B1101AA077A7B8E307C33310D0D2B, amount: swap.protocol_fee, token_address: transfer.token_address)
end
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) == 0x000066CD867DA536A73D39CF05174387923358DC0009A29CC7162D4AED00675DAB55
)
]
actions triggered_by: transaction, on: update_code() do
params = [
"0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827",
"UCO",
0x0000DD19DD796959B72998536F67814DCCABE156FF647E0E63E43395203908963767,
0x0000A4CAB2362A97002EE0A6DD2013BEC3AF02C4D8C392712CFBC38F3E4809B9314C
]
new_code = Contract.call_function(0x000086E60124C986EBCAA5AFFB7A3DB8213072A132233FE61CF45651FDCF3C4CECEA, "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
condition triggered_by: transaction, on: set_protocol_fee(new_protocol_fee), as: [
content: new_protocol_fee <= 1 && new_protocol_fee >= 0,
previous_public_key: (
# Pool code can only be updated from the master 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) == 0x0000E46F8E90074DDF1DFC46385E07D826D35251F3A7B7FF65AD6F7E4B138AFF7C10
)
]
actions triggered_by: transaction, on: set_protocol_fee(new_protocol_fee) do
State.set("protocol_fee", new_protocol_fee)
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 == "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827" 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 == "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827" 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
protocol_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.0025
protocol_fee = amount * State.get("protocol_fee", 0.25) / 100
amount_with_fee = amount - fee - protocol_fee
market_price = 0
if token_address == "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827" 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
# This check is necessary as there might be some approximation in small decimal calculation
if market_price > output_amount do
price_impact = ((market_price / output_amount) - 1) * 100
else
price_impact = 0
end
end
end
[
output_amount: output_amount,
fee: fee,
protocol_fee: protocol_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])
stats = State.get("stats", [
token1_total_fee: 0,
token2_total_fee: 0,
token1_total_volume: 0,
token2_total_volume: 0,
token1_total_protocol_fee: 0,
token2_total_protocol_fee: 0,
])
[
token1: [
address: "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827",
reserve: reserves.token1
],
token2: [
address: "UCO",
reserve: reserves.token2
],
lp_token: [
address: 0x0000A4CAB2362A97002EE0A6DD2013BEC3AF02C4D8C392712CFBC38F3E4809B9314C,
supply: State.get("lp_token_supply", 0)
],
fee: 0.25,
protocol_fee: State.get("protocol_fee", 0.25),
stats: stats
]
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 = 0x0000DD19DD796959B72998536F67814DCCABE156FF647E0E63E43395203908963767
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 == "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827" do
token1_amount = transfer.amount
end
if transfer.token_address == "UCO" do
token2_amount = transfer.amount
end
end
end
[token1: token1_amount, token2: token2_amount]
end
fun get_user_transfer(tx) do
contract_address = 0x0000DD19DD796959B72998536F67814DCCABE156FF647E0E63E43395203908963767
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 = [
"0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827",
"UCO"
]
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 = 0x0000A4CAB2362A97002EE0A6DD2013BEC3AF02C4D8C392712CFBC38F3E4809B9314C
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 "UCO" == "UCO" do
token2_balance = balances.uco
else
token2_id = [token_address: "UCO", token_id: 0]
token2_balance = Map.get(balances.tokens, token2_id, 0)
end
token1_id = [token_address: "0000288BF6F0E12457B125DC54D2DFA4EB010BE3073CF02E10FB79B696180F55B827", token_id: 0]
[
token1: Map.get(balances.tokens, token1_id, 0),
token2: token2_balance
]
end
{
"aeip": [
8,
18,
19
],
"recipients": [
{
"amount": 6038084,
"to": "0000FC4FF61099BFCBCA539CAC69CA5A977B84EAE081EA433949A3F6EF016A3FB036"
}
],
"supply": 6038084,
"token_reference": "0000A4CAB2362A97002EE0A6DD2013BEC3AF02C4D8C392712CFBC38F3E4809B9314C"
}
{
"lp_token_supply": 8.27384969,
"reserves": {
"token1": 0.06851379,
"token2": 2187.68465265
},
"stats": {
"token1_total_fee": 3.7359e-4,
"token1_total_protocol_fee": 3.7359e-4,
"token1_total_volume": 0.14944175,
"token2_total_fee": 11.13726872,
"token2_total_protocol_fee": 11.13726872,
"token2_total_volume": 4454.90749927
}
}
-
Secret shared with 1 key
Encoded secret
E47BF63ADB91A41D1CD4E5F29259264590BC1A7E0B1AF94F3AE2522205AF93A776A57FA182A771F33E6435FB76C4D0D54D756EE8D356989119749349
Authorized keys
- 00017877BCF4122095926A49489009649603AB129822A19EF9D573B8FD714911ED7F
Contract recipients (0)
Inputs (0)
Contract inputs (0)
Unspent outputs (2)
-
From 00009620...4C89At 2024-02-03 18:28:35 UTCAmount 2,187.68465265 UCO
-
From 00009620...4C89At 2024-02-03 18:28:35 UTCAmount 0.06851379 0000288B...B827
Previous public key
00012A90EDD45C935A902E60BD031C0EC06110F2A2400368D62E837DDA5B27912AE8
Previous signature
0AD57BF447565E54605E7CE06FB3805463B6919880CDF4808B07EAFA4B8E2C425B8E1BDDAC687DE2A05A12FC4074D692EDAAD9629AE0DE56368074B2C29DC501
Origin signature
3045022018708D9467E40DD3C537D9B514760CE939447B90514F09CC3CD5D592D0DC8D02022100B5AEE2D9AD23ED4A47D20FFF66D0BCE454F7AF5AE448DB9446E8D70BFDB1D193
Proof of work
010204E9BB401A767B0C92D5DB139379AAB18E1B449B455F49E3A44F3466B2D38E59F0516B79493A528A95E5A74CF46F79CF12DEB73293458F6A4403D2B3E57D697347
Proof of integrity
00535E10F2A4D7BBAF6E3124F20E1960F7289C36094C547BA7B22DA5F6829AA2F1
Coordinator signature
F8B5A58C2A5482440DF8FFFA4E7B8F79083B4DC5ABE2A51FC6ECD104CB8A721DF7B18EA37DE66A6504C7A408D16468472CB4BA2BA1FACE7BE0D48657B3712C0D
Validator #1 public key
000177BA744AC778DC2D51A1B7C622E7AC4BD1E1AA8DA2D0FCE71BAAB7DAD0E020E0
Validator #1 signature
95FAAE9A235A0E3220AD3593D552C0663F0C953CBD93266D1EA3E4BB041ACBC4470581BD6978C75CDDEE6F99F0D518BFDCF3C5E4F74627A5568BCEC59E9AF20C
Validator #2 public key
0001500FBE298B79FFBDD5CCA1798F30FD88A53D26EC39DE5DDE1F4137B032A4BC34
Validator #2 signature
D71F75DFAF0528C1BF9F0C08B7BDE516D482AFBEB8547315A71F459574954CBAF82F62EB512CABAE73F5C1424A1269EF200D7FC9E79FE0080201ED59BD79B905