-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
475 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.