589 lines
22 KiB
Erlang
589 lines
22 KiB
Erlang
-module(gd_v_call).
|
|
-vsn("0.8.1").
|
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
|
-copyright("QPQ AG <info@qpq.swiss>").
|
|
-license("GPL-3.0-or-later").
|
|
|
|
-behavior(wx_object).
|
|
%-behavior(gd_v).
|
|
-include_lib("wx/include/wx.hrl").
|
|
-export([to_front/1, tx_hash/1, tx_data/1, tx_info/1]).
|
|
-export([start_link/1]).
|
|
-export([init/1, terminate/2, code_change/3,
|
|
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
|
|
-include("$zx_include/zx_logger.hrl").
|
|
-include("gd.hrl").
|
|
-include("gdl.hrl").
|
|
|
|
|
|
% State
|
|
-record(s,
|
|
{wx = none :: none | wx:wx_object(),
|
|
frame = none :: none | wx:wx_object(),
|
|
lang = en :: en | jp,
|
|
j = none :: none | fun(),
|
|
prefs = #{} :: map(),
|
|
con_id = <<"">> :: binary(),
|
|
fundef = none :: none | fun_def(),
|
|
funret = none :: none | term(), % FIXME
|
|
build = none :: none | map(),
|
|
args = [] :: [#w{}],
|
|
kp = #w{} :: #w{},
|
|
params = [] :: [param()],
|
|
return = #w{} :: #w{}, % wxTextCtrl, single-line
|
|
copy = #w{} :: #w{},
|
|
status = none :: status(),
|
|
action = #w{} :: #w{},
|
|
tx_data = none :: none | map(),
|
|
tx_info = none :: none | map(),
|
|
hash = #w{} :: #w{}, % wxTextCtrl, single-line
|
|
info = #w{} :: #w{}}). % wxTextCtrl, multi-line
|
|
|
|
|
|
-type fun_name() :: string().
|
|
-type fun_ilk() :: call | dryr | init.
|
|
-type fun_def() :: {fun_name(), fun_ilk()}.
|
|
-type param() :: {Label :: string(), Check :: fun(), #w{}}.
|
|
-type status() :: none
|
|
| submitted
|
|
| rejected
|
|
| included.
|
|
|
|
|
|
%%% Interface
|
|
|
|
-spec to_front(Win) -> ok
|
|
when Win :: wx:wx_object().
|
|
|
|
to_front(Win) ->
|
|
wx_object:cast(Win, to_front).
|
|
|
|
|
|
-spec tx_hash(Win) -> Result
|
|
when Win :: pid() | wx:wx_object(),
|
|
Result :: none | string().
|
|
|
|
tx_hash(Win) ->
|
|
wx_object:call(Win, tx_hash).
|
|
|
|
|
|
-spec tx_data(Win) -> Result
|
|
when Win :: pid() | wx:wx_object(),
|
|
Result :: none | map().
|
|
|
|
tx_data(Win) ->
|
|
wx_object:call(Win, tx_data).
|
|
|
|
|
|
-spec tx_info(Win) -> Result
|
|
when Win :: pid() | wx:wx_object(),
|
|
Result :: none | map().
|
|
|
|
tx_info(Win) ->
|
|
wx_object:call(Win, tx_info).
|
|
|
|
|
|
|
|
%%% Startup Functions
|
|
|
|
start_link(Args) ->
|
|
wx_object:start_link(?MODULE, Args, []).
|
|
|
|
|
|
init({Prefs, FunDef = {FunName, FunIlk}, ConID, Build, Selected, Keys}) ->
|
|
Lang = maps:get(lang, Prefs, en),
|
|
Trans = gd_jt:read_translations(?MODULE),
|
|
J = gd_jt:j(Lang, Trans),
|
|
{aaci, ConName, FunSpecs, _} = maps:get(aaci, Build),
|
|
FunSpec = {FunArgs, FunReturn} = maps:get(FunName, FunSpecs),
|
|
{CallTypeLabel, ActionLabel} =
|
|
case FunIlk of
|
|
call -> {J("Contract Call"), J("Submit Call")};
|
|
dryr -> {J("Dry Run"), J("Submit Dry Run")};
|
|
init -> {J("Deploy"), J("Deploy")}
|
|
end,
|
|
Arity = integer_to_list(length(FunArgs)),
|
|
Title = [CallTypeLabel, ": ", ConName, ".", FunName, "/", Arity],
|
|
Wx = wx:new(),
|
|
Frame = wxFrame:new(Wx, ?wxID_ANY, Title),
|
|
MainSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
KeySz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Signature Key")}]),
|
|
KeyBox = wxStaticBoxSizer:getStaticBox(KeySz),
|
|
KeyPicker = wxChoice:new(KeyBox, ?wxID_ANY, [{choices, Keys}]),
|
|
KP = #w{name = key_picker, id = wxChoice:getId(KeyPicker), wx = KeyPicker},
|
|
ZeroBasedSelected = Selected - 1,
|
|
ok = wxChoice:setSelection(KeyPicker, ZeroBasedSelected),
|
|
_ = wxStaticBoxSizer:add(KeySz, KeyPicker, zxw:flags(wide)),
|
|
|
|
{ArgSz, Args, Return, Copy, HasArgs} = call_arg_sizer(Frame, J, FunIlk, FunSpec),
|
|
{ParamSz, Params} = call_param_sizer(Frame, J),
|
|
|
|
Action = #w{wx = ActionBn} = gd_lib:button(Frame, ActionLabel),
|
|
|
|
TX_Sz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Transaction Info")}]),
|
|
TX_Sz_Box = wxStaticBoxSizer:getStaticBox(TX_Sz),
|
|
Single = [{style, ?wxTE_READONLY}],
|
|
Multi = [{style, ?wxTE_MULTILINE bor ?wxTE_READONLY}],
|
|
TX_Hash = #w{wx = HashT} = gd_lib:mono_text(TX_Sz_Box, tx_hash, "", Single),
|
|
TX_Info = #w{wx = InfoT} = gd_lib:mono_text(TX_Sz_Box, tx_info, "", Multi),
|
|
|
|
_ = wxStaticBoxSizer:add(TX_Sz, HashT, zxw:flags({base, 5})),
|
|
_ = wxStaticBoxSizer:add(TX_Sz, InfoT, zxw:flags({wide, 5})),
|
|
|
|
ArgSzArgs =
|
|
case HasArgs of
|
|
true -> [{proportion, 2}, {flag, ?wxEXPAND bor ?wxALL}, {border, 5}];
|
|
false -> zxw:flags({base, 5})
|
|
end,
|
|
_ = wxSizer:add(MainSz, ArgSz, ArgSzArgs),
|
|
_ = wxSizer:add(MainSz, KeySz, zxw:flags({base, 5})),
|
|
_ = wxSizer:add(MainSz, ParamSz, zxw:flags({base, 5})),
|
|
_ = wxSizer:add(MainSz, ActionBn, zxw:flags({base, 5})),
|
|
_ = wxSizer:add(MainSz, TX_Sz, [{proportion, 1}, {flag, ?wxEXPAND bor ?wxALL}, {border, 5}]),
|
|
|
|
_ = wxFrame:setSizer(Frame, MainSz),
|
|
_ = wxFrame:setSize(Frame, {900, 900}),
|
|
_ = wxSizer:layout(MainSz),
|
|
ok = wxFrame:connect(Frame, close_window),
|
|
ok = wxFrame:connect(Frame, command_button_clicked),
|
|
true = wxFrame:show(Frame),
|
|
State =
|
|
#s{wx = Wx, frame = Frame, j = J, prefs = Prefs,
|
|
fundef = FunDef, funret = FunReturn, con_id = ConID, build = Build,
|
|
args = Args, kp = KP, params = Params,
|
|
return = Return, copy = Copy,
|
|
action = Action, status = none,
|
|
hash = TX_Hash, info = TX_Info},
|
|
{Frame, State}.
|
|
|
|
|
|
call_arg_sizer(Frame, J, FunIlk, {CallArgs, ReturnType}) ->
|
|
SpecSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Function Spec")}]),
|
|
SpecBox = wxStaticBoxSizer:getStaticBox(SpecSz),
|
|
{CallSz, CallControls, HasArgs} = call_sizer(SpecBox, J, CallArgs),
|
|
{ReturnSz, Return, Copy} = return_sizer(SpecBox, J, FunIlk, ReturnType),
|
|
_ = wxStaticBoxSizer:add(SpecSz, CallSz, zxw:flags({wide, 5})),
|
|
_ = wxStaticBoxSizer:add(SpecSz, ReturnSz, zxw:flags({base, 5})),
|
|
{SpecSz, CallControls, Return, Copy, HasArgs}.
|
|
|
|
call_sizer(Parent, J, []) ->
|
|
CallSz = wxStaticBoxSizer:new(?wxVERTICAL, Parent, [{label, J("Call Args")}]),
|
|
CallBox = wxStaticBoxSizer:getStaticBox(CallSz),
|
|
Args = wxStaticText:new(CallBox, ?wxID_ANY, ["[", J("No Args"), "]"]),
|
|
_ = wxStaticBoxSizer:add(CallSz, Args, zxw:flags({wide, 5})),
|
|
{CallSz, [], false};
|
|
call_sizer(Parent, J, CallArgs) ->
|
|
CallSz = wxStaticBoxSizer:new(?wxVERTICAL, Parent, [{label, J("Call Args")}]),
|
|
CallBox = wxStaticBoxSizer:getStaticBox(CallSz),
|
|
ScrollWin = wxScrolledWindow:new(CallBox),
|
|
ScrollSz = wxBoxSizer:new(?wxVERTICAL),
|
|
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz),
|
|
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
|
|
GridSz = wxFlexGridSizer:new(2, [{gap, {4, 4}}]),
|
|
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
|
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
|
_ = wxStaticBoxSizer:add(ScrollSz, GridSz, zxw:flags(wide)),
|
|
_ = wxStaticBoxSizer:add(CallSz, ScrollWin, zxw:flags(wide)),
|
|
AddArg =
|
|
fun({Name, Type}) ->
|
|
L = wxStaticText:new(ScrollWin, ?wxID_ANY, [Name, " : ", textify(Type)]),
|
|
C = #w{wx = T} = gd_lib:mono_text(ScrollWin, Name),
|
|
_ = wxFlexGridSizer:add(GridSz, L, zxw:flags({base, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, T, zxw:flags({wide, 5})),
|
|
C
|
|
end,
|
|
Controls = lists:map(AddArg, CallArgs),
|
|
{CallSz, Controls, true}.
|
|
|
|
return_sizer(Parent, J, FunIlk, ReturnType) ->
|
|
IlkLabel =
|
|
case FunIlk =:= init of
|
|
false -> textify(ReturnType);
|
|
true -> J("Contract Address")
|
|
end,
|
|
ReturnSz = wxStaticBoxSizer:new(?wxVERTICAL, Parent, [{label, J("Return Type")}]),
|
|
ReturnSzBox = wxStaticBoxSizer:getStaticBox(ReturnSz),
|
|
ReturnLabel = wxStaticText:new(ReturnSzBox, ?wxID_ANY, IlkLabel),
|
|
Single = [{style, ?wxTE_READONLY}],
|
|
Return = #w{wx = ReturnTx} = gd_lib:mono_text(ReturnSzBox, return, "", Single),
|
|
Copy = #w{wx = CopyB} = gd_lib:button(ReturnSzBox, J("Copy")),
|
|
_ = wxButton:disable(CopyB),
|
|
_ = wxStaticBoxSizer:add(ReturnSz, ReturnLabel, zxw:flags({wide, 5})),
|
|
_ = wxStaticBoxSizer:add(ReturnSz, ReturnTx, zxw:flags({wide, 5})),
|
|
_ = wxStaticBoxSizer:add(ReturnSz, CopyB, zxw:flags({wide, 5})),
|
|
{ReturnSz, Return, Copy}.
|
|
|
|
|
|
call_param_sizer(Frame, J) ->
|
|
{ok, Height} = hz:top_height(),
|
|
DefTTL = Height + 10000,
|
|
DefGasP = hz:min_gas_price(),
|
|
DefGas = 5000000,
|
|
DefAmount = 0,
|
|
ParamSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("TX Parameters")}]),
|
|
ParamBox = wxStaticBoxSizer:getStaticBox(ParamSz),
|
|
GridSz = wxFlexGridSizer:new(2, 4, 4),
|
|
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
|
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
|
Amount_L = wxStaticText:new(ParamBox, ?wxID_ANY, J("Amount")),
|
|
Amount_T = wxTextCtrl:new(ParamBox, ?wxID_ANY),
|
|
ok = wxTextCtrl:setValue(Amount_T, integer_to_list(DefAmount)),
|
|
Gas_L = wxStaticText:new(ParamBox, ?wxID_ANY, J("Gas")),
|
|
Gas_T = wxTextCtrl:new(ParamBox, ?wxID_ANY),
|
|
ok = wxTextCtrl:setValue(Gas_T, integer_to_list(DefGas)),
|
|
GasP_L = wxStaticText:new(ParamBox, ?wxID_ANY, J("Gas Price")),
|
|
GasP_T = wxTextCtrl:new(ParamBox, ?wxID_ANY),
|
|
ok = wxTextCtrl:setValue(GasP_T, integer_to_list(DefGasP)),
|
|
TTL_L = wxStaticText:new(ParamBox, ?wxID_ANY, "TTL"),
|
|
TTL_T = wxTextCtrl:new(ParamBox, ?wxID_ANY),
|
|
ok = wxTextCtrl:setValue(TTL_T, integer_to_list(DefTTL)),
|
|
_ = wxFlexGridSizer:add(GridSz, Amount_L, zxw:flags({base, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, Amount_T, zxw:flags({wide, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, Gas_L, zxw:flags({base, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, Gas_T, zxw:flags({wide, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, GasP_L, zxw:flags({base, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, GasP_T, zxw:flags({wide, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, TTL_L, zxw:flags({base, 5})),
|
|
_ = wxFlexGridSizer:add(GridSz, TTL_T, zxw:flags({wide, 5})),
|
|
_ = wxSizer:add(ParamSz, GridSz, zxw:flags(wide)),
|
|
Params =
|
|
[{J("TX Amount"), fun gte_0/1, Amount_T} ,
|
|
{J("Gas") , fun gt_0/1, Gas_T},
|
|
{J("Gas Price"), fun gt_0/1, GasP_T},
|
|
{ "TTL", fun gte_0/1, TTL_T}],
|
|
{ParamSz, Params}.
|
|
|
|
|
|
|
|
%%% Spine
|
|
|
|
handle_call(tx_hash, _, State) ->
|
|
TXHash = do_tx_hash(State),
|
|
{reply, TXHash, State};
|
|
handle_call(tx_data, _, State = #s{tx_data = TXData}) ->
|
|
{reply, TXData, State};
|
|
handle_call(tx_info, _, State = #s{tx_info = TXInfo}) ->
|
|
{reply, TXInfo, State};
|
|
handle_call(Unexpected, From, State) ->
|
|
ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
|
|
{noreply, State}.
|
|
|
|
|
|
handle_cast(Unexpected, State) ->
|
|
ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
|
|
{noreply, State}.
|
|
|
|
|
|
handle_info(retire, State) ->
|
|
retire(State);
|
|
handle_info(Unexpected, State) ->
|
|
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
|
|
{noreply, State}.
|
|
|
|
|
|
handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID},
|
|
State = #s{action = #w{id = ID}, status = none}) ->
|
|
NewState = prep_call(State),
|
|
{noreply, NewState};
|
|
handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID},
|
|
State = #s{action = #w{id = ID}}) ->
|
|
NewState = check_tx(State),
|
|
{noreply, NewState};
|
|
handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID},
|
|
State = #s{copy = #w{id = ID}}) ->
|
|
ok = copy(State),
|
|
{noreply, State};
|
|
handle_event(#wx{event = #wxClose{}}, State) ->
|
|
retire(State);
|
|
handle_event(Event, State) ->
|
|
ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]),
|
|
{noreply, State}.
|
|
|
|
|
|
code_change(_, State, _) ->
|
|
{ok, State}.
|
|
|
|
|
|
retire(State = #s{frame = Frame}) ->
|
|
ok = wxWindow:destroy(Frame),
|
|
{stop, normal, State}.
|
|
|
|
|
|
terminate(Reason, State) ->
|
|
ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]),
|
|
wx:destroy().
|
|
|
|
|
|
|
|
%%% Handlers
|
|
|
|
handle_troubling(State = #s{frame = Frame}, Info) ->
|
|
ok = wxFrame:raise(Frame),
|
|
ok = zxw:show_message(Frame, Info),
|
|
State.
|
|
|
|
|
|
do_tx_hash(#s{tx_data = #{"tx_hash" := TXHash}}) ->
|
|
TXHash;
|
|
do_tx_hash(#s{tx_data = none}) ->
|
|
none.
|
|
|
|
|
|
prep_call(State) ->
|
|
case gd_con:chain_id() of
|
|
{ok, ChainID} -> prep_call2(State, ChainID);
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
prep_call2(State, ChainID) ->
|
|
case params(State) of
|
|
{ok, Params} -> prep_call3(State, ChainID, Params);
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
prep_call3(State, ChainID, Params) ->
|
|
case args(State) of
|
|
{ok, Args} -> prep_call4(State, ChainID, Params, {sophia, Args});
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
prep_call4(State = #s{fundef = {"init", init}, build = Build}, ChainID, Params, Args) ->
|
|
{CallerID, Nonce, Gas, GP, Amount, TTL} = Params,
|
|
case hz:contract_create_built(CallerID, Nonce, Gas, GP, Amount, TTL, Build, Args) of
|
|
{ok, CreateTX} -> deploy(State, ChainID, CallerID, CreateTX);
|
|
Error -> handle_troubling(State, Error)
|
|
end;
|
|
prep_call4(State = #s{fundef = {Name, Ilk}, con_id = ConID, build = Build}, ChainID, Params, Args) ->
|
|
{CallerID, Nonce, Gas, GP, Amount, TTL} = Params,
|
|
AACI = maps:get(aaci, Build),
|
|
case hz:contract_call(CallerID, Nonce, Gas, GP, Amount, TTL, AACI, ConID, Name, Args) of
|
|
{ok, UnsignedTX} ->
|
|
case Ilk of
|
|
call -> do_call(State, ChainID, CallerID, UnsignedTX);
|
|
dryr -> do_dry_run(State, ConID, UnsignedTX)
|
|
end;
|
|
Error ->
|
|
handle_troubling(State, Error)
|
|
end.
|
|
|
|
params(State = #s{kp = #w{wx = KeyPicker}}) ->
|
|
ID = wxChoice:getString(KeyPicker, wxChoice:getSelection(KeyPicker)),
|
|
PK = unicode:characters_to_binary(ID),
|
|
case hz:next_nonce(PK) of
|
|
{ok, Nonce} -> params2(State, PK, Nonce);
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
params2(#s{params = Params}, PK, Nonce) ->
|
|
case lists:foldl(fun extract/2, {ok, []}, Params) of
|
|
{ok, [TTL, GP, Gas, Amount]} -> {ok, {PK, Nonce, Gas, GP, Amount, TTL}};
|
|
Error -> Error
|
|
end.
|
|
|
|
extract({Name, Check, Widget}, {Status, Out}) ->
|
|
case Check(wxTextCtrl:getValue(Widget)) of
|
|
{ok, Value} -> {Status, [Value | Out]};
|
|
Error -> {error, [{Name, Error}, Out]}
|
|
end.
|
|
|
|
|
|
% TODO: Put some basic checking in here, like for blank strings, at least.
|
|
% It should be possible to perform type/parse checks over this since we have
|
|
% access to the AACI. But not today.
|
|
args(#s{args = ArgFields}) ->
|
|
Values = [wxTextCtrl:getValue(W) || #w{wx = W} <- ArgFields],
|
|
{ok, Values}.
|
|
|
|
|
|
deploy(State, ChainID, CallerID, CreateTX) ->
|
|
case gd_con:sign_call(ChainID, CallerID, CreateTX) of
|
|
{ok, SignedTX} -> deploy2(State, SignedTX);
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
deploy2(State = #s{j = J, hash = #w{wx = HashT}, action = #w{wx = ActionB}}, SignedTX) ->
|
|
case hz:post_tx(SignedTX) of
|
|
{ok, Data = #{"tx_hash" := TXHash}} ->
|
|
_ = wxButton:disable(ActionB),
|
|
ok = wxButton:setLabel(ActionB, J("Check Transaction Status")),
|
|
ok = log(info, "Submitted transaction ~s", [TXHash]),
|
|
ok = wxTextCtrl:setValue(HashT, unicode:characters_to_list(TXHash)),
|
|
check_tx(State#s{tx_data = Data, status = submitted});
|
|
{ok, #{"reason" := Reason}} ->
|
|
handle_troubling(State, {error, Reason});
|
|
Error ->
|
|
handle_troubling(State, Error)
|
|
end.
|
|
|
|
|
|
do_call(State, ChainID, CallerID, UnsignedTX) ->
|
|
case gd_con:sign_call(ChainID, CallerID, UnsignedTX) of
|
|
{ok, SignedTX} -> do_call2(State, SignedTX);
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
do_call2(State = #s{action = #w{wx = ActionB}}, SignedTX) ->
|
|
_ = wxButton:disable(ActionB),
|
|
case hz:post_tx(SignedTX) of
|
|
{ok, #{"reason" := Reason}} -> handle_troubling(State#s{status = rejected}, Reason);
|
|
{ok, Data} -> check_tx(State#s{tx_data = Data, status = submitted});
|
|
Error -> handle_troubling(State, Error)
|
|
end.
|
|
|
|
|
|
do_dry_run(State = #s{action = #w{wx = ActionB}}, ConID, TX) ->
|
|
_ = wxButton:disable(ActionB),
|
|
case hz:dry_run(TX) of
|
|
{ok, Result} -> dry_run2(State#s{tx_info = Result});
|
|
Other -> handle_troubling(State, {error, ConID, Other})
|
|
end.
|
|
|
|
|
|
dry_run2(State = #s{funret = ReturnType,
|
|
return = #w{wx = ReturnT},
|
|
copy = #w{wx = CopyB},
|
|
tx_data = TXData,
|
|
tx_info = TXInfo,
|
|
hash = #w{wx = HashT},
|
|
info = #w{wx = InfoT}}) ->
|
|
ReturnV =
|
|
case TXInfo of
|
|
#{"results" :=
|
|
[#{"call_obj" :=
|
|
#{"return_type" := "revert",
|
|
"return_value" := ReturnCB},
|
|
"result" := "ok","type" := "contract_call"}]} ->
|
|
io_lib:format("Revert: ~ts", [hz:decode_bytearray(ReturnCB, sophia)]);
|
|
#{"results" :=
|
|
[#{"call_obj" :=
|
|
#{"return_type" := "ok",
|
|
"return_value" := ReturnCB},
|
|
"result" := "ok","type" := "contract_call"}]} ->
|
|
hz:decode_bytearray(ReturnCB, {sophia, ReturnType});
|
|
Other ->
|
|
io_lib:format("???: ~tp", [Other])
|
|
end,
|
|
_ = wxButton:enable(CopyB),
|
|
FormattedHash = io_lib:format("~tp", [TXData]),
|
|
FormattedInfo = io_lib:format("~tp", [TXInfo]),
|
|
ok = wxTextCtrl:setValue(ReturnT, ReturnV),
|
|
ok = wxTextCtrl:setValue(HashT, FormattedHash),
|
|
ok = wxTextCtrl:setValue(InfoT, FormattedInfo),
|
|
State.
|
|
|
|
|
|
check_tx(State = #s{j = J,
|
|
fundef = {_, init},
|
|
tx_data = #{"tx_hash" := TXHash},
|
|
tx_info = none,
|
|
status = submitted,
|
|
action = #w{wx = ActionB},
|
|
info = #w{wx = InfoT}}) ->
|
|
case hz:tx_info(TXHash) of
|
|
{ok, Info = #{"call_info" := #{"return_type" := "ok", "contract_id" := ConID}}} ->
|
|
ok = tell(info, "Contract deployed: ~p", [Info]),
|
|
_ = wxButton:disable(ActionB),
|
|
ok = gd_con:open_contract(ConID),
|
|
self() ! retire,
|
|
State;
|
|
{error, "Tx not mined"} ->
|
|
ok = wxTextCtrl:setValue(InfoT, J("[Transaction not yet mined.]")),
|
|
ok = wxButton:setLabel(ActionB, J("Check Deployment Status")),
|
|
_ = wxButton:enable(ActionB),
|
|
State;
|
|
Other ->
|
|
FormattedJunk = io_lib:format("~tp", [Other]),
|
|
ok = wxTextCtrl:setValue(InfoT, FormattedJunk),
|
|
ok = wxButton:setLabel(ActionB, J("Check Depoyment Status")),
|
|
_ = wxButton:enable(ActionB),
|
|
State
|
|
end;
|
|
check_tx(State = #s{j = J,
|
|
funret = ReturnType,
|
|
tx_data = #{"tx_hash" := TXHash},
|
|
tx_info = none,
|
|
status = submitted,
|
|
return = #w{wx = ReturnT},
|
|
copy = #w{wx = CopyB},
|
|
action = #w{wx = ActionB},
|
|
info = #w{wx = InfoT}}) ->
|
|
case hz:tx_info(TXHash) of
|
|
{ok, Info = #{"call_info" := #{"return_type" := "ok",
|
|
"return_value" := ReturnCB}}} ->
|
|
FormattedInfo = io_lib:format("~tp", [Info]),
|
|
_ = wxButton:enable(CopyB),
|
|
_ = wxButton:disable(ActionB),
|
|
ReturnV = hz:decode_bytearray(ReturnCB, {sophia, ReturnType}),
|
|
ok = wxTextCtrl:setValue(ReturnT, ReturnV),
|
|
ok = wxTextCtrl:setValue(InfoT, FormattedInfo),
|
|
State#s{status = included, tx_info = Info};
|
|
{ok, Reason = #{"call_info" := #{"return_type" := "revert", "return_value" := ReturnCB}}} ->
|
|
_ = wxButton:enable(CopyB),
|
|
_ = wxButton:disable(ActionB),
|
|
ReturnV = io_lib:format("Revert: ~ts", [hz:decode_bytearray(ReturnCB, sophia)]),
|
|
ok = wxTextCtrl:setValue(ReturnT, ReturnV),
|
|
FormattedInfo = io_lib:format("~tp", [Reason]),
|
|
ok = wxTextCtrl:setValue(InfoT, FormattedInfo),
|
|
State#s{status = rejected, tx_info = Reason};
|
|
{error, "Tx not mined"} ->
|
|
ok = wxTextCtrl:setValue(InfoT, J("[Transaction not yet mined.]")),
|
|
ok = wxButton:setLabel(ActionB, J("Check Transaction Status")),
|
|
_ = wxButton:enable(ActionB),
|
|
State;
|
|
Error ->
|
|
handle_troubling(State, Error)
|
|
end;
|
|
check_tx(State = #s{tx_data = TXData,
|
|
action = #w{wx = ActionB}}) ->
|
|
_ = wxButton:disable(ActionB),
|
|
tell(info, "TXData: ~p", [TXData]),
|
|
ok = tell("Nothing to check"),
|
|
State.
|
|
|
|
|
|
copy(#s{tx_info = none}) ->
|
|
ok;
|
|
copy(#s{return = #w{wx = ReturnT}}) ->
|
|
Output = wxTextCtrl:getValue(ReturnT),
|
|
gd_lib:copy_to_clipboard(Output).
|
|
|
|
|
|
|
|
textify({integer, _, _}) -> "int";
|
|
textify({boolean, _, _}) -> "bool";
|
|
textify({{bytes, [I]}, _, _}) -> io_lib:format("bytes(~w)", [I]);
|
|
textify({{bytes, any}, _, _}) -> "bytes()";
|
|
textify({T, _, _}) when is_atom(T) -> atom_to_list(T);
|
|
textify({T, _, _}) when is_list(T) -> T;
|
|
textify({T, _, _}) -> io_lib:format("~tp", [T]).
|
|
|
|
|
|
gt_0(S) ->
|
|
C = "Must be an integer greater than 0",
|
|
R =
|
|
try
|
|
{ok, list_to_integer(S)}
|
|
catch
|
|
error:badarg -> {error, {S, C}}
|
|
end,
|
|
case R of
|
|
{ok, N} when N > 0 -> {ok, N};
|
|
{ok, N} when N =< 0 -> {error, {S, C}};
|
|
Error -> Error
|
|
end.
|
|
|
|
gte_0(S) ->
|
|
C = "Must be a non-negative integer.",
|
|
R =
|
|
try
|
|
{ok, list_to_integer(S)}
|
|
catch
|
|
error:badarg -> {error, {S, C}}
|
|
end,
|
|
case R of
|
|
{ok, N} when N >= 0 -> {ok, N};
|
|
{ok, N} when N < 0 -> {error, {S, C}};
|
|
Error -> Error
|
|
end.
|