defmodule Soroban.Contract.InvokeContractFunction do
@moduledoc """
`InvokeContractFunction` implementation to invoke authorized and not authorized contract functions.
"""
alias Soroban.Contract.RPCCalls
alias Soroban.RPC
alias Soroban.RPC.{SendTransactionResponse, Server, SimulateTransactionResponse}
alias Stellar.TxBuild.{
Account,
HostFunction,
InvokeContractArgs,
InvokeHostFunction,
SCAddress,
SCVal,
SequenceNumber,
Signature
}
@type server :: Server.t()
@type network_passphrase :: String.t()
@type account :: Account.t()
@type function_args :: list(struct())
@type auth_secret_keys :: list(String.t())
@type invoke_host_function :: InvokeHostFunction.t()
@type envelope_xdr :: String.t()
@type function_name :: String.t()
@type contract_address :: String.t()
@type source_secret_key :: String.t()
@type source_public_key :: String.t()
@type simulate_response :: {:ok, SimulateTransactionResponse.t()}
@type send_response :: {:ok, SendTransactionResponse.t()}
@type signature :: Signature.t()
@type sequence_number :: SequenceNumber.t()
@type sc_val_list :: list(SCVal.t())
@type addl_resources :: keyword()
@spec invoke(
server :: server(),
network_passphrase :: network_passphrase(),
contract_address :: contract_address(),
source_secret_key :: source_secret_key(),
function_name :: function_name(),
function_args :: function_args(),
auth_secret_keys :: auth_secret_keys(),
addl_resources :: addl_resources()
) :: send_response()
def invoke(
%Server{} = server,
network_passphrase,
contract_address,
source_secret_key,
function_name,
function_args,
auth_secret_keys \\ [],
addl_resources \\ []
) do
with {public_key, _secret} = keypair <- Stellar.KeyPair.from_secret_seed(source_secret_key),
{:ok, seq_num} <- RPC.fetch_next_sequence_number(server, public_key),
{:ok, function_args} <- convert_to_sc_val(function_args),
%Signature{} = signature <- Signature.new(keypair),
%Account{} = source_account <- Account.new(public_key),
%SequenceNumber{} = sequence_number <- SequenceNumber.new(seq_num),
%InvokeHostFunction{} = invoke_host_function_op <-
create_host_function_op(contract_address, function_name, function_args, public_key) do
invoke_host_function_op
|> RPCCalls.simulate(
server,
network_passphrase,
source_account,
sequence_number,
addl_resources
)
|> RPCCalls.send_transaction(
server,
network_passphrase,
source_account,
sequence_number,
signature,
invoke_host_function_op,
auth_secret_keys
)
end
end
@spec retrieve_unsigned_xdr_to_invoke(
server :: server(),
network_passphrase :: network_passphrase(),
contract_address :: contract_address(),
source_public_key :: source_public_key(),
function_name :: function_name(),
function_args :: function_args(),
addl_resources :: addl_resources()
) :: envelope_xdr()
def retrieve_unsigned_xdr_to_invoke(
%Server{} = server,
network_passphrase,
contract_address,
source_public_key,
function_name,
function_args,
addl_resources \\ []
) do
with {:ok, seq_num} <- RPC.fetch_next_sequence_number(server, source_public_key),
{:ok, function_args} <- convert_to_sc_val(function_args),
%Account{} = source_account <- Account.new(source_public_key),
%SequenceNumber{} = sequence_number <- SequenceNumber.new(seq_num),
%InvokeHostFunction{} = invoke_host_function_op <-
create_host_function_op(
contract_address,
function_name,
function_args,
source_public_key
) do
invoke_host_function_op
|> RPCCalls.simulate(
server,
network_passphrase,
source_account,
sequence_number,
addl_resources
)
|> RPCCalls.retrieve_unsigned_xdr(
server,
network_passphrase,
source_account,
sequence_number,
invoke_host_function_op
)
end
end
@spec simulate_invoke(
server :: server(),
network_passphrase :: network_passphrase(),
contract_address :: contract_address(),
source_public_key :: source_public_key(),
function_name :: function_name(),
function_args :: function_args(),
addl_resources :: addl_resources()
) :: simulate_response()
def simulate_invoke(
%Server{} = server,
network_passphrase,
contract_address,
source_public_key,
function_name,
function_args,
addl_resources \\ []
) do
with {:ok, seq_num} <- RPC.fetch_next_sequence_number(server, source_public_key),
{:ok, function_args} <- convert_to_sc_val(function_args),
%Account{} = source_account <- Account.new(source_public_key),
%SequenceNumber{} = sequence_number <- SequenceNumber.new(seq_num),
%InvokeHostFunction{} = invoke_host_function_op <-
create_host_function_op(
contract_address,
function_name,
function_args,
source_public_key
) do
RPCCalls.simulate(
invoke_host_function_op,
server,
network_passphrase,
source_account,
sequence_number,
addl_resources
)
end
end
@spec create_host_function_op(
contract_address :: contract_address(),
function_name :: function_name(),
function_args :: function_args(),
source_public_key :: source_public_key()
) :: invoke_host_function()
defp create_host_function_op(contract_address, function_name, function_args, source_public_key) do
function_args =
InvokeContractArgs.new(
contract_address: SCAddress.new(contract_address),
function_name: function_name,
args: function_args
)
host_function = HostFunction.new(invoke_contract: function_args)
InvokeHostFunction.new(host_function: host_function, source_account: source_public_key)
end
@spec convert_to_sc_val(function_args :: function_args()) :: {:ok, sc_val_list()}
defp convert_to_sc_val(function_args) do
sc_vals = Enum.map(function_args, fn %{__struct__: struct} = arg -> struct.to_sc_val(arg) end)
{:ok, sc_vals}
end
end