Packages
stellar_sdk
0.21.1
0.23.0
0.22.0
0.21.2
0.21.1
0.21.0
0.20.0
0.19.0
0.18.1
0.18.0
0.17.1
0.17.0
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Elixir SDK for the Stellar network.
Current section
Files
Jump to
Current section
Files
lib/tx_build/sc_val.ex
defmodule Stellar.TxBuild.SCVal do
@moduledoc """
`SCVal` struct definition.
"""
import Bitwise
@behaviour Stellar.TxBuild.XDR
alias StellarBase.XDR.SCContractInstance
alias Stellar.TxBuild.{SCAddress, SCError, SCMapEntry}
alias StellarBase.XDR.{
Bool,
Duration,
Hash,
Int128Parts,
Int256Parts,
Int32,
Int64,
SCBytes,
ContractExecutable,
ContractExecutableType,
SCMap,
SCNonceKey,
SCString,
SCSymbol,
SCVal,
SCValType,
SCVec,
OptionalSCMap,
OptionalSCVec,
TimePoint,
UInt128Parts,
UInt256Parts,
UInt32,
UInt64,
Void
}
@type type ::
:bool
| :void
| :error
| :u32
| :i32
| :u64
| :i64
| :time_point
| :duration
| :u128
| :i128
| :u256
| :i256
| :bytes
| :string
| :symbol
| :vec
| :map
| :address
| :ledger_key_contract_instance
| :ledger_key_nonce
| :contract_instance
@type validation :: {:ok, any()} | {:error, atom()}
@type value ::
nil
| integer()
| boolean()
| SCError.t()
| map()
| binary()
| list()
| atom()
| SCAddress.t()
@type map_entry :: StellarBase.XDR.SCMapEntry.t()
@type t :: %__MODULE__{
type: type(),
value: value()
}
defstruct [:type, :value]
@allowed_types ~w(bool void error u32 i32 u64 i64 time_point duration u128 i128 u256 i256 bytes string symbol vec map address ledger_key_contract_instance ledger_key_nonce contract_instance)a
@impl true
def new(args, opts \\ nil)
def new([{type, value}], _opts)
when type in @allowed_types do
with {:ok, _value} <- validate_sc_val({type, value}) do
%__MODULE__{
type: type,
value: value
}
end
end
def new(_args, _opts), do: {:error, :invalid_sc_val_type}
@impl true
def to_xdr(%__MODULE__{type: :bool, value: value}) do
type = SCValType.new(:SCV_BOOL)
value
|> Bool.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :void, value: _value}) do
type = SCValType.new(:SCV_VOID)
Void.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :error, value: value}) do
val_type = SCValType.new(:SCV_ERROR)
value
|> SCError.to_xdr()
|> SCVal.new(val_type)
end
def to_xdr(%__MODULE__{type: :u32, value: value}) do
type = SCValType.new(:SCV_U32)
value
|> UInt32.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :i32, value: value}) do
type = SCValType.new(:SCV_I32)
value
|> Int32.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :u64, value: value}) do
type = SCValType.new(:SCV_U64)
value
|> UInt64.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :i64, value: value}) do
type = SCValType.new(:SCV_I64)
value
|> Int64.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :time_point, value: value}) do
type = SCValType.new(:SCV_TIMEPOINT)
value
|> TimePoint.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :duration, value: value}) do
type = SCValType.new(:SCV_DURATION)
value
|> Duration.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :u128, value: %{lo: lo, hi: hi}}) do
type = SCValType.new(:SCV_U128)
hi = UInt64.new(hi)
lo = UInt64.new(lo)
hi
|> UInt128Parts.new(lo)
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :i128, value: %{lo: lo, hi: hi}}) do
type = SCValType.new(:SCV_I128)
hi = Int64.new(hi)
lo = UInt64.new(lo)
hi
|> Int128Parts.new(lo)
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{
type: :u256,
value: %{hi_hi: hi_hi, hi_lo: hi_lo, lo_hi: lo_hi, lo_lo: lo_lo}
}) do
type = SCValType.new(:SCV_U256)
hi_hi = UInt64.new(hi_hi)
hi_lo = UInt64.new(hi_lo)
lo_hi = UInt64.new(lo_hi)
lo_lo = UInt64.new(lo_lo)
hi_hi
|> UInt256Parts.new(hi_lo, lo_hi, lo_lo)
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{
type: :i256,
value: %{hi_hi: hi_hi, hi_lo: hi_lo, lo_hi: lo_hi, lo_lo: lo_lo}
}) do
type = SCValType.new(:SCV_I256)
hi_hi = Int64.new(hi_hi)
hi_lo = UInt64.new(hi_lo)
lo_hi = UInt64.new(lo_hi)
lo_lo = UInt64.new(lo_lo)
hi_hi
|> Int256Parts.new(hi_lo, lo_hi, lo_lo)
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :bytes, value: value}) do
type = SCValType.new(:SCV_BYTES)
value
|> SCBytes.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :string, value: value}) do
type = SCValType.new(:SCV_STRING)
value
|> SCString.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :symbol, value: value}) do
type = SCValType.new(:SCV_SYMBOL)
value
|> SCSymbol.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :vec, value: nil}) do
type = SCValType.new(:SCV_VEC)
nil
|> OptionalSCVec.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :vec, value: value}) do
type = SCValType.new(:SCV_VEC)
value
|> Enum.map(&to_xdr/1)
|> SCVec.new()
|> OptionalSCVec.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :map, value: nil}) do
type = SCValType.new(:SCV_MAP)
nil
|> OptionalSCMap.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :map, value: value}) do
type = SCValType.new(:SCV_MAP)
value
|> Enum.map(&SCMapEntry.to_xdr/1)
|> SCMap.new()
|> OptionalSCMap.new()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :ledger_key_contract_instance, value: nil}) do
type = SCValType.new(:SCV_LEDGER_KEY_CONTRACT_INSTANCE)
SCVal.new(Void.new(), type)
end
def to_xdr(%__MODULE__{type: :address, value: value}) do
type = SCValType.new(:SCV_ADDRESS)
value
|> SCAddress.to_xdr()
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :contract_instance, value: {:wasm_ref, hash}}) do
type = SCValType.new(:SCV_CONTRACT_INSTANCE)
contract_code = ContractExecutableType.new(:CONTRACT_EXECUTABLE_WASM)
hash
|> Hash.new()
|> ContractExecutable.new(contract_code)
|> SCContractInstance.new(OptionalSCMap.new())
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :contract_instance, value: :token}) do
type = SCValType.new(:SCV_CONTRACT_INSTANCE)
contract_code = ContractExecutableType.new(:CONTRACT_EXECUTABLE_STELLAR_ASSET)
Void.new()
|> ContractExecutable.new(contract_code)
|> SCContractInstance.new(OptionalSCMap.new())
|> SCVal.new(type)
end
def to_xdr(%__MODULE__{type: :ledger_key_nonce, value: value}) do
type = SCValType.new(:SCV_LEDGER_KEY_NONCE)
value
|> Int64.new()
|> SCNonceKey.new()
|> SCVal.new(type)
end
@spec to_native_from_xdr(xdr :: String.t()) :: any
def to_native_from_xdr(xdr) when is_binary(xdr) do
with {:ok, {scval, _rest}} <- validate_xdr_decoding(xdr) do
to_native(scval)
end
end
def to_native_from_xdr(_xdr), do: {:error, :invalid_xdr}
@spec to_native(SCVal.t()) :: any
def to_native(%SCVal{
value: %StellarBase.XDR.SCSymbol{value: value},
type: %StellarBase.XDR.SCValType{identifier: :SCV_SYMBOL}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_VEC},
value: %StellarBase.XDR.OptionalSCVec{
sc_vec: %StellarBase.XDR.SCVec{
items: items
}
}
}) do
Enum.map(items, &to_native/1)
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_VEC},
value: %StellarBase.XDR.OptionalSCVec{
sc_vec: nil
}
}) do
[]
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_VOID},
value: _value
}) do
nil
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_BOOL},
value: %StellarBase.XDR.Bool{value: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_U128},
value: %StellarBase.XDR.UInt128Parts{
hi: %StellarBase.XDR.UInt64{datum: hi_value},
lo: %StellarBase.XDR.UInt64{datum: lo_value}
}
}) do
(hi_value <<< 64) + lo_value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_U64},
value: %StellarBase.XDR.UInt64{datum: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_I64},
value: %StellarBase.XDR.Int64{datum: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_U32},
value: %StellarBase.XDR.UInt32{datum: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_I32},
value: %StellarBase.XDR.Int32{datum: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_I128},
value: %StellarBase.XDR.Int128Parts{
hi: %StellarBase.XDR.Int64{datum: hi_value},
lo: %StellarBase.XDR.UInt64{datum: lo_value}
}
}) do
(hi_value <<< 64) + lo_value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_U256},
value: %StellarBase.XDR.UInt256Parts{
hi_hi: %StellarBase.XDR.UInt64{datum: hi_hi_value},
hi_lo: %StellarBase.XDR.UInt64{datum: hi_lo_value},
lo_hi: %StellarBase.XDR.UInt64{datum: lo_hi_value},
lo_lo: %StellarBase.XDR.UInt64{datum: lo_lo_value}
}
}) do
(hi_hi_value <<< 192) + (hi_lo_value <<< 128) + (lo_hi_value <<< 64) + lo_lo_value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_I256},
value: %StellarBase.XDR.Int256Parts{
hi_hi: %StellarBase.XDR.Int64{datum: hi_hi_value},
hi_lo: %StellarBase.XDR.UInt64{datum: hi_lo_value},
lo_hi: %StellarBase.XDR.UInt64{datum: lo_hi_value},
lo_lo: %StellarBase.XDR.UInt64{datum: lo_lo_value}
}
}) do
(hi_hi_value <<< 192) + (hi_lo_value <<< 128) + (lo_hi_value <<< 64) + lo_lo_value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_BYTES},
value: %StellarBase.XDR.SCBytes{value: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_STRING},
value: %StellarBase.XDR.SCString{value: value}
}) do
value
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_MAP},
value: %StellarBase.XDR.OptionalSCMap{
sc_map: %StellarBase.XDR.SCMap{
items: items
}
}
}) do
Enum.reduce(items, %{}, fn entry, acc ->
{key, val} = map_entry_to_native(entry)
Map.put(acc, key, val)
end)
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_MAP},
value: %StellarBase.XDR.OptionalSCMap{
sc_map: nil
}
}) do
%{}
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_ADDRESS},
value: %StellarBase.XDR.SCAddress{
sc_address: %StellarBase.XDR.AccountID{
account_id: %StellarBase.XDR.PublicKey{
public_key: %StellarBase.XDR.UInt256{
datum: public_key
}
}
},
type: %StellarBase.XDR.SCAddressType{identifier: :SC_ADDRESS_TYPE_ACCOUNT}
}
}) do
StellarBase.StrKey.encode!(public_key, :ed25519_public_key)
end
def to_native(%SCVal{
type: %StellarBase.XDR.SCValType{identifier: :SCV_ADDRESS},
value: %StellarBase.XDR.SCAddress{
sc_address: %StellarBase.XDR.Hash{value: hash},
type: %StellarBase.XDR.SCAddressType{identifier: :SC_ADDRESS_TYPE_CONTRACT}
}
}) do
StellarBase.StrKey.encode!(hash, :contract)
end
def to_native(_sc_val), do: {:error, :invalid_or_not_supported_sc_val}
@spec validate_xdr_decoding(xdr :: String.t()) :: validation()
defp validate_xdr_decoding(xdr) when is_binary(xdr) do
case Base.decode64(xdr) do
{:ok, decoded_xdr} -> SCVal.decode_xdr(decoded_xdr)
_ -> {:error, :invalid_base64}
end
end
@spec map_entry_to_native(map_entry :: map_entry()) :: tuple()
defp map_entry_to_native(%StellarBase.XDR.SCMapEntry{key: key, val: val}),
do: {to_native(key), to_native(val)}
@spec validate_sc_val(tuple :: tuple()) :: validation()
defp validate_sc_val({type, value})
when type in ~w(u32 u64 time_point duration)a and is_integer(value) and value >= 0,
do: {:ok, value}
defp validate_sc_val({type, value})
when type in ~w(i32 i64)a and is_integer(value),
do: {:ok, value}
defp validate_sc_val({:bool, value}) when is_boolean(value), do: {:ok, value}
defp validate_sc_val({:void, _value}), do: {:ok, nil}
defp validate_sc_val({:error, %SCError{} = value}), do: {:ok, value}
defp validate_sc_val({type, %{lo: lo, hi: hi} = value})
when type in ~w(u128 i128)a and is_integer(lo) and is_integer(hi),
do: {:ok, value}
defp validate_sc_val({type, %{hi_hi: hi_hi, hi_lo: hi_lo, lo_hi: lo_hi, lo_lo: lo_lo} = value})
when type in ~w(u256 i256)a and is_integer(hi_hi) and is_integer(hi_lo) and
is_integer(lo_hi) and is_integer(lo_lo),
do: {:ok, value}
defp validate_sc_val({type, value})
when type in ~w(bytes string symbol)a and is_binary(value),
do: {:ok, value}
defp validate_sc_val({:vec, nil}), do: {:ok, nil}
defp validate_sc_val({:vec, value}) when is_list(value) do
if Enum.all?(value, &is_sc_val?/1),
do: {:ok, value},
else: {:error, :invalid_vec}
end
defp validate_sc_val({:map, nil}), do: {:ok, nil}
defp validate_sc_val({:map, value}) when is_list(value) do
if Enum.all?(value, &is_map_entry?/1),
do: {:ok, value},
else: {:error, :invalid_map}
end
defp validate_sc_val({:contract_instance, :token}), do: {:ok, :token}
defp validate_sc_val({:contract_instance, {:wasm_ref, hash} = value}) when is_binary(hash),
do: {:ok, value}
defp validate_sc_val({:address, %SCAddress{} = value}), do: {:ok, value}
defp validate_sc_val({:ledger_key_contract_instance, _value}), do: {:ok, nil}
defp validate_sc_val({:ledger_key_nonce, value}) when is_integer(value), do: {:ok, value}
defp validate_sc_val({type, _value}), do: {:error, :"invalid_#{type}"}
@spec is_map_entry?(value :: any()) :: boolean()
defp is_map_entry?(%SCMapEntry{}), do: true
defp is_map_entry?(_), do: false
@spec is_sc_val?(value :: any()) :: boolean()
defp is_sc_val?(%__MODULE__{}), do: true
defp is_sc_val?(_), do: false
end