lib/circlex/emulator/deposit_detector.ex

defmodule Circlex.Emulator.DepositDetector do
  @moduledoc """
  Checks for Deposits
  """
  use GenServer

  alias Circlex.Emulator.State.{TransferState, WalletState}
  alias Circlex.Emulator.Actor.TransferActor
  alias Circlex.Struct.Amount

  require Logger

  @chain "ETH"
  @currency "USD"
  @usdc_decimals 6

  def start_link([filters, state_pid]) do
    GenServer.start_link(__MODULE__, {filters, state_pid})
  end

  @impl true
  def init({filters, state_pid}) do
    Process.put(:state_pid, state_pid)

    for filter <- filters do
      Signet.Filter.listen(filter)
    end

    {:ok, %{logs: [], events: %{}}}
  end

  def logs(pid) do
    GenServer.call(pid, :logs)
  end

  def events(pid) do
    GenServer.call(pid, :events)
  end

  @impl true
  def handle_info({:event, event, log}, state = %{events: events}) do
    trx_id = Signet.Util.encode_hex(log.transaction_hash)

    case event do
      {"Transfer", %{"from" => from, "to" => to, "amount" => amount}} ->
        # Okay, now let's see if it matches any known wallet.
        case WalletState.get_wallet_by_address(@chain, @currency, Signet.Util.encode_hex(to)) do
          {:ok, wallet} ->
            {:ok, transfer} =
              TransferState.new_transfer(
                %{
                  type: "blockchain",
                  chain: "ETH",
                  address: Signet.Util.encode_hex(from)
                },
                %{
                  type: "wallet",
                  id: wallet.wallet_id,
                  address: Signet.Util.encode_hex(to)
                },
                Amount.from_wei(amount, @usdc_decimals),
                trx_id
              )

            TransferState.add_transfer(transfer)

            TransferActor.start_link(transfer.id)

            Logger.info(
              "Detected new USDC transfer to wallet via trx: #{trx_id}: #{inspect(transfer)}"
            )

          :not_found ->
            Logger.info("Ignoring irrevelevant USDC transfer via trx: #{trx_id}...")
        end

      _ ->
        nil
    end

    {:noreply, %{state | events: Map.put(events, trx_id, event)}}
  end

  def handle_info({:log, log}, state = %{logs: logs}) do
    {:noreply, %{state | logs: [log.transaction_hash | logs]}}
  end

  @impl true
  def handle_call(:logs, _from, state = %{logs: logs}) do
    {:reply, logs, state}
  end

  def handle_call(:events, _from, state = %{events: events}) do
    {:reply, events, state}
  end
end