Type
TRANSFER
Validation date
2024-01-31 19:30:29 UTC
Fee
0 UCO

Code (3.27 KB)

@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: 0x00007869D75250EBF76280FC3A2CD186C6C6556E46CD800ADE0656604F811F588969,
    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: "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1")
  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: "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1")
  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 == "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1" 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 = "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1"
    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 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

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 = [
    "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1",
    "UCO",
    0x00000C35228746892951F0CBA230FFA51F4A547D2F83C3622AAFD520DC4CE7351523,
    0x00007869D75250EBF76280FC3A2CD186C6C6556E46CD800ADE0656604F811F588969
  ]

  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 == "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1" 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 == "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1" 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 == "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1" 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: "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1",
      reserve: reserves.token1
    ],
    token2: [
      address: "UCO",
      reserve: reserves.token2
    ],
    lp_token: [
      address: 0x00007869D75250EBF76280FC3A2CD186C6C6556E46CD800ADE0656604F811F588969,
      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 = 0x00000C35228746892951F0CBA230FFA51F4A547D2F83C3622AAFD520DC4CE7351523

  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 == "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1" 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 = 0x00000C35228746892951F0CBA230FFA51F4A547D2F83C3622AAFD520DC4CE7351523

  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 = [
    "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1",
    "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 = 0x00007869D75250EBF76280FC3A2CD186C6C6556E46CD800ADE0656604F811F588969

  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: "00001A4AB7AD0CE2B494C965C66FF2962692A5FE5ECB71B345ABB53BAD88A83A01F1", token_id: 0]
  [
    token1: Map.get(balances.tokens, token1_id, 0),
    token2: token2_balance
  ]
end

Content (0 B)

State (254 B)

{
  "lp_token_supply": 12.98881472,
  "reserves": {
    "token1": 0.13101386,
    "token2": 904.03072159
  },
  "stats": {
    "token1_total_fee": 2.8499e-4,
    "token1_total_protocol_fee": 2.8499e-4,
    "token1_total_volume": 0.11399891,
    "token2_total_fee": 1.42975058,
    "token2_total_protocol_fee": 1.42975058,
    "token2_total_volume": 571.90023459
  }
}
                  
Movements (0)

Ownerships (1)

  • Secret shared with 1 key

    Encoded secret

    ADF255F2ACD81949E0ACAA21745132CF78CC26472BF46B63D801A3E807D6119EE35ABBE84070B169BFFD34A66031538DBAB9255A8E4047FA07EB52C1

    Authorized keys

    • 00017877BCF4122095926A49489009649603AB129822A19EF9D573B8FD714911ED7F

Contract recipients (0)

Inputs (0)

Contract inputs (0)

Proofs and signatures

Previous public key

0001EF9472A03E8622EE2CBB6C1F0319C4CF321FDDFC4EB6425829FFD590CBB0CB31

Previous signature

01322A283C3A7AB6E7246D9CA91C4C88A58BB714AEC41363DAADCE5002D26102D0920985E15835D0F10F012FC85910468E132C4FDF41385BC24D87C97DC27006

Origin signature

30440220282837FE6B8C49DBC7522B69D0EFE419308A8AD4FA53919EC75283738252348702201A174435A632493987D5A444302A5BCC3CC9126FDBBEE5780A360BFA2FBE8464

Proof of work

010104BB7216545F28C83467606EDA88968E19C436410AB446D1BC88FEBDE3506275F9B0B931CAAE420B96B2A158B7112F13795C878CCC346C76E04A96C59CCFC6FC39

Proof of integrity

002714F4BB0FC7572EA9F5F16926B7E136AAF0948114FDBD1756E43B840CC9828B

Coordinator signature

ADCB05ABF6DB029CE181D079F1EC746F7F062B1A2B6C1678A6AC470D928796E2761C4DB2F62479642F9F99EBD29EAA5DBBFBD3D3634B809FAC7F0954FC1BE901

Validator #1 public key

0001B0A94804BF8ECC9897075C6207FF63EF4D339F57A0349888E6B77CD47DB53EF3

Validator #1 signature

252BD90DFE9B5D0B7C5811219360FEB0FA19A1CD551245CE12D78E3B412BF459CDD17421DB74D1768E9E0633C9AB59AF47141C1725C09A14E98E1E1B00D08109

Validator #2 public key

00011ED0B570D680BE5ECD58D2D121689DA73C46DCB38A01C6E10D06286040ADE30A

Validator #2 signature

87D20526472AF9720BC686EF983860E06AF1F260A2AEA439DF2FCA9CE200F33C004C26B65450C21B70C53FA1D22E00297D1EF17DB399959E9CBAAE25B9F39C0C

Validator #3 public key

000103E30584AD8DE66F9E29419D5D0ABEE5A76722C9FD0D012BDDE3A6E2B149C48D

Validator #3 signature

8B549520C6EC2B4D195CBEFC35712A8C8FA272B0FC8F87131BE248764FAC535D8A67D09CA03E79673F06E0534C6B26BF9263DCEC4E8F312677DE976ECD8B8309

Validator #4 public key

000151C124A6211CD402FD1CFE560C5DB51ED0CBEF44B09B21A41206028E7E5942BF

Validator #4 signature

FC43FEC801161592CBDBD2452D816226610EBEA408AC21493BE75860D09D3D024ABC788F67462E2AA5DC250D6F36B50642243FD0A26BE5E52729222C2898F401

Validator #5 public key

000134772A943DC0F7038B1BABA39E18841C12DD6682B1B6D1D15E08D7CAF5339AB0

Validator #5 signature

04E362FED3F47D25AEC0BE0003EBBEB161F16DE5889ED1874FD9D7CE17206964E9FFC0721260DDD09D4634D786A619B9C257FBBE6FEC032800683CB2DFC2DE06