• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinaProtocol / mina / 411

24 Jul 2025 03:14PM UTC coverage: 33.188% (-27.7%) from 60.871%
411

push

buildkite

web-flow
Merge pull request #17541 from MinaProtocol/brian/merge-compatible-into-develop

Merge compatible into develop

164 of 702 new or added lines in 96 files covered. (23.36%)

18243 existing lines in 393 files now uncovered.

23983 of 72264 relevant lines covered (33.19%)

24667.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.17
/src/lib/rosetta_lib/user_command_info.ml
1
open Core_kernel
57✔
2
module Fee_currency = Currency.Fee
3
module Amount_currency = Currency.Amount
4
open Rosetta_models
5
module Signed_command = Mina_base.Signed_command
6
module Token_id = Mina_base.Token_id
7
module Public_key = Signature_lib.Public_key
8
module Signed_command_memo = Mina_base.Signed_command_memo
9
module Payment_payload = Mina_base.Payment_payload
10
module Stake_delegation = Mina_base.Stake_delegation
11

12
let pk_to_public_key ~context (`Pk pk) =
13
  Public_key.Compressed.of_base58_check pk
×
14
  |> Result.map_error ~f:(fun _ ->
×
15
         Errors.create ~context `Public_key_format_not_valid )
×
16

17
let account_id (`Pk pk) (`Token_id token_id) =
UNCOV
18
  { Account_identifier.address = pk
×
19
  ; sub_account = None
UNCOV
20
  ; metadata = Some (Amount_of.Token_id.encode token_id)
×
21
  }
22

23
let token_id_of_account (account : Account_identifier.t) =
UNCOV
24
  let module Decoder = Amount_of.Token_id.T (Result) in
×
25
  Decoder.decode account.metadata
UNCOV
26
  |> Result.map ~f:(Option.value ~default:Amount_of.Token_id.default)
×
UNCOV
27
  |> Result.ok
×
28

29
module Op = struct
30
  type 'a t = { label : 'a; related_to : 'a option } [@@deriving equal]
×
31

32
  module T (M : Monad.S2) = struct
33
    let build ~a_eq ~plan ~f =
UNCOV
34
      let open M.Let_syntax in
×
35
      let%map _, rev_data =
UNCOV
36
        List.fold plan
×
UNCOV
37
          ~init:(M.return (0, []))
×
38
          ~f:(fun macc op ->
UNCOV
39
            let open M.Let_syntax in
×
40
            let%bind i, acc = macc in
UNCOV
41
            let operation_identifier i =
×
UNCOV
42
              { Operation_identifier.index = Int64.of_int_exn i
×
43
              ; network_index = None
44
              }
45
            in
46
            let related_operations =
47
              op.related_to
48
              |> Option.bind ~f:(fun relate ->
UNCOV
49
                     List.findi plan ~f:(fun _ a -> a_eq relate a.label) )
×
UNCOV
50
              |> Option.map ~f:(fun (i, _) -> [ operation_identifier i ])
×
UNCOV
51
              |> Option.value ~default:[]
×
52
            in
53
            let%map a =
UNCOV
54
              f ~related_operations
×
UNCOV
55
                ~operation_identifier:(operation_identifier i) op
×
56
            in
UNCOV
57
            (i + 1, a :: acc) )
×
58
      in
UNCOV
59
      List.rev rev_data
×
60
  end
61

62
  module Ident2 = struct
63
    type ('a, 'e) t = 'a
64

65
    module T = struct
66
      type ('a, 'e) t = 'a
67

68
      let map = `Define_using_bind
69

UNCOV
70
      let return a = a
×
71

UNCOV
72
      let bind a ~f = f a
×
73
    end
74

75
    include Monad.Make2 (T)
76
  end
77

78
  include T (Ident2)
79
end
80

81
module Kind = struct
82
  type t = [ `Payment | `Delegation ] [@@deriving yojson, equal, sexp, compare]
×
83
end
84

85
module Account_creation_fees_paid = struct
86
  type t = By_no_one | By_receiver of Unsigned_extended.UInt64.t
×
87
  [@@deriving equal, to_yojson, sexp, compare]
88
end
89

90
module Failure_status = struct
91
  type t = [ `Applied of Account_creation_fees_paid.t | `Failed of string ]
×
92
  [@@deriving equal, to_yojson, sexp, compare]
×
93
end
94

95
type t =
×
96
  { kind : Kind.t
×
97
  ; fee_payer : [ `Pk of string ]
×
98
  ; source : [ `Pk of string ]
×
99
  ; receiver : [ `Pk of string ]
×
100
  ; fee_token : [ `Token_id of string ]
×
101
  ; token : [ `Token_id of string ]
×
102
  ; fee : Unsigned_extended.UInt64.t
×
103
  ; nonce : Unsigned_extended.UInt32.t
×
104
  ; amount : Unsigned_extended.UInt64.t option
×
105
  ; valid_until : Unsigned_extended.UInt32.t option
×
106
  ; memo : string option
×
107
  ; hash : string
×
108
  ; failure_status : Failure_status.t option
×
109
  }
110
[@@deriving to_yojson, equal, sexp, compare]
×
111

112
module Partial = struct
113
  type t =
×
114
    { kind : Kind.t
×
115
    ; fee_payer : [ `Pk of string ]
×
116
    ; source : [ `Pk of string ]
×
117
    ; receiver : [ `Pk of string ]
×
118
    ; fee_token : [ `Token_id of string ]
×
119
    ; token : [ `Token_id of string ]
×
120
    ; fee : Unsigned_extended.UInt64.t
×
121
    ; amount : Unsigned_extended.UInt64.t option
×
122
    ; valid_until : Unsigned_extended.UInt32.t option
×
123
    ; memo : string option
×
124
    }
125
  [@@deriving to_yojson, sexp, compare, equal]
×
126

127
  module Reason = Errors.Partial_reason
128

129
  let to_user_command_payload :
130
         t
131
      -> nonce:Unsigned_extended.UInt32.t
132
      -> (Signed_command.Payload.t, Errors.t) Result.t =
133
   fun t ~nonce ->
134
    let open Result.Let_syntax in
×
135
    let%bind fee_payer_pk = pk_to_public_key ~context:"Fee payer" t.fee_payer in
×
136
    let%bind source_pk = pk_to_public_key ~context:"Source" t.source in
×
137
    let%bind receiver_pk = pk_to_public_key ~context:"Receiver" t.receiver in
×
138
    let%bind () =
139
      Result.ok_if_true
×
140
        (Public_key.Compressed.equal fee_payer_pk source_pk)
×
141
        ~error:
142
          (Errors.create
×
143
             (`Operations_not_valid
144
               [ Errors.Partial_reason.Fee_payer_and_source_mismatch ] ) )
145
    in
146
    let%bind memo =
147
      match t.memo with
148
      | Some memo -> (
×
149
          try Ok (Signed_command_memo.create_from_string_exn memo)
×
150
          with _ -> Error (Errors.create `Memo_invalid) )
×
151
      | None ->
×
152
          Ok Signed_command_memo.empty
153
    in
154
    let%map body =
155
      match t.kind with
156
      | `Payment ->
×
157
          let%map amount =
158
            Result.of_option t.amount
×
159
              ~error:
160
                (Errors.create
×
161
                   (`Operations_not_valid
162
                     [ Errors.Partial_reason.Amount_not_some ] ) )
163
          in
164
          let payload =
×
165
            { Payment_payload.Poly.receiver_pk
166
            ; amount = Amount_currency.of_uint64 amount
×
167
            }
168
          in
169
          Signed_command.Payload.Body.Payment payload
170
      | `Delegation ->
×
171
          let payload =
172
            Stake_delegation.Set_delegate { new_delegate = receiver_pk }
173
          in
174
          Result.return @@ Signed_command.Payload.Body.Stake_delegation payload
×
175
    in
176
    Signed_command.Payload.create
×
177
      ~fee:(Fee_currency.of_uint64 t.fee)
×
178
      ~fee_payer_pk ~nonce ~body ~memo
179
      ~valid_until:
180
        (Option.map ~f:Mina_numbers.Global_slot_since_genesis.of_uint32
×
181
           t.valid_until )
182
end
183

184
let forget (t : t) : Partial.t =
UNCOV
185
  { kind = t.kind
×
186
  ; fee_payer = t.fee_payer
187
  ; source = t.source
188
  ; receiver = t.receiver
189
  ; fee_token = t.fee_token
190
  ; token = t.token
191
  ; fee = t.fee
192
  ; amount = t.amount
193
  ; valid_until = t.valid_until
194
  ; memo = t.memo
195
  }
196

197
let remember ~nonce ~hash t =
198
  { kind = t.kind
×
199
  ; fee_payer = t.fee_payer
200
  ; source = t.source
201
  ; receiver = t.receiver
202
  ; fee_token = t.fee_token
203
  ; token = t.token
204
  ; fee = t.fee
205
  ; amount = t.amount
206
  ; valid_until = t.valid_until
207
  ; memo = t.memo
208
  ; hash
209
  ; nonce
210
  ; failure_status = None
211
  }
212

213
let of_operations ?memo ?valid_until (ops : Operation.t list) :
214
    (Partial.t, Partial.Reason.t) Validation.t =
215
  (* TODO: If we care about DoS attacks, break early if length too large *)
216
  (* Note: It's better to have nice errors with the validation than
217
     micro-optimize searching through a small list a minimal number of times. *)
UNCOV
218
  let find_kind k (ops : Operation.t list) =
×
UNCOV
219
    let name = Operation_types.name k in
×
UNCOV
220
    List.find ops ~f:(fun op -> String.equal op.Operation._type name)
×
UNCOV
221
    |> Result.of_option ~error:[ Partial.Reason.Can't_find_kind name ]
×
222
  in
223
  let module V = Validation in
224
  let open V.Let_syntax in
225
  let open Partial.Reason in
226
  (* For a payment we demand:
227
     *
228
     * ops = length exactly 3
229
     *
230
     * payment_source_dec with account 'a, some amount 'x, status=None
231
     * fee_payment with account 'a, some amount 'y, status=None
232
     * payment_receiver_inc with account 'b, some amount 'x, status=None
233
  *)
234
  let payment =
235
    let%map () =
UNCOV
236
      if Mina_stdlib.List.Length.Compare.(ops = 3) then V.return ()
×
UNCOV
237
      else V.fail Length_mismatch
×
238
    and account_a =
239
      let open Result.Let_syntax in
UNCOV
240
      let%bind { account; _ } = find_kind `Payment_source_dec ops
×
UNCOV
241
      and { account = account'; _ } = find_kind `Fee_payment ops in
×
UNCOV
242
      match (account, account') with
×
UNCOV
243
      | Some x, Some y when Account_identifier.equal x y ->
×
UNCOV
244
          V.return x
×
245
      | Some _, Some _ ->
×
246
          V.fail Fee_payer_and_source_mismatch
247
      | None, _ | _, None ->
×
248
          V.fail Account_not_some
249
    and token =
250
      let open Result.Let_syntax in
UNCOV
251
      let%bind { account; _ } = find_kind `Payment_source_dec ops in
×
UNCOV
252
      match account with
×
UNCOV
253
      | Some account -> (
×
254
          match token_id_of_account account with
255
          | None ->
×
256
              V.fail Incorrect_token_id
UNCOV
257
          | Some token ->
×
258
              V.return (`Token_id token) )
259
      | None ->
×
260
          V.fail Account_not_some
261
    and fee_token =
262
      let open Result.Let_syntax in
UNCOV
263
      let%bind { account; _ } = find_kind `Fee_payment ops in
×
UNCOV
264
      match account with
×
UNCOV
265
      | Some account -> (
×
266
          match token_id_of_account account with
UNCOV
267
          | Some token_id ->
×
268
              V.return (`Token_id token_id)
269
          | None ->
×
270
              V.fail Incorrect_token_id )
271
      | None ->
×
272
          V.fail Account_not_some
273
    and account_b =
274
      let open Result.Let_syntax in
UNCOV
275
      let%bind { account; _ } = find_kind `Payment_receiver_inc ops in
×
UNCOV
276
      Result.of_option account ~error:[ Account_not_some ]
×
277
    and () =
278
      if
279
        List.for_all ops ~f:(fun op ->
UNCOV
280
            let p = Option.equal String.equal op.status in
×
UNCOV
281
            p None || p (Some "") )
×
UNCOV
282
      then V.return ()
×
283
      else V.fail Status_not_pending
×
284
    and payment_amount_x =
285
      let open Result.Let_syntax in
UNCOV
286
      let%bind { amount; _ } = find_kind `Payment_source_dec ops
×
UNCOV
287
      and { amount = amount'; _ } = find_kind `Payment_receiver_inc ops in
×
UNCOV
288
      match (amount, amount') with
×
UNCOV
289
      | Some x, Some y when Amount.equal (Amount_of.negated x) y ->
×
UNCOV
290
          V.return y
×
291
      | Some _, Some _ ->
×
292
          V.fail Amount_inc_dec_mismatch
293
      | None, _ | _, None ->
×
294
          V.fail Amount_not_some
295
    and payment_amount_y =
296
      let open Result.Let_syntax in
UNCOV
297
      let%bind { amount; _ } = find_kind `Fee_payment ops in
×
UNCOV
298
      match amount with
×
UNCOV
299
      | Some x when Amount_of.compare_to_int64 x 0L < 1 ->
×
UNCOV
300
          V.return (Amount_of.negated x)
×
301
      | Some _ ->
×
302
          V.fail Fee_not_negative
303
      | None ->
×
304
          V.fail Amount_not_some
305
    in
UNCOV
306
    { Partial.kind = `Payment
×
307
    ; fee_payer = `Pk account_a.address
308
    ; source = `Pk account_a.address
309
    ; receiver = `Pk account_b.address
310
    ; fee_token
311
    ; token (* TODO: Catch exception properly on these uint64 decodes *)
UNCOV
312
    ; fee = Unsigned.UInt64.of_string payment_amount_y.Amount.value
×
UNCOV
313
    ; amount = Some (Unsigned.UInt64.of_string payment_amount_x.Amount.value)
×
314
    ; valid_until
315
    ; memo
316
    }
317
  in
318
  (* For a delegation we demand:
319
     *
320
     * ops = length exactly 2
321
     *
322
     * fee_payment with account 'a, some amount 'y, status=None
323
     * delegate_change with account 'a, metadata:{delegate_change_target:'b}, status="Pending"
324
  *)
325
  let delegation =
326
    let%map () =
UNCOV
327
      if Mina_stdlib.List.Length.Compare.(ops = 2) then V.return ()
×
UNCOV
328
      else V.fail Length_mismatch
×
329
    and account_a =
330
      let open Result.Let_syntax in
UNCOV
331
      let%bind { account; _ } = find_kind `Fee_payment ops in
×
UNCOV
332
      Option.value_map account ~default:(V.fail Account_not_some) ~f:V.return
×
333
    and fee_token =
334
      let open Result.Let_syntax in
UNCOV
335
      let%bind { account; _ } = find_kind `Fee_payment ops in
×
UNCOV
336
      match account with
×
UNCOV
337
      | Some account -> (
×
338
          match token_id_of_account account with
UNCOV
339
          | Some token_id ->
×
340
              V.return (`Token_id token_id)
341
          | None ->
×
342
              V.fail Incorrect_token_id )
343
      | None ->
×
344
          V.fail Account_not_some
345
    and account_b =
346
      let open Result.Let_syntax in
UNCOV
347
      let%bind { metadata; _ } = find_kind `Delegate_change ops in
×
UNCOV
348
      match metadata with
×
UNCOV
349
      | Some metadata -> (
×
350
          match metadata with
UNCOV
351
          | `Assoc [ ("delegate_change_target", `String s) ] ->
×
352
              return s
353
          | _ ->
×
354
              V.fail Invalid_metadata )
355
      | None ->
×
356
          V.fail Account_not_some
357
    and () =
358
      if
359
        List.for_all ops ~f:(fun op ->
UNCOV
360
            let p = Option.equal String.equal op.status in
×
UNCOV
361
            p None || p (Some "") )
×
UNCOV
362
      then V.return ()
×
363
      else V.fail Status_not_pending
×
364
    and payment_amount_y =
365
      let open Result.Let_syntax in
UNCOV
366
      let%bind { amount; _ } = find_kind `Fee_payment ops in
×
UNCOV
367
      match amount with
×
UNCOV
368
      | Some x ->
×
UNCOV
369
          V.return (Amount_of.negated x)
×
370
      | None ->
×
371
          V.fail Amount_not_some
372
    in
UNCOV
373
    { Partial.kind = `Delegation
×
374
    ; fee_payer = `Pk account_a.address
375
    ; source = `Pk account_a.address
376
    ; receiver = `Pk account_b
377
    ; fee_token
378
    ; token =
UNCOV
379
        `Token_id Token_id.(default |> to_string)
×
380
        (* only default token can be delegated *)
UNCOV
381
    ; fee = Unsigned.UInt64.of_string payment_amount_y.Amount.value
×
382
    ; amount = None
383
    ; valid_until
384
    ; memo
385
    }
386
  in
387
  let partials = [ payment; delegation ] in
388
  let oks, errs = List.partition_map partials ~f:Result.to_either in
UNCOV
389
  match (oks, errs) with
×
390
  | [], errs ->
×
391
      (* no Oks *)
392
      Error (List.concat errs)
×
UNCOV
393
  | [ partial ], _ ->
×
394
      (* exactly one Ok *)
395
      Ok partial
396
  | _, _ ->
×
397
      (* more than one Ok, a bug in our implementation *)
398
      failwith
399
        "A sequence of operations must represent exactly one user command"
400

401
let to_operations ~failure_status (t : Partial.t) : Operation.t list =
402
  (* First build a plan. The plan specifies all operations ahead of time so
403
     * we can later compute indices and relations when we're building the full
404
     * models.
405
     *
406
     * For now, relations will be defined only on the two sides of a given
407
     * transfer. ie. Source decreases, and receiver increases.
408
  *)
UNCOV
409
  let plan : 'a Op.t list =
×
UNCOV
410
    ( if not Unsigned.UInt64.(equal t.fee zero) then
×
UNCOV
411
      [ { Op.label = `Fee_payment; related_to = None } ]
×
412
    else [] )
×
413
    @ ( match failure_status with
414
      | Some (`Applied (Account_creation_fees_paid.By_receiver amount)) ->
×
415
          [ { Op.label = `Account_creation_fee_via_payment amount
416
            ; related_to = None
417
            }
418
          ]
UNCOV
419
      | _ ->
×
420
          [] )
421
    @
422
    match t.kind with
UNCOV
423
    | `Payment -> (
×
424
        (* When amount is not none, we move the amount from source to receiver
425
           -- unless it's a failure, we will capture that below *)
426
        match t.amount with
UNCOV
427
        | Some amount ->
×
428
            [ { Op.label = `Payment_source_dec amount; related_to = None }
429
            ; { Op.label = `Payment_receiver_inc amount
430
              ; related_to = Some (`Payment_source_dec amount)
431
              }
432
            ]
433
        | None ->
×
434
            [] )
UNCOV
435
    | `Delegation ->
×
436
        [ { Op.label = `Delegate_change; related_to = None } ]
437
  in
438
  Op.build
439
    ~a_eq:
440
      [%eq:
UNCOV
441
        [ `Fee_payment
×
442
        | `Payment_source_dec of Unsigned.UInt64.t
UNCOV
443
        | `Payment_receiver_inc of Unsigned.UInt64.t ]] ~plan
×
444
    ~f:(fun ~related_operations ~operation_identifier op ->
UNCOV
445
      let status, metadata, did_fail =
×
446
        match (op.label, failure_status) with
447
        (* If we're looking at mempool transactions, it's always pending *)
UNCOV
448
        | _, None ->
×
449
            (None, None, false)
450
        | _, Some (`Applied _) ->
×
451
            (Some `Success, None, false)
452
        | _, Some (`Failed reason) ->
×
453
            (Some `Failed, Some (`Assoc [ ("reason", `String reason) ]), true)
454
      in
455
      let pending_or_success_only = function
UNCOV
456
        | None ->
×
457
            None
458
        | Some (`Success | `Failed) ->
×
459
            Some `Success
460
      in
461
      let merge_metadata m1 m2 =
UNCOV
462
        match (m1, m2) with
×
463
        | None, None ->
×
464
            None
465
        | Some x, None | None, Some x ->
×
466
            Some x
467
        | Some (`Assoc xs), Some (`Assoc ys) ->
×
468
            Some (`Assoc (xs @ ys))
469
        | _ ->
×
470
            failwith "Unexpected pattern"
471
      in
472
      match op.label with
UNCOV
473
      | `Fee_payment ->
×
474
          { Operation.operation_identifier
475
          ; related_operations
476
          ; status =
477
              status |> pending_or_success_only
UNCOV
478
              |> Option.map ~f:Operation_statuses.name
×
UNCOV
479
          ; account = Some (account_id t.fee_payer t.fee_token)
×
UNCOV
480
          ; _type = Operation_types.name `Fee_payment
×
UNCOV
481
          ; amount = Some Amount_of.(negated @@ token t.fee_token t.fee)
×
482
          ; coin_change = None
483
          ; metadata
484
          }
UNCOV
485
      | `Payment_source_dec amount ->
×
486
          { Operation.operation_identifier
487
          ; related_operations
UNCOV
488
          ; status = Option.map ~f:Operation_statuses.name status
×
UNCOV
489
          ; account = Some (account_id t.source t.token)
×
UNCOV
490
          ; _type = Operation_types.name `Payment_source_dec
×
491
          ; amount =
492
              ( if did_fail then None
×
UNCOV
493
              else Some Amount_of.(negated @@ token t.token amount) )
×
494
          ; coin_change = None
495
          ; metadata
496
          }
UNCOV
497
      | `Payment_receiver_inc amount ->
×
498
          { Operation.operation_identifier
499
          ; related_operations
UNCOV
500
          ; status = Option.map ~f:Operation_statuses.name status
×
UNCOV
501
          ; account = Some (account_id t.receiver t.token)
×
UNCOV
502
          ; _type = Operation_types.name `Payment_receiver_inc
×
503
          ; amount =
504
              (if did_fail then None else Some (Amount_of.token t.token amount))
×
505
          ; coin_change = None
506
          ; metadata
507
          }
508
      | `Account_creation_fee_via_payment account_creation_fee ->
×
509
          { Operation.operation_identifier
510
          ; related_operations
511
          ; status = Option.map ~f:Operation_statuses.name status
×
512
          ; account = Some (account_id t.receiver t.token)
×
513
          ; _type = Operation_types.name `Account_creation_fee_via_payment
×
514
          ; amount = Some Amount_of.(negated @@ mina account_creation_fee)
×
515
          ; coin_change = None
516
          ; metadata
517
          }
UNCOV
518
      | `Delegate_change ->
×
519
          { Operation.operation_identifier
520
          ; related_operations
UNCOV
521
          ; status = Option.map ~f:Operation_statuses.name status
×
522
          ; account =
UNCOV
523
              Some (account_id t.source (`Token_id Amount_of.Token_id.default))
×
UNCOV
524
          ; _type = Operation_types.name `Delegate_change
×
525
          ; amount = None
526
          ; coin_change = None
527
          ; metadata =
UNCOV
528
              merge_metadata metadata
×
529
                (Some
530
                   (`Assoc
531
                     [ ( "delegate_change_target"
532
                       , `String
533
                           (let (`Pk r) = t.receiver in
534
                            r ) )
535
                     ] ) )
536
          } )
537

538
let to_operations' (t : t) : Operation.t list =
UNCOV
539
  to_operations ~failure_status:t.failure_status (forget t)
×
540

541
let non_default_token =
542
  `Token_id
543
    (Token_id.to_string (Quickcheck.random_value Token_id.gen_non_default))
57✔
544

545
let dummies =
546
  [ { kind = `Payment (* default token *)
547
    ; fee_payer = `Pk "Alice"
548
    ; source = `Pk "Alice"
549
    ; token = `Token_id Amount_of.Token_id.default
550
    ; fee_token = `Token_id Amount_of.Token_id.default
551
    ; fee = Unsigned.UInt64.of_int 2_000_000_000
57✔
552
    ; receiver = `Pk "Bob"
553
    ; nonce = Unsigned.UInt32.of_int 3
57✔
554
    ; amount = Some (Unsigned.UInt64.of_int 2_000_000_000)
57✔
555
    ; failure_status = Some (`Applied Account_creation_fees_paid.By_no_one)
556
    ; hash = "TXN_1_HASH"
557
    ; valid_until = None
558
    ; memo = Some "hello"
559
    }
560
  ; { kind = `Payment (* new account created *)
561
    ; fee_payer = `Pk "Alice"
562
    ; source = `Pk "Alice"
563
    ; token = `Token_id Amount_of.Token_id.default
564
    ; fee_token = `Token_id Amount_of.Token_id.default
565
    ; fee = Unsigned.UInt64.of_int 2_000_000_000
57✔
566
    ; receiver = `Pk "Bob"
567
    ; nonce = Unsigned.UInt32.of_int 3
57✔
568
    ; amount = Some (Unsigned.UInt64.of_int 2_000_000_000)
57✔
569
    ; failure_status =
570
        Some
571
          (`Applied
572
            (Account_creation_fees_paid.By_receiver
573
               (Unsigned.UInt64.of_int 1_000_000) ) )
57✔
574
    ; hash = "TXN_1new_HASH"
575
    ; valid_until = None
576
    ; memo = Some "hello"
577
    }
578
  ; { kind = `Payment (* failed payment *)
579
    ; fee_payer = `Pk "Alice"
580
    ; source = `Pk "Alice"
581
    ; token = `Token_id Amount_of.Token_id.default
582
    ; fee_token = `Token_id Amount_of.Token_id.default
583
    ; fee = Unsigned.UInt64.of_int 2_000_000_000
57✔
584
    ; receiver = `Pk "Bob"
585
    ; nonce = Unsigned.UInt32.of_int 3
57✔
586
    ; amount = Some (Unsigned.UInt64.of_int 2_000_000_000)
57✔
587
    ; failure_status = Some (`Failed "Failure")
588
    ; hash = "TXN_1fail_HASH"
589
    ; valid_until = None
590
    ; memo = Some "hello"
591
    }
592
  ; { kind = `Payment (* custom token *)
593
    ; fee_payer = `Pk "Alice"
594
    ; source = `Pk "Alice"
595
    ; token = non_default_token
596
    ; fee = Unsigned.UInt64.of_int 2_000_000_000
57✔
597
    ; receiver = `Pk "Bob"
598
    ; fee_token = `Token_id Amount_of.Token_id.default
599
    ; nonce = Unsigned.UInt32.of_int 3
57✔
600
    ; amount = Some (Unsigned.UInt64.of_int 2_000_000_000)
57✔
601
    ; failure_status = Some (`Applied Account_creation_fees_paid.By_no_one)
602
    ; hash = "TXN_1a_HASH"
603
    ; valid_until = None
604
    ; memo = Some "hello"
605
    }
606
  ; { kind = `Payment (* custom fee-token *)
607
    ; fee_payer = `Pk "Alice"
608
    ; source = `Pk "Alice"
609
    ; token = `Token_id Amount_of.Token_id.default
610
    ; fee = Unsigned.UInt64.of_int 2_000_000_000
57✔
611
    ; receiver = `Pk "Bob"
612
    ; fee_token = non_default_token
613
    ; nonce = Unsigned.UInt32.of_int 3
57✔
614
    ; amount = Some (Unsigned.UInt64.of_int 2_000_000_000)
57✔
615
    ; failure_status = Some (`Applied Account_creation_fees_paid.By_no_one)
616
    ; hash = "TXN_1b_HASH"
617
    ; valid_until = None
618
    ; memo = Some "hello"
619
    }
620
  ; { kind = `Delegation
621
    ; fee_payer = `Pk "Alice"
622
    ; source = `Pk "Alice"
623
    ; token = `Token_id Amount_of.Token_id.default
624
    ; fee_token = `Token_id Amount_of.Token_id.default
625
    ; fee = Unsigned.UInt64.of_int 2_000_000_000
57✔
626
    ; receiver = `Pk "Bob"
627
    ; nonce = Unsigned.UInt32.of_int 3
57✔
628
    ; amount = None
629
    ; failure_status = Some (`Applied Account_creation_fees_paid.By_no_one)
630
    ; hash = "TXN_2_HASH"
631
    ; valid_until = None
632
    ; memo = Some "hello"
633
    }
634
  ]
57✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc