Type
TOKEN
Validation date
2024-04-26 22:21:58 UTC
Fee
0.4666477 UCO

Code (3.32 KB)

@version 1

condition triggered_by: transaction, on: add_liquidity(token1_min_amount, token2_min_amount), as: [
  token_transfers: (
    user_amounts = get_user_transfers_amount(transaction)

    valid_transfers? = user_amounts.token1 > 0 && user_amounts.token2 > 0
    valid_min? = user_amounts.token1 >= token1_min_amount && user_amounts.token2 >= token2_min_amount

    valid_transfers? && valid_min?
  )
]

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 = user_amounts.token1 + pool_balances.token1 - reserves.token1 - token1_to_refund
  token2_amount = user_amounts.token2 + pool_balances.token2 - reserves.token2 - token2_to_refund

  lp_token_to_mint = get_lp_token_to_mint(token1_amount, token2_amount)

  # Handle invalid values and refund user 
  valid_amounts? = final_amounts.token1 > 0 && final_amounts.token2 > 0
  valid_liquidity? = lp_token_to_mint > 0

  if valid_amounts? && valid_liquidity? do
    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: 0x0000D1B4A0597A033F7DD0C8CA274745F850A990725B7B73B5E8CEBC7C4F9EA82954,
      recipients: [
        [to: transaction.address, amount: lp_token_to_mint_bigint]
      ]
    ]

    new_token1_reserve = user_amounts.token1 + pool_balances.token1 - token1_to_refund
    new_token2_reserve = user_amounts.token2 + 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: "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF")
    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))
  else
    # Liquidity provision is invalid, refund user of it's tokens
    Contract.set_type("transfer")

    if "UCO" == "UCO" do
      Contract.add_uco_transfer(to: transaction.address, amount: user_amounts.token2)
    else
      Contract.add_token_transfer(to: transaction.address, amount: user_amounts.token2, token_address: "UCO")
    end

    Contract.add_token_transfer(to: transaction.address, amount: user_amounts.token1, token_address: "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF")
  end
end

condition triggered_by: transaction, on: remove_liquidity(), as: [
  token_transfers: (
    user_amount = get_user_lp_amount(transaction.token_transfers)

    user_amount > 0
  )
]

actions triggered_by: transaction, on: remove_liquidity() do
  return? = true

  user_amount = get_user_lp_amount(transaction.token_transfers)
  lp_token_supply = State.get("lp_token_supply", 0)

  if 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

    if token1_to_remove > 0 && token2_to_remove > 0 do
      return? = false

      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: "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF")
      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
  end

  if return? do
    # Refund is invalid, return LP tokens to user
    Contract.set_type("transfer")
    Contract.add_token_transfer(to: transaction.address, amount: user_amount, token_address: 0x0000D1B4A0597A033F7DD0C8CA274745F850A990725B7B73B5E8CEBC7C4F9EA82954)
  end
end

condition triggered_by: transaction, on: swap(_min_to_receive), as: [
  token_transfers: (
    transfer = get_user_transfer(transaction)

    transfer != nil
  )
]

actions triggered_by: transaction, on: swap(min_to_receive) do
  transfer = get_user_transfer(transaction)

  swap = get_swap_infos(transfer.token_address, transfer.amount)

  if swap.output_amount > 0 && swap.output_amount >= min_to_receive do

    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 == "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF" do
      pool_balances = [
        token1: pool_balances.token1 + transfer.amount - swap.protocol_fee,
        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 = [
        token1: pool_balances.token1 - swap.output_amount,
        token2: pool_balances.token2 + transfer.amount - swap.protocol_fee
      ]
      token_to_send = "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF"
      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: 0x0000CC1FADBD31B043947C016E09CCD59BC3C81E55AB8A4932A046236D5E0FEE9E45, amount: swap.protocol_fee)
      else
        Contract.add_token_transfer(to: 0x0000CC1FADBD31B043947C016E09CCD59BC3C81E55AB8A4932A046236D5E0FEE9E45, amount: swap.protocol_fee, token_address: transfer.token_address)
      end
    end
  else
    # Swap is invalid, return tokens to user
    Contract.set_type("transfer")

    if transfer.token_address == "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF" do
      Contract.add_token_transfer(to: transaction.address, amount: transfer.amount, token_address: "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF")
    else
      if transfer.token_address == "UCO" do
        Contract.add_uco_transfer(to: transaction.address, amount: transfer.amount)
      else
        Contract.add_token_transfer(to: transaction.address, amount: transfer.amount, token_address: "UCO")
      end
    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) == 0x000077CEC9D9DBC0183CAF843CBB4828A932BB1457E382AC83B31AD6F9755DD50FFC
  )
]

actions triggered_by: transaction, on: update_code() do
  params = [
    "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF",
    "UCO",
    0x000090C5AFCC97C2357E964E3DDF5BE9948477F7C1DE2C633CDFC95B202970AEA036,
    0x0000D1B4A0597A033F7DD0C8CA274745F850A990725B7B73B5E8CEBC7C4F9EA82954
  ]

  new_code = Contract.call_function(0x00004CE47B2828E923EB679FEF311DD458AA0571C67DB5CB46B4E0793CAC525AC791, "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) == 0x0000BE4D33FC48CD4791A58EF7296F6916F5A2032A4E66111CF10521D08FC660A4D3
  )
]

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 == "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF" 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 == "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF" 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) / 100
    amount_with_fee = amount - fee - protocol_fee

    market_price = 0

    if token_address == "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF" 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: "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF",
      reserve: reserves.token1
    ],
    token2: [
      address: "UCO",
      reserve: reserves.token2
    ],
    lp_token: [
      address: 0x0000D1B4A0597A033F7DD0C8CA274745F850A990725B7B73B5E8CEBC7C4F9EA82954,
      supply: State.get("lp_token_supply", 0)
    ],
    fee: 0.25,
    protocol_fee: State.get("protocol_fee", 0),
    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 = 0x000090C5AFCC97C2357E964E3DDF5BE9948477F7C1DE2C633CDFC95B202970AEA036

  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 == "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF" 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 = 0x000090C5AFCC97C2357E964E3DDF5BE9948477F7C1DE2C633CDFC95B202970AEA036

  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 = [
    "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF",
    "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 = 0x0000D1B4A0597A033F7DD0C8CA274745F850A990725B7B73B5E8CEBC7C4F9EA82954

  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
  token2_balance = 0
  if "UCO" == "UCO" do
    token2_balance = contract.balance.uco
  else
    token2_id = [token_address: "UCO", token_id: 0]
    token2_balance = Map.get(contract.balance.tokens, token2_id, 0)
  end

  token1_id = [token_address: "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF", token_id: 0]
  [
    token1: Map.get(contract.balance.tokens, token1_id, 0),
    token2: token2_balance
  ]
end

Content (344 B)

{
  "aeip": [
    2,
    8,
    18,
    19
  ],
  "allow_mint": true,
  "name": "aeSwap LP Token",
  "properties": {
    "token1_address": "0000457EACA7FBAA96DB4A8D506A0B69684F546166FBF3C55391B1461907EFA58EAF",
    "token2_address": "UCO"
  },
  "recipients": [
    {
      "amount": 10,
      "to": "00000000000000000000000000000000000000000000000000000000000000000000"
    }
  ],
  "supply": 10,
  "symbol": "aeSwapLP",
  "type": "fungible"
}

State (0 B)

Movements (0)

Ownerships (1)

  • Secret shared with 1 key

    Encoded secret

    79D14A69573AD2BDC195F38D006D29BFC1653EEB5DC2E0F5EA2387430C9806B796789B9944ECAD3A5802FBADAF81D794AD83777FF9D314F20DA31526

    Authorized keys

    • 000122A6CD9ED07E46835D6E88E5BD0BEE84C3F5E5DBF8E916AD9B2EC912C44AFEC2

Contract recipients (0)

Inputs (0)

Contract inputs (0)

Unspent outputs (1)

Proofs and signatures

Previous public key

00016DE45F92408474220205AC3A6495B4F8B4005A3367612AE6F62C69877AA07950

Previous signature

158A41071979056A3AA712981E98BA6D225D01E4B3B692F24E1148C11BBCF0573DD4127810AAC0468D5ECAD8C1205976DEA8A242BBCC9B45B3187E6DC4122E07

Origin signature

3045022100E5EB60EBBAF97A77ABD1BAF0331BC11216FA9299761B3554BE2D87BAFCE47482022028E4AD720413047019A77D92F1C32AE23C59922E95E7EC880B42D9732F03D7EF

Proof of work

010104AB41291F847A601055AEDD1AF24FF76FA970D6441E2DCA3818A8319B004C96B27B8FEB1DA31A044BA0A4800B4353359735719EBB3A05F98393A9CC599C3FAFD6

Proof of integrity

00593FF7EF414666DC48764115105FCD9D43F2B90ED2B4A79177DD150BA5ABFB6C

Coordinator signature

EDA37AA6E9E352C5683402BA74E88FB2C68454258193F7D474371C1CDDA0AE5FDB9E9AF55CA634318DD0DC1AC2F6C7F0E84BA6FF35BD6D36132395C7D5CC9901

Validator #1 public key

0001AD128AAE351A4BBC1153FDF8E5BBC27A9B30C336BC60004465963E1964A6145B

Validator #1 signature

655BB887AC5BA11286823B062188FF0580587AF414327C0738F0ECAEA9C83B73FBB913A23F111FC98C980DAC9E02E43FD43610EB86AC75F2F3D8DAD03FDD960D

Validator #2 public key

00013979F182FBF100A7D850091072443374862E8A007B24B5E14A1405B1F3B1F406

Validator #2 signature

729FE3193132603B06876A23BE25799E2E327DCCAE13D8C749BA0E04AE6B53E0E7D685F16AA8A23E5C8E34C0DCA33777E66E1C489D22FDF2CB20DD320E2E4A0B