66#include "os.h"
77#include "cx.h"
88#include "buffer.h"
9+ #include "mem.h" // for app_mem_alloc
910
1011#include "sign_tx.h"
1112#include "sw.h"
1213#include "globals.h"
1314#include "display.h"
1415#include "tx_types.h"
1516#include "proto_deserialize.h"
17+ #include "prepared_transaction.h"
1618#include "validate.h"
1719#include "canonical_hash.h"
1820
21+ #define TRANSFER_CMD_DISPLAY_FIELDS 4
22+
23+ // Configuration structures
24+ typedef struct {
25+ const char * package_id ;
26+ const char * module_name ;
27+ const char * entity_name ;
28+ } identifier_config_t ;
29+
30+ // Configuration constants
31+ static const identifier_config_t EXTERNAL_PARTY_AMULET_RULES_TEMPLATE = {
32+ .package_id = "95a88ff9ffd509e097802ecf3bbd58c83a5dff408e439cca4e2105ebd2cd0760" ,
33+ .module_name = "Splice.ExternalPartyAmuletRules" ,
34+ .entity_name = "ExternalPartyAmuletRules" };
35+
36+ static const char * TRANSFER_FACTORY_CHOICE_ID = "TransferFactory_Transfer" ;
37+
38+ static const identifier_config_t TRANSFER_RECORD = {
39+ .package_id = "55ba4deb0ad4662c4168b39859738a0e91388d252286480c7331b3f71a517281" ,
40+ .module_name = "Splice.Api.Token.TransferInstructionV1" ,
41+ .entity_name = "TransferFactory_Transfer" };
42+
43+ typedef struct {
44+ const char * item_name ;
45+ int field_index ;
46+ } field_display_t ;
47+
48+ // const char sender_label[] = "Sender";
49+ // const char receiver_label[] = "Receiver";
50+ // const char amount_label[] = "Amount";
51+ // const char instrument_id_label[] = "Instrument ID";
52+
53+ // Field display configuration
54+ const field_display_t SENDER_FIELD = {"Sender" , 0 };
55+ const field_display_t RECEIVER_FIELD = {"Receiver" , 1 };
56+ const field_display_t AMOUNT_FIELD = {"Amount" , 2 };
57+ const field_display_t INSTRUMENT_ID_FIELD = {"Instrument ID" , 3 };
58+
59+ #define TRANSFER_CMD_DISPLAY_FIELDS 4
60+
1961typedef enum {
2062 RECEIVING_DAML_TX_PART , /// Receiving part of DAML transaction
2163 RECEIVING_DAML_NODES , /// Receiving DAML nodes
@@ -24,8 +66,8 @@ typedef enum {
2466} prepared_tx_receiving_state_e ;
2567
2668static prepared_tx_receiving_state_e tx_state = RECEIVING_DAML_TX_PART ;
27-
2869static int process_prepared_tx_finalize ();
70+ static void parse_node_for_display (const Node * node , transaction_ctx_t * tx_info );
2971
3072void process_prepared_tx_init () {
3173 tx_state = RECEIVING_DAML_TX_PART ;
@@ -66,6 +108,9 @@ int process_prepared_tx_part(buffer_t *buf) {
66108 & G_context .tx_info .tx_parts_ctx .daml_transaction ,
67109 & G_context .tx_info .tx_parts_ctx .node );
68110
111+ // Extract displayable fields from the node
112+ parse_node_for_display (& G_context .tx_info .tx_parts_ctx .node , & G_context .tx_info );
113+
69114 release_node (& G_context .tx_info );
70115
71116 if (res != 0 ) {
@@ -176,3 +221,135 @@ static int process_prepared_tx_finalize() {
176221
177222 return 0 ;
178223}
224+
225+ // Helper function to match identifiers
226+ static bool match_identifier (const Identifier * id , const identifier_config_t * config ) {
227+ return strcmp (id -> package_id , (char * ) PIC (config -> package_id )) == 0 &&
228+ strcmp (id -> module_name , (char * ) PIC (config -> module_name )) == 0 &&
229+ strcmp (id -> entity_name , (char * ) PIC (config -> entity_name )) == 0 ;
230+ }
231+
232+ // Helper function to initialize transaction pairs
233+ static bool init_transaction_pairs (transaction_ctx_t * tx_info , size_t count ) {
234+ // Free existing pairs if any
235+ if (tx_info -> pairs != NULL ) {
236+ app_mem_free (tx_info -> pairs );
237+ }
238+
239+ // Free existing allocated strings if any
240+ if (tx_info -> allocated_strings != NULL ) {
241+ // Free individual strings first
242+ for (size_t i = 0 ; i < tx_info -> pairs_count ; i ++ ) {
243+ if (tx_info -> allocated_strings [i ] != NULL ) {
244+ app_mem_free (tx_info -> allocated_strings [i ]);
245+ }
246+ }
247+ app_mem_free (tx_info -> allocated_strings );
248+ }
249+
250+ // Allocate new arrays
251+ tx_info -> pairs_count = count ;
252+ tx_info -> pairs =
253+ (nbgl_contentTagValue_t * ) app_mem_alloc (count * sizeof (nbgl_contentTagValue_t ));
254+ tx_info -> allocated_strings = (char * * ) app_mem_alloc (count * sizeof (char * ));
255+
256+ if (tx_info -> pairs == NULL || tx_info -> allocated_strings == NULL ) {
257+ tx_info -> pairs_count = 0 ;
258+ return false;
259+ }
260+
261+ memset (tx_info -> pairs , 0 , count * sizeof (nbgl_contentTagValue_t ));
262+ memset (tx_info -> allocated_strings , 0 , count * sizeof (char * ));
263+
264+ return true;
265+ }
266+
267+ // Helper function to set field value safely with context-managed memory
268+ static void set_field_value (transaction_ctx_t * tx_info ,
269+ const field_display_t * field_config ,
270+ const char * value ) {
271+ if (field_config -> field_index < TRANSFER_CMD_DISPLAY_FIELDS && value != NULL ) {
272+ size_t value_len = strlen (value ) + 1 ;
273+
274+ // Allocate memory in the context's allocated_strings array
275+ tx_info -> allocated_strings [field_config -> field_index ] = (char * ) app_mem_alloc (value_len );
276+ if (tx_info -> allocated_strings [field_config -> field_index ] != NULL ) {
277+ memcpy (tx_info -> allocated_strings [field_config -> field_index ], value , value_len );
278+ tx_info -> pairs [field_config -> field_index ].item = (char * ) PIC (field_config -> item_name );
279+ tx_info -> pairs [field_config -> field_index ].value =
280+ tx_info -> allocated_strings [field_config -> field_index ];
281+ }
282+ }
283+ }
284+
285+ // Process transfer fields - maintains single-pass efficiency
286+ static void process_transfer_fields (const com_daml_ledger_api_v2_Record * rec ,
287+ transaction_ctx_t * tx_info ) {
288+ // Single pass through top-level fields
289+ for (size_t i = 0 ; i < rec -> fields_count ; i ++ ) {
290+ const RecordField * field = & rec -> fields [i ];
291+
292+ if (strcmp (field -> label , "transfer" ) == 0 ) {
293+ const com_daml_ledger_api_v2_Record * transfer_rec = & field -> value -> record ;
294+ for (size_t j = 0 ; j < transfer_rec -> fields_count ; j ++ ) {
295+ const RecordField * transfer_field = & transfer_rec -> fields [j ];
296+
297+ if (strcmp (transfer_field -> label , "sender" ) == 0 ) {
298+ set_field_value (tx_info , & SENDER_FIELD , transfer_field -> value -> party );
299+ } else if (strcmp (transfer_field -> label , "receiver" ) == 0 ) {
300+ set_field_value (tx_info , & RECEIVER_FIELD , transfer_field -> value -> party );
301+ } else if (strcmp (transfer_field -> label , "amount" ) == 0 ) {
302+ set_field_value (tx_info , & AMOUNT_FIELD , transfer_field -> value -> numeric );
303+ } else if (strcmp (transfer_field -> label , "instrumentId" ) == 0 ) {
304+ const com_daml_ledger_api_v2_Record * inst_id_rec =
305+ & transfer_field -> value -> record ;
306+ // Single pass through instrument ID fields
307+ for (size_t k = 0 ; k < inst_id_rec -> fields_count ; k ++ ) {
308+ const RecordField * inst_id_field = & inst_id_rec -> fields [k ];
309+ if (strcmp (inst_id_field -> label , "id" ) == 0 ) {
310+ set_field_value (tx_info ,
311+ & INSTRUMENT_ID_FIELD ,
312+ inst_id_field -> value -> text );
313+ }
314+ }
315+ }
316+ }
317+ }
318+ }
319+ }
320+
321+ // Helper function to process transfer command
322+ static void process_transfer_command (const com_daml_ledger_api_v2_Record * rec ,
323+ transaction_ctx_t * tx_info ) {
324+ if (!init_transaction_pairs (tx_info , TRANSFER_CMD_DISPLAY_FIELDS )) {
325+ return ;
326+ }
327+ process_transfer_fields (rec , tx_info );
328+ tx_info -> clear_signing_available = true;
329+ }
330+
331+ // Helper function to process exercise node
332+ static void process_exercise_node (const Node_Exercise * exercise , transaction_ctx_t * tx_info ) {
333+ if (!exercise -> has_template_id ) {
334+ return ;
335+ }
336+
337+ // Check if this is a transfer command
338+ if (match_identifier (& exercise -> template_id , & EXTERNAL_PARTY_AMULET_RULES_TEMPLATE ) &&
339+ strcmp (exercise -> choice_id , TRANSFER_FACTORY_CHOICE_ID ) == 0 &&
340+ match_identifier (& exercise -> chosen_value .record .record_id , & TRANSFER_RECORD )) {
341+ process_transfer_command (& exercise -> chosen_value .record , tx_info );
342+ }
343+ }
344+
345+ // Main parsing function
346+ static void parse_node_for_display (const Node * node , transaction_ctx_t * tx_info ) {
347+ if (node -> NODE_VERSION_ONEOF_FIELD != NODE_V1_TAG ) {
348+ return ;
349+ }
350+
351+ const Node_V1 * v = & node -> v1 ;
352+ if (v -> NODE_V1_KIND_ONEOF_FIELD == NODE_V1_EXERCISE_TAG ) {
353+ process_exercise_node (& v -> exercise , tx_info );
354+ }
355+ }
0 commit comments