defmodule Circlex.Emulator do
@moduledoc """
A basic server to mimic the functionality of Circle.
"""
require Logger
@type opt() :: {:port, integer()} | {:initial_state, term()}
@default_port 3333
def emulator_config(), do: Application.get_env(:circlex_api, :emulator)
def usdc_address(),
do: Signet.Util.decode_hex!(Keyword.fetch!(emulator_config(), :usdc_address))
def port(), do: Keyword.get(emulator_config(), :port, @default_port)
def merchant_id(), do: Keyword.fetch!(emulator_config(), :merchant_id)
@transfer_event "Transfer(address indexed from, address indexed to, uint amount)"
def action_delay(),
do: Keyword.fetch!(emulator_config(), :action_delay_ms)
def child_spec(opts) do
%{
id: __MODULE__,
start: {__MODULE__, :start, [opts]},
type: :worker,
restart: :permanent,
shutdown: 500
}
end
@spec start() :: {:ok, pid()} | {:error, String.t()}
@spec start([opt()]) :: {:ok, pid()} | {:error, String.t()}
def start(opts \\ []) do
emulator_port = Keyword.get(opts, :port, port())
initial_state = Keyword.get(opts, :initial_state, emulator_config()[:initial_state])
state_name = Keyword.get(opts, :state_name, Circlex.Emulator.State)
cowboy_ref = Module.concat(__MODULE__, "Port_" <> to_string(emulator_port))
next = Keyword.get(opts, :next, %{})
listeners = Keyword.get(opts, :listeners, [])
ethereum_node = Signet.Application.ethereum_node()
signer = Keyword.get(opts, :signer, emulator_config()[:signer] || Signet.Signer.Default)
children = [
{Plug.Cowboy,
scheme: :http,
plug: {Circlex.Emulator.Router, [state_pid: state_name, signer_proc: signer]},
options: [ref: cowboy_ref, port: emulator_port]},
{Circlex.Emulator.State,
name: state_name, initial_state: initial_state, next: next, signer_proc: signer},
{Signet.Filter,
[name: USDCDepositFilter, address: usdc_address(), events: [@transfer_event]]},
{Circlex.Emulator.DepositDetector, [[USDCDepositFilter], state_name]},
{Circlex.Emulator.Notifier, listeners: listeners}
]
Logger.info(
"Circlex Emulator starting on port #{emulator_port} connected to #{ethereum_node}..."
)
Supervisor.start_link(children, strategy: :one_for_one)
end
end