@@ -4,6 +4,7 @@ defmodule AeMdw.Contracts do
4
4
"""
5
5
6
6
alias :aeser_api_encoder , as: Enc
7
+ alias AeMdw.AexnContracts
7
8
alias AeMdw.Collection
8
9
alias AeMdw.Contract
9
10
alias AeMdw.Db.Contract , as: DbContract
@@ -38,6 +39,7 @@ defmodule AeMdw.Contracts do
38
39
@ typep reason ( ) :: binary ( )
39
40
@ typep pagination ( ) :: Collection . direction_limit ( )
40
41
@ typep range ( ) :: { :gen , Range . t ( ) } | { :txi , Range . t ( ) } | nil
42
+ @ typep logs_opt ( ) :: { :v3? , boolean ( ) }
41
43
42
44
@ contract_log_table Model.ContractLog
43
45
@ idx_contract_log_table Model.IdxContractLog
@@ -85,33 +87,93 @@ defmodule AeMdw.Contracts do
85
87
end
86
88
end
87
89
88
- @ spec fetch_logs ( State . t ( ) , pagination ( ) , range ( ) , query ( ) , cursor ( ) ) ::
90
+ @ spec fetch_logs ( State . t ( ) , pagination ( ) , range ( ) , query ( ) , cursor ( ) , [ logs_opt ( ) ] ) ::
89
91
{ :ok , { cursor ( ) , [ log ( ) ] , cursor ( ) } } | { :error , Error . t ( ) }
90
- def fetch_logs ( state , pagination , range , query , cursor ) do
92
+ def fetch_logs ( state , pagination , range , query , cursor , opts ) do
91
93
cursor = deserialize_logs_cursor ( cursor )
92
94
scope = deserialize_scope ( state , range )
93
95
94
- with { :ok , filters } <- Util . convert_params ( query , & convert_param ( state , & 1 ) ) do
96
+ with { :ok , filters } <- Util . convert_params ( query , & convert_logs_param ( state , & 1 ) ) do
97
+ encode_args = % {
98
+ aexn_args?: Map . get ( filters , :aexn_args , false ) ,
99
+ custom_args?: Map . get ( filters , :custom_args , false )
100
+ }
101
+
95
102
paginated_logs =
96
103
filters
97
104
|> build_logs_pagination ( state , scope , cursor )
98
- |> Collection . paginate ( pagination , & & 1 , & serialize_logs_cursor / 1 )
105
+ |> Collection . paginate (
106
+ pagination ,
107
+ & render_log ( state , & 1 , encode_args , opts ) ,
108
+ & serialize_logs_cursor / 1
109
+ )
99
110
100
111
{ :ok , paginated_logs }
101
112
end
102
113
end
103
114
104
- @ spec fetch_calls ( State . t ( ) , pagination ( ) , range ( ) , query ( ) , cursor ( ) ) ::
115
+ @ spec fetch_contract_logs ( State . t ( ) , binary ( ) , pagination ( ) , range ( ) , query ( ) , cursor ( ) ) ::
116
+ { :ok , { cursor ( ) , [ log ( ) ] , cursor ( ) } } | { :error , Error . t ( ) }
117
+ def fetch_contract_logs ( state , contract_id , pagination , range , query , cursor ) do
118
+ with { :ok , contract_pk } <- Validate . id ( contract_id , [ :contract_pubkey ] ) ,
119
+ { :ok , filters } <- Util . convert_params ( query , & convert_logs_param ( state , & 1 ) ) ,
120
+ { :ok , create_txi } <- create_txi ( state , contract_pk ) do
121
+ cursor = deserialize_logs_cursor ( cursor )
122
+ scope = deserialize_scope ( state , range )
123
+
124
+ encode_args = % {
125
+ aexn_args?: Map . get ( filters , :aexn_args , false ) ,
126
+ custom_args?: Map . get ( filters , :custom_args , false )
127
+ }
128
+
129
+ filters
130
+ |> Map . put ( :create_txi , create_txi )
131
+ |> build_logs_pagination ( state , scope , cursor )
132
+ |> Collection . paginate (
133
+ pagination ,
134
+ & render_log ( state , & 1 , encode_args , v3?: true ) ,
135
+ & serialize_logs_cursor / 1
136
+ )
137
+ |> then ( & { :ok , & 1 } )
138
+ end
139
+ end
140
+
141
+ @ spec fetch_contract_calls ( State . t ( ) , binary ( ) , pagination ( ) , range ( ) , query ( ) , cursor ( ) ) ::
142
+ { :ok , { cursor ( ) , [ call ( ) ] , cursor ( ) } } | { :error , Error . t ( ) }
143
+ def fetch_contract_calls ( state , contract_id , pagination , range , query , cursor ) do
144
+ with { :ok , contract_pk } <- Validate . id ( contract_id , [ :contract_pubkey ] ) ,
145
+ { :ok , filters } <- Util . convert_params ( query , & convert_param ( state , & 1 ) ) ,
146
+ { :ok , create_txi } <- create_txi ( state , contract_pk ) do
147
+ cursor = deserialize_calls_cursor ( cursor )
148
+ scope = deserialize_scope ( state , range )
149
+
150
+ filters
151
+ |> Map . put ( :create_txi , create_txi )
152
+ |> build_calls_pagination ( state , scope , cursor )
153
+ |> Collection . paginate (
154
+ pagination ,
155
+ & render_call ( state , & 1 , v3?: true ) ,
156
+ & serialize_calls_cursor / 1
157
+ )
158
+ |> then ( & { :ok , & 1 } )
159
+ end
160
+ end
161
+
162
+ @ spec fetch_calls ( State . t ( ) , pagination ( ) , range ( ) , query ( ) , cursor ( ) , Keyword . t ( ) ) ::
105
163
{ :ok , { cursor ( ) , [ call ( ) ] , cursor ( ) } } | { :error , Error . t ( ) }
106
- def fetch_calls ( state , pagination , range , query , cursor ) do
164
+ def fetch_calls ( state , pagination , range , query , cursor , opts ) do
107
165
cursor = deserialize_calls_cursor ( cursor )
108
166
scope = deserialize_scope ( state , range )
109
167
110
168
with { :ok , filters } <- Util . convert_params ( query , & convert_param ( state , & 1 ) ) do
111
169
paginated_calls =
112
170
filters
113
171
|> build_calls_pagination ( state , scope , cursor )
114
- |> Collection . paginate ( pagination , & render_call ( state , & 1 ) , & serialize_calls_cursor / 1 )
172
+ |> Collection . paginate (
173
+ pagination ,
174
+ & render_call ( state , & 1 , opts ) ,
175
+ & serialize_calls_cursor / 1
176
+ )
115
177
116
178
{ :ok , paginated_calls }
117
179
end
@@ -492,11 +554,26 @@ defmodule AeMdw.Contracts do
492
554
end )
493
555
end
494
556
557
+ defp convert_logs_param ( _state , { "aexn-args" , value } ) when value in ~w( true false) ,
558
+ do: { :ok , { :aexn_args , value == "true" } }
559
+
560
+ defp convert_logs_param ( _state , { "aexn-args" , _val } ) ,
561
+ do: { :error , ErrInput.Query . exception ( value: "aexn-args should be either true or false" ) }
562
+
563
+ defp convert_logs_param ( _state , { "custom-args" , value } ) when value in ~w( true false) ,
564
+ do: { :ok , { :custom_args , value == "true" } }
565
+
566
+ defp convert_logs_param ( _state , { "custom-args" , _val } ) ,
567
+ do: { :error , ErrInput.Query . exception ( value: "custom-args should be either true or false" ) }
568
+
569
+ defp convert_logs_param ( state , arg ) , do: convert_param ( state , arg )
570
+
495
571
defp convert_param ( state , { "contract_id" , contract_id } ) ,
496
572
do: convert_param ( state , { "contract" , contract_id } )
497
573
498
574
defp convert_param ( state , { "contract" , contract_id } ) do
499
- with { :ok , create_txi } <- create_txi ( state , contract_id ) do
575
+ with { :ok , contract_pk } <- Validate . id ( contract_id ) ,
576
+ { :ok , create_txi } <- create_txi ( state , contract_pk ) do
500
577
{ :ok , { :create_txi , create_txi } }
501
578
end
502
579
end
@@ -536,13 +613,9 @@ defmodule AeMdw.Contracts do
536
613
537
614
defp deserialize_scope ( _state , { :txi , first_txi .. last_txi } ) , do: { first_txi , last_txi }
538
615
539
- defp create_txi ( state , contract_id ) do
540
- with { :ok , pk } <- Validate . id ( contract_id ) ,
541
- { :ok , txi } <- Origin . tx_index ( state , { :contract , pk } ) do
542
- { :ok , txi }
543
- else
544
- { :error , reason } -> { :error , reason }
545
- :not_found -> { :error , ErrInput.Id . exception ( value: contract_id ) }
616
+ defp create_txi ( state , contract_pk ) do
617
+ with :not_found <- Origin . tx_index ( state , { :contract , contract_pk } ) do
618
+ { :error , ErrInput.Id . exception ( value: Enc . encode ( :contract_pubkey , contract_pk ) ) }
546
619
end
547
620
end
548
621
@@ -556,9 +629,16 @@ defmodule AeMdw.Contracts do
556
629
tx_type
557
630
end
558
631
559
- defp render_call ( state , { call_txi , local_idx , _create_txi , _pk , _fname , _pos } ) do
632
+ defp render_call ( state , { call_txi , local_idx , _create_txi , _pk , _fname , _pos } , opts ) do
560
633
call_key = { call_txi , local_idx }
561
- Format . to_map ( state , call_key , @ int_contract_call_table )
634
+
635
+ call = Format . to_map ( state , call_key , @ int_contract_call_table )
636
+
637
+ if Keyword . get ( opts , :v3? , true ) do
638
+ Map . drop ( call , ~w( call_txi contract_txi) a )
639
+ else
640
+ call
641
+ end
562
642
end
563
643
564
644
defp render_contract ( state , create_txi_idx ) do
@@ -593,6 +673,170 @@ defmodule AeMdw.Contracts do
593
673
}
594
674
end
595
675
676
+ defp render_log ( state , { create_txi , call_txi , log_idx } = index , encode_args , opts ) do
677
+ { contract_tx_hash , ct_pk } =
678
+ if create_txi == - 1 do
679
+ { nil , Origin . pubkey ( state , { :contract_call , call_txi } ) }
680
+ else
681
+ tx_hash = Enc . encode ( :tx_hash , Txs . txi_to_hash ( state , create_txi ) )
682
+
683
+ { tx_hash , Origin . pubkey ( state , { :contract , create_txi } ) }
684
+ end
685
+
686
+ v3? = Keyword . get ( opts , :v3? , true )
687
+
688
+ Model . tx ( id: call_tx_hash , block_index: { height , micro_index } ) =
689
+ State . fetch! ( state , Model.Tx , call_txi )
690
+
691
+ Model . block ( hash: block_hash ) = DBUtil . read_block! ( state , { height , micro_index } )
692
+
693
+ Model . contract_log ( args: args , data: data , ext_contract: ext_contract , hash: event_hash ) =
694
+ State . fetch! ( state , Model.ContractLog , index )
695
+
696
+ event_name = AexnContracts . event_name ( event_hash ) || get_custom_event_name ( event_hash )
697
+
698
+ state
699
+ |> render_remote_log_fields ( ext_contract )
700
+ |> Map . merge ( % {
701
+ contract_txi: create_txi ,
702
+ contract_tx_hash: contract_tx_hash ,
703
+ contract_id: encode_contract ( ct_pk ) ,
704
+ call_txi: call_txi ,
705
+ call_tx_hash: Enc . encode ( :tx_hash , call_tx_hash ) ,
706
+ block_time: DBUtil . block_time ( block_hash ) ,
707
+ args: format_args ( event_name , args , encode_args ) ,
708
+ data: maybe_encode_base64 ( data ) ,
709
+ event_hash: Base . hex_encode32 ( event_hash ) ,
710
+ event_name: event_name ,
711
+ height: height ,
712
+ micro_index: micro_index ,
713
+ block_hash: Enc . encode ( :micro_block_hash , block_hash ) ,
714
+ log_idx: log_idx
715
+ } )
716
+ |> maybe_remove_logs_txis ( v3? )
717
+ end
718
+
719
+ defp maybe_remove_logs_txis ( log , true ) do
720
+ Map . drop ( log , [ :contract_txi , :call_txi , :ext_caller_contract_txi ] )
721
+ end
722
+
723
+ defp maybe_remove_logs_txis ( log , false ) do
724
+ log
725
+ end
726
+
727
+ defp render_remote_log_fields ( _state , nil ) do
728
+ % {
729
+ ext_caller_contract_tx_hash: nil ,
730
+ ext_caller_contract_id: nil ,
731
+ parent_contract_id: nil
732
+ }
733
+ end
734
+
735
+ defp render_remote_log_fields ( _state , { :parent_contract_pk , parent_pk } ) do
736
+ % {
737
+ ext_caller_contract_txi: - 1 ,
738
+ ext_caller_contract_tx_hash: nil ,
739
+ ext_caller_contract_id: nil ,
740
+ parent_contract_id: encode_contract ( parent_pk )
741
+ }
742
+ end
743
+
744
+ defp render_remote_log_fields ( state , ext_ct_pk ) do
745
+ ext_ct_txi = Origin . tx_index! ( state , { :contract , ext_ct_pk } )
746
+ ext_ct_tx_hash = Enc . encode ( :tx_hash , Txs . txi_to_hash ( state , ext_ct_txi ) )
747
+
748
+ % {
749
+ ext_caller_contract_txi: ext_ct_txi ,
750
+ ext_caller_contract_tx_hash: ext_ct_tx_hash ,
751
+ ext_caller_contract_id: encode_contract ( ext_ct_pk ) ,
752
+ parent_contract_id: nil
753
+ }
754
+ end
755
+
756
+ defp maybe_encode_base64 ( data ) do
757
+ if String . valid? ( data ) , do: data , else: Base . encode64 ( data )
758
+ end
759
+
760
+ defp format_args ( "Allowance" , [ account1 , account2 , << amount :: 256 >> ] , % { aexn_args?: true } ) do
761
+ [ encode_account ( account1 ) , encode_account ( account2 ) , amount ]
762
+ end
763
+
764
+ defp format_args ( "Approval" , [ account1 , account2 , << token_id :: 256 >> , enable ] , % {
765
+ aexn_args?: true
766
+ } )
767
+ when enable in [ "true" , "false" ] do
768
+ [ encode_account ( account1 ) , encode_account ( account2 ) , token_id , enable ]
769
+ end
770
+
771
+ defp format_args ( "ApprovalForAll" , [ account1 , account2 , enable ] , % { aexn_args?: true } )
772
+ when enable in [ "true" , "false" ] do
773
+ [ encode_account ( account1 ) , encode_account ( account2 ) , enable ]
774
+ end
775
+
776
+ defp format_args ( event_name , [ account , << token_id :: 256 >> ] , % { aexn_args?: true } )
777
+ when event_name in [ "Burn" , "Mint" , "Swap" ] do
778
+ [ encode_account ( account ) , token_id ]
779
+ end
780
+
781
+ defp format_args ( "PairCreated" , [ pair_pk , token1 , token2 ] , % { aexn_args?: true } ) do
782
+ [ encode_contract ( pair_pk ) , encode_contract ( token1 ) , encode_contract ( token2 ) ]
783
+ end
784
+
785
+ defp format_args ( "Transfer" , [ from , to , << token_id :: 256 >> ] , % { aexn_args?: true } ) do
786
+ [ encode_account ( from ) , encode_account ( to ) , token_id ]
787
+ end
788
+
789
+ defp format_args (
790
+ "TemplateMint" ,
791
+ [ account , << template_id :: 256 >> , << token_id :: 256 >> ] ,
792
+ % { aexn_args?: true }
793
+ ) do
794
+ [ encode_account ( account ) , template_id , token_id ]
795
+ end
796
+
797
+ defp format_args (
798
+ "TemplateMint" ,
799
+ [ account , << template_id :: 256 >> , << token_id :: 256 >> , edition_serial ] ,
800
+ % { aexn_args?: true }
801
+ ) do
802
+ [ encode_account ( account ) , template_id , token_id , edition_serial ]
803
+ end
804
+
805
+ defp format_args ( event_name , args , % { custom_args?: true } ) do
806
+ case :persistent_term . get ( { __MODULE__ , event_name } , nil ) do
807
+ nil ->
808
+ Enum . map ( args , fn << topic :: 256 >> -> to_string ( topic ) end )
809
+
810
+ custom_args_config ->
811
+ encode_custom_args ( args , custom_args_config )
812
+ end
813
+ end
814
+
815
+ defp format_args ( _event_name , args , _format_opts ) do
816
+ Enum . map ( args , fn << topic :: 256 >> -> to_string ( topic ) end )
817
+ end
818
+
819
+ defp encode_custom_args ( args , custom_args_config ) do
820
+ Enum . with_index ( args , fn arg , i ->
821
+ case Map . get ( custom_args_config , i ) do
822
+ nil ->
823
+ << topic :: 256 >> = arg
824
+ to_string ( topic )
825
+
826
+ type ->
827
+ Enc . encode ( type , arg )
828
+ end
829
+ end )
830
+ end
831
+
832
+ defp get_custom_event_name ( event_hash ) do
833
+ :persistent_term . get ( { __MODULE__ , event_hash } , nil )
834
+ end
835
+
836
+ defp encode_contract ( pk ) , do: Enc . encode ( :contract_pubkey , pk )
837
+
838
+ defp encode_account ( pk ) , do: Enc . encode ( :account_pubkey , pk )
839
+
596
840
defp serialize_logs_cursor ( { create_txi , call_txi , log_idx } ) ,
597
841
do: Base . hex_encode32 ( "#{ create_txi } $#{ call_txi } $#{ log_idx } " , padding: false )
598
842
0 commit comments