lib/aptos/rpc.ex

defmodule Web3MoveEx.Aptos.RPC do
    @moduledoc """
      Copy From: https://github.com/myastrallabs/stormstout
    """

    defstruct [:endpoint, :client, :chain_id]

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

    @doc """
      {:ok, client} = Aptos.RPC.connect()
      {:ok, result} = Aptos.RPC.get_token_data(
          client,
          "0xdc4e806913a006d86da8327a079d794435e2e3117fd418062ddf43943d663490",
          "DummyDog",
          "DummyDog 1"
        )
    """
    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

        @doc """
      {:ok, client} = AptosRPC.connect()
      {:ok, res} =  Aptos.RPC.get_collection_data(
        client,
        "0xdc4e806913a006d86da8327a079d794435e2e3117fd418062ddf43943d663490",
        "DummyDog"
      )
    """
    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



    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

    @doc """
    > https://fullnode.devnet.aptoslabs.com/v1/spec#/operations/get_table_item

    Example by curl:
    ```curl --request POST \
      --url https://fullnode.testnet.aptoslabs.com/v1/tables/0x55faf86aea81d23c0f8ec9bb5fa6ec8fed920a3482ea75d5bf27474a00d42198/item \
      --header 'Content-Type: application/json' \
      --data '{
      "key_type": "0x1::string::String",
      "value_type": "0xb923303d20c38a120669ad0ed751a105f254b049e75a350111d566009df9ba11::addr_info::AddrInfo",
      "key": "0x73c7448760517E3E6e416b2c130E3c6dB2026A1d"
    }'

    {"addr":"0x73c7448760517E3E6e416b2c130E3c6dB2026A1d","addr_type":"0","chains":["Ethereum"],"created_at":"1668826549","description":"Cool Addr","expired_at":"1700362549","id":"1","msg":"33344091.1.nonce_geek","pubkey":"","signature":"0x","updated_at":"0"}%
    ```

    Example to call by func:
    > Web3MoveEx.Aptos.RPC.get_table_item(client, "0x55faf86aea81d23c0f8ec9bb5fa6ec8fed920a3482ea75d5bf27474a00d42198", "0x1::string::String", "0xb923303d20c38a120669ad0ed751a105f254b049e75a350111d566009df9ba11::addr_info::AddrInfo", "0x73c7448760517E3E6e416b2c130E3c6dB2026A1d")
    """

    def get_table_item(client, table_handle, key_type, value_type, key) do
      payload = %{key_type: key_type, value_type: value_type, key: key}
      get_table_item(client, table_handle, payload)
    end
    def get_table_item(client, table_handle, table_key) do
      post(client, "/tables/#{table_handle}/item", table_key)
    end

  end