Skip to content

Commit

Permalink
add event
Browse files Browse the repository at this point in the history
  • Loading branch information
zven21 committed Nov 7, 2022
1 parent 5155e90 commit 6a81290
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 2 deletions.
2 changes: 2 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

config :noncegeek, AptosEx, rpc_endpoint: "https://testnet.aptoslabs.com/v1"

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs"
3 changes: 2 additions & 1 deletion lib/noncegeek/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ defmodule Noncegeek.Application do
# Start the PubSub system
{Phoenix.PubSub, name: Noncegeek.PubSub},
# Start the Endpoint (http/https)
NoncegeekWeb.Endpoint
NoncegeekWeb.Endpoint,
{AptosEx, Application.fetch_env!(:noncegeek, AptosEx)}
# Start a worker by calling: Noncegeek.Worker.start_link(arg)
# {Noncegeek.Worker, arg}
]
Expand Down
150 changes: 150 additions & 0 deletions lib/noncegeek/aptos_ex/aptos_ex.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
defmodule AptosEx do
@moduledoc """
The AptosEx module is a wrapper around the Aptos API.
"""

use GenServer

alias AptosEx.RPC

defmodule State do
defstruct ~w(
rpc_endpoint
client
)a
end

def start_link(options) do
rpc_endpoint = Keyword.get(options, :rpc_endpoint)

unless rpc_endpoint do
raise ArgumentError,
":rpc_endpoint must be provided to `#{__MODULE__}`" <>
"to allow for json_rpc calls when running"
end

GenServer.start_link(__MODULE__, rpc_endpoint, name: __MODULE__)
end

@doc """
Get Aptos client
"""
def get_aptos_client() do
with {:ok, client} <- GenServer.call(__MODULE__, :client) do
client
else
_ ->
raise RuntimeError, "connect aptos rpc failed, please check your config"
end
end

@impl GenServer
def init(rpc_endpoint) do
state = %State{
rpc_endpoint: rpc_endpoint
}

{:ok, state, {:continue, :start}}
end

@impl GenServer
def handle_continue(:start, %State{rpc_endpoint: rpc_endpoint} = state) do
client = RPC.connect(rpc_endpoint)
{:noreply, %{state | client: client}}
end

@impl GenServer
def handle_call(:client, _, %{client: client} = state) do
{:reply, client, state}
end

@doc """
Get Aptos account
"""
def get_account(address) do
client = get_aptos_client()
RPC.get_account(client, address)
end

@doc """
Get account resources
"""
def get_account_resources(address, query \\ []) do
client = get_aptos_client()
RPC.get_account_resources(client, address, query)
end

@doc """
Get account resource
"""
def get_account_resource(address, resource_type, query \\ []) do
client = get_aptos_client()
RPC.get_account_resource(client, address, resource_type, query)
end

@doc """
Get transaction by hash.
"""
def get_transaction_by_hash(hash) do
client = get_aptos_client()
RPC.get_transaction_by_hash(client, hash)
end

@doc """
Check transaction result.
"""
def check_transaction_by_hash(hash, times \\ 3) do
client = get_aptos_client()
RPC.check_transaction_by_hash(client, hash, times)
end

@doc """
Get events by event_key
"""
def get_events(event_key) do
client = get_aptos_client()
RPC.get_events(client, event_key)
end

@doc """
Get events by address event_handle
"""
def get_events(address, event_handle, field, query \\ [limit: 10]) do
client = get_aptos_client()
RPC.get_events(client, address, event_handle, field, query)
end

@doc """
Get table item
"""
def get_table_item(table_handle, table_key) do
client = get_aptos_client()
RPC.get_table_item(client, table_handle, table_key)
end

@doc """
Get token detail
## Examples
iex> AptosEx.get_token_data("0xe19430a2498ff6800666d41cfd4b64d6d2a53574ef7457f700f96f4a61703d07", "DummyNames", "dummy1")
"""
def get_token_data(creator, collection_name, token_name) do
client = get_aptos_client()
RPC.get_token_data(client, creator, collection_name, token_name)
end

@doc """
Get collection data
## Examples
iex> AptosEx.get_collection_data("0xe19430a2498ff6800666d41cfd4b64d6d2a53574ef7457f700f96f4a61703d07", "DummyNames")
"""
def get_collection_data(account, collection_name) do
client = get_aptos_client()
RPC.get_collection_data(client, account, collection_name)
end
end
144 changes: 144 additions & 0 deletions lib/noncegeek/aptos_ex/rpc.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
defmodule AptosEx.RPC do
@moduledoc false

defstruct [:endpoint, :client, :chain_id]

# @endpoint "https://fullnode.devnet.aptoslabs.com/v1"
@endpoint "https://testnet.aptoslabs.com/v1"

def connect(endpoint \\ @endpoint) do
client =
Tesla.client([
# TODO: convert input/output type
{Tesla.Middleware.BaseUrl, endpoint},
# {Tesla.Middleware.Headers, [{"content-type", "application/json"}]},
{Tesla.Middleware.JSON, engine_opts: [keys: :atoms]}
])

rpc = %__MODULE__{client: client, endpoint: endpoint}

with {:ok, %{chain_id: chain_id}} <- ledger_information(rpc) do
{:ok, %{rpc | endpoint: endpoint, chain_id: chain_id}}
end
end

defp get(%{client: client}, path, options \\ []) do
with {:ok, %{status: 200, body: resp_body}} <- Tesla.get(client, path, options) do
{:ok, resp_body}
else
{:ok, %{body: resp_body}} -> {:error, resp_body}
{:error, error} -> {:error, error}
end
end

defp post(%{client: client}, path, body, options \\ []) do
with {:ok, %{body: resp_body}} <- Tesla.post(client, path, body, options) do
case resp_body do
%{code: _, message: message} -> {:error, message}
_ -> {:ok, resp_body}
end
else
{:error, error} -> {:error, error}
end
end

# Chain
def ledger_information(client) do
get(client, "/")
end

# Accounts
def get_account(client, address) do
get(client, "/accounts/#{address}")
end

def get_account_resources(client, address, query \\ []) do
get(client, "/accounts/#{address}/resources", query: query)
end

def get_account_resource(client, address, resource_type, query \\ []) do
get(client, "/accounts/#{address}/resource/#{resource_type}", query: query)
end

# Transactions
def get_transaction_by_hash(client, hash) do
get(client, "/transactions/by_hash/#{hash}")
end

def check_transaction_by_hash(client, hash, times \\ 3) do
case get_transaction_by_hash(client, hash) do
{:ok, result} ->
result.success

{:error, _} ->
if times > 0 do
Process.sleep(1000)
check_transaction_by_hash(client, hash, times - 1)
else
false
end
end
end

# Events
def get_events(client, event_key) do
case get(client, "/events/#{event_key}") do
{:ok, event_list} -> {:ok, event_list}
{:error, %{error_code: "resource_not_found"}} -> {:ok, []}
end
end

def get_events(client, address, event_handle, field, query \\ [limit: 10]) do
case get(client, "/accounts/#{address}/events/#{event_handle}/#{field}", query: query) do
{:ok, event_list} -> {:ok, event_list}
{:error, %{error_code: "resource_not_found"}} -> {:ok, []}
end
end

# Table
def get_table_item(client, table_handle, table_key) do
post(client, "/tables/#{table_handle}/item", table_key)
end

# Tokens
def get_token_data(client, creator, collection_name, token_name) do
with {:ok, result} <- get_account_resource(client, creator, "0x3::token::Collections") do
%{handle: handle} = result.data.token_data

token_data_id = %{
creator: creator,
collection: collection_name,
name: token_name
}

table_key = %{
key_type: "0x3::token::TokenDataId",
value_type: "0x3::token::TokenData",
key: token_data_id
}

get_table_item(client, handle, table_key)
end
end

def get_collection_data(client, account, collection_name) do
with {:ok, result} <- get_account_resource(client, account, "0x3::token::Collections") do
%{handle: handle} = result.data.collection_data

table_key = %{
key_type: "0x1::string::String",
value_type: "0x3::token::CollectionData",
key: collection_name
}

{:ok, result} = get_table_item(client, handle, table_key)

case result do
%{error_code: _} -> {:error, result}
_ -> {:ok, result}
end
else
_ -> {:error, "Token data not found"}
end
end
end
47 changes: 47 additions & 0 deletions lib/noncegeek/explorer/models/event.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule Noncegeek.Explorer.Model.Event do
@moduledoc false

use Ecto.Schema
import Ecto.Changeset

@timestamps_opts [type: :utc_datetime_usec]
schema "events" do
field :sequence_number, :integer
field :type, :string

field :account_address, :string

field :creation_number, :integer
field :version, :integer

# field :token_id, :map
# field :collection_id, :map
field :amount, :decimal
field :data, :map

belongs_to :token, Lotus.Explorer.Model.Token,
foreign_key: :token_id,
references: :token_id,
type: :map

timestamps()
end

@doc false
def changeset(event, attrs) do
required_fields = ~w(
account_address
sequence_number
token_id
type
data
version
)a

optional_fields = ~w(amount)a

event
|> cast(attrs, required_fields ++ optional_fields)
|> validate_required(required_fields)
end
end
Loading

0 comments on commit 6a81290

Please sign in to comment.