lib/transaction.ex

defmodule NearApi.Transaction do
  @moduledoc """
  Create NEAR [transactions](https://docs.near.org/docs/tutorials/create-transactions) here
  """
  require Logger

  @type t :: %__MODULE__{
          signer_id: String.t(),
          receiver_id: String.t(),
          nonce: integer,
          public_key: NearApi.PublicKey.t(),
          block_hash: binary,
          actions: [NearApi.Action.t()]
        }

  use Borsh,
    schema: [
      signer_id: :string,
      public_key: :borsh,
      nonce: :u64,
      receiver_id: :string,
      block_hash: [32],
      actions: [:borsh]
    ]

  defstruct [
    :signer_id,
    :public_key,
    :nonce,
    :receiver_id,
    :block_hash,
    :actions
  ]

  @doc """
  Creates a NearApi.Transaction struct, not serialized
  Parameters:
    `from_account` - an account strut NearApi.Account where `account_id` and `key_pair`
    `receiver_id` - NEAR account ID who we are sending tokens, e.g. `helloworld.near`
    `actions` - a list of transaction actions, e.g. NearApi.Actions.FunctionCall or NearApi.Actions.Transfer
  """
  @spec create_transaction(from_account :: NearApi.Account.t(), receiver_id :: String.t(), actions :: list) ::
          {:ok, NearApi.Transaction.t()} | {:error, :error_retrieving_access_key}
  def create_transaction(from_account, receiver_id, actions) do
    public_key = from_account.key_pair.public_key
    public_key_encoded = B58.encode58(from_account.key_pair.public_key.data)
    account_id = from_account.account_id

    with {:ok, key} <- NearApi.RPC.AccessKeys.view_access_key(account_id, nil, public_key_encoded) do
      block_hash_raw = key["result"]["block_hash"]
      nonce = key["result"]["nonce"] + 1

      block_hash = B58.decode58!(block_hash_raw)

      {:ok,
       %NearApi.Transaction{
         signer_id: account_id,
         receiver_id: receiver_id,
         nonce: nonce,
         public_key: public_key,
         block_hash: block_hash,
         actions: actions
       }}
    else
      error ->
        Logger.error("#{__MODULE__}: Cannot retrieve access key: #{inspect(error)}")
        {:error, :error_retrieving_access_key}
    end
  end

  def sign_and_serialise(tx, key_pair) do
    serialised_tx = borsh_encode(tx)
    serialized_tx_hash = :crypto.hash(:sha256, serialised_tx)
    signature = NearApi.KeyPair.signature(serialized_tx_hash, key_pair)

    st = %NearApi.SignedTransaction{transaction: tx, signature: %NearApi.Signature{key_type: 0, data: signature}}
    NearApi.SignedTransaction.borsh_encode(st)
  end

  def payload(tx, key_pair) do
    tx
    |> sign_and_serialise(key_pair)
    |> Base.encode64(padding: false)
  end
end