snarkvm-synthesizer-program 4.6.1

Program for a decentralized virtual machine
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
// Copyright (c) 2019-2026 Provable Inc.
// This file is part of the snarkVM library.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/********************************************** INVARIANTS ************************************************************/

// 1. contains committee[r0] =>
//   a. contains delegated[r0]
//      - The set of delegated addresses is superset of active validators.
// 2. delegated[*] >= 10,000
//   a. contain bonded[*].microcredits >= 10,000
//      - Delegators can bond to any address with the 10,000 credit minimum
// 3. delegated[r0] < 10,000,000 =>
//   a. !contain committee[r0]
//      - If the delegated total (which includes the self bond) is less than 10,000,000 credits, it won't be in the committee
// 4. bond[r0].validator == r0 =>
//   a. contains committee[r0]
//     - Self bonded addresses are always active validators. Delegators **can** force validators to unbond by removing sufficient delegation
// 5. committee[r0] =>
//   a. bonded[r0].microcredits >= 100 credits
//      - Self bonded addresses are always active validators with at least 100 credits self bonded
//   b. delegated[r0] >= 10,000,000 credits
//      - The total delegated amount for an active validator is at least 10,000,000 credits
// 6. delegated[r0] == sum bond[*].microcredits by bond[].validator == r0
// 7. bond[r0].microcredits < 10,000 =>
//   a. bond[r0].validator == r0
//   b. contains committee[r0]
//   - A bond value < 10,000 implies self-bonded active validator.
// 8. bond[r0].validator != r0 =>
//   a. bond[r0].microcredits >= 10,000
// 9. count bonded[] - count committee[] == metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0]
//   - Delegator count consistent with metadata.
// 10. count committee[] == metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]
// 11. 100,000 + count committee[] >= count bonded[] >= count delegated[] >= count committee[]
// 12. bond[*].microcredits > 100


/**********************************************************************************************************************/

program credits.aleo;

/**********************************************************************************************************************/

/// The `committee` mapping contains the active validator set and their corresponding stake.
mapping committee:
    // The key represents the address of the validator.
    key as address.public;
    // The value represents the committee state of the validator.
    value as committee_state.public;

// The `committee_state` struct tracks the total stake of the validator, and whether they are open to new stakers.
struct committee_state:
    // The boolean flag indicating if the validator is open to new stakers.
    is_open as boolean;
    // The percentage amount (from 0 to 100, inclusive) of rewards that retained by the validator.
    commission as u8;

/**********************************************************************************************************************/

// The `delegated` mapping tracks the total amount of microcredits that are prebonded and bonded to validator addresses.
// Note: The mapping includes both prebonded and bonded microcredits. However, it does not contain unbonding microcredits.
mapping delegated:
    // The key represents the address of the validator.
    key as address.public;
    // The value represents the amount of microcredits bonded to the validator, by the validator and its delegators.
    value as u64.public;

/**********************************************************************************************************************/

/// The `metadata` mapping stores:
///   - The number of members in the committee.
///   - The number of delegators.
mapping metadata:
    // The key represents the index at which the count is stored.
    //    - This address (aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc) stores the number of **members** in the committee.
    //    - This address (aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0) stores the number of **delegators**.
    key as address.public;
    // The value represents the count.
    value as u32.public;

/**********************************************************************************************************************/

// The `bonded` mapping represents the amount of microcredits that are currently bonded.
mapping bonded:
    // The key represents the address of the staker, which includes the validators and their delegators.
    key as address.public;
    // The value represents the bond state.
    value as bond_state.public;

// The `bond_state` struct tracks the amount of microcredits that are currently bonded to the specified validator.
struct bond_state:
    // The address of the validator.
    validator as address;
    // The amount of microcredits that are currently bonded to the specified validator.
    microcredits as u64;

/**********************************************************************************************************************/

// The `unbonding` mapping contains a set of stakers with their unbonding microcredits and unlock height.
mapping unbonding:
    // The key represents the address of the staker, which includes the validators and their delegators.
    key as address.public;
    // The value represents the unbond state.
    value as unbond_state.public;

// The `unbond_state` struct tracks the microcredits that are currently unbonding, along with the unlock height.
struct unbond_state:
    // The amount of microcredits that are currently unbonding.
    microcredits as u64;
    // The block height at which the unbonding will be complete, and can be claimed.
    height as u32;

/**********************************************************************************************************************/

// The `account` mapping is used to store credits publicly.
mapping account:
    // The key represents the address of the owner.
    key as address.public;
    // The value represents the amount of public microcredits that belong to the specified owner.
    value as u64.public;

/**********************************************************************************************************************/

// The `withdraw` mapping contains the staking address and their corresponding withdrawal address.
mapping withdraw:
    // The key represents the staking address of the owner.
    key as address.public;
    // The value represents the withdrawal address of the owner.
    value as address.public;

/**********************************************************************************************************************/

mapping pool:
    // The key represents the index at which the count is stored.
    //    - This address (aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc) stores the number of microcredits
    //      that have been migrated into the pool for records after the inclusion upgrade.
    key as address.public;
    // The value represents the amount of microcredits in the pool.
    value as u64.public;

/**********************************************************************************************************************/

// The `credits` record is used to store credits privately.
record credits:
    // The address of the owner.
    owner as address.private;
    // The amount of private microcredits that belong to the specified owner.
    microcredits as u64.private;

/**********************************************************************************************************************/

// This function allows a validator to bond their microcredits to themselves and specify a withdrawal address
// and commission percentage.
//
// The commission percentage is the portion of rewards that the validator may claim for itself.
// Note: Setting the commission percentage to 100 results in all rewards being retained for the validator.
//
// The corresponding functions for 'bond_validator' are 'unbond_public' and 'claim_unbond_public'.
function bond_validator:
    // Input the withdrawal address.
    input r0 as address.public;
    // Input the amount of microcredits to bond.
    input r1 as u64.public;
    // Input the commission percentage.
    input r2 as u8.public;

    // Ensure the withdrawal address is not the validator address.
    assert.neq self.signer r0;

    // Determine if the amount is at least 1 credit.
    gte r1 1_000_000u64 into r3;
    // Enforce the amount is at least 1 credit.
    assert.eq r3 true;

    // Ensure the commission percentage does not exceed 100%.
    gt r2 100u8 into r4;
    assert.neq r4 true;

    // Bond the specified amount of microcredits to the specified validator.
    async bond_validator self.signer r0 r1 r2 into r5;
    // Output the finalize future.
    output r5 as credits.aleo/bond_validator.future;

finalize bond_validator:
    // Input the validator's address.
    input r0 as address.public;
    // Input the withdrawal address.
    input r1 as address.public;
    // Input the amount of microcredits to bond.
    input r2 as u64.public;
    // Input the commission percentage.
    input r3 as u8.public;

    // Retrieve the withdrawal address for the validator.
    get.or_use withdraw[r0] r1 into r4;
    // Ensure that the withdrawal address is consistent.
    assert.eq r1 r4;

    // Construct the initial committee state.
    // Note: We set the initial 'is_open' state to 'true'.
    cast true r3 into r5 as committee_state;
    // Retrieve the committee state of the specified validator.
    get.or_use committee[r0] r5 into r6;

    // Ensure the commission percentage remains unchanged.
    // Note: The commission percentage can only be set when the validator bonds for the first time.
    assert.eq r3 r6.commission;

    /* Bonded */

    // Construct the initial bond state.
    cast r0 0u64 into r7 as bond_state;
    // Get the bond state for the signer, or default to the initial bond state.
    get.or_use bonded[r0] r7 into r8;
    // Enforce the validator matches in the bond state.
    assert.eq r8.validator r0;

    // Increment the amount of microcredits for the bond state.
    add r8.microcredits r2 into r9;
    // Construct the updated bond state.
    cast r0 r9 into r10 as bond_state;

    /* Delegated */

    // Include total pre-delegated amount when deciding if validator has enough credits to join committee.
    // First get the current delegated amount, or start with 0 if none delegated.
    get.or_use delegated[r0] 0u64 into r11;
    // Self-bond amount + existing delegated amount = total bond.
    add r2 r11 into r12;
    // Determine if the amount is at least 10 million credits.
    gte r12 10_000_000_000_000u64 into r13;
    // Enforce the amount is at least 10 million credits.
    assert.eq r13 true;

    /* Account */

    // Get the balance of the signer.
    // If the account does not exist, this finalize scope will fail.
    get account[r0] into r14;
    // Decrement the balance of the signer.
    sub r14 r2 into r15;

    /* Writes */

    // if (new_validator)
    contains committee[r0] into r16;
    branch.eq r16 true to validator_in_committee;
    // {
        // Set the withdrawal address.
        // Note: This operation is only necessary on the first time for a validator entry, in order to initialize the value.
        set r4 into withdraw[r0];

        // Check if the initial bond amount is at least 100 credits.
        gte r2 100_000_000u64 into r17;
        // Ensure that the initial bond is at least 100 credits.
        assert.eq r17 true;

        // Get the committee size.
        get.or_use metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] 0u32 into r18;
        // Increment the committee size by one.
        add r18 1u32 into r19;
        // Set the new committee size.
        set r19 into metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc];

        // Check if the validator exists in the unbonding state.
        contains unbonding[r0] into r20;
        // Ensure the validator currently is not unbonding.
        assert.eq r20 false;
    // }

    position validator_in_committee;

    // Update the committee state of the specified validator.
    set r6 into committee[r0];
    // Update the delegated amount.
    set r12 into delegated[r0];
    // Update the bond state for the signer.
    set r10 into bonded[r0];
    // Update the balance of the signer.
    set r15 into account[r0];

/**********************************************************************************************************************/

// This function allows any delegator to bond their microcredits to a validator and specify a withdrawal address.
//
// The declared validator is **not** required to be in the committee yet, however it may **not** be unbonding.
// This function enforces a minimum of 10,000 credits for each delegator.
// The maximum number of delegators allowed in the committee is 100,000 delegator addresses.
//
// The corresponding functions for 'bond_public' are 'bond_validator', 'unbond_public', and 'claim_unbond_public'.
function bond_public:
    // Input the validator's address.
    input r0 as address.public;
    // Input the withdrawal address.
    input r1 as address.public;
    // Input the amount of microcredits to bond.
    input r2 as u64.public;

    // Determine if the amount is at least one credit.
    gte r2 1_000_000u64 into r3;
    // Enforce the amount is at least one credit.
    assert.eq r3 true;

    // Do not allow self-bonding. Validators start with bond_validator.
    assert.neq self.caller r0;

    // Bond the specified amount of microcredits to the specified validator.
    async bond_public self.caller r0 r1 r2 into r4;
    // Output the finalize future.
    output r4 as credits.aleo/bond_public.future;

finalize bond_public:
    // Input the staker's address.
    input r0 as address.public;
    // Input the validator's address.
    input r1 as address.public;
    // Input the withdrawal address.
    input r2 as address.public;
    // Input the amount of microcredits to bond.
    input r3 as u64.public;

    // Retrieve the withdrawal address for the staker.
    get.or_use withdraw[r0] r2 into r4;
    // Ensure that the withdrawal address is consistent.
    assert.eq r2 r4;

    // Check if the delegator is already bonded to the validator.
    contains bonded[r0] into r5;
    branch.eq r5 true to continue_bond_delegator;
    // {
        // Set the withdrawal address.
        // Note: This operation is only necessary on the first time for a staker entry, in order to initialize the value.
        set r2 into withdraw[r0];

        // Ensure that the validator is open to new stakers. By default, `is_open` is set to `true`.
        cast true 0u8 into r6 as committee_state;
        get.or_use committee[r1] r6 into r7;
        assert.eq r7.is_open true;

        // Get the number of delegators.
        get.or_use metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] 0u32 into r8;
        // Increment the number of bonded delegators by one.
        add r8 1u32 into r9;
        // Determine if the number of delegators is less than or equal to 100_000.
        lte r9 100_000u32 into r10;
        // Enforce that the number of delegators is less than or equal to 100_000.
        assert.eq r10 true;
        // Set the new number of delegators.
        set r9 into metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0];
    // }

    position continue_bond_delegator;

    /* Bonded */

    // Construct the initial bond state.
    cast r1 0u64 into r11 as bond_state;
    // Get the bond state for the caller, or default to the initial bond state.
    get.or_use bonded[r0] r11 into r12;
    // Enforce the validator matches in the bond state.
    assert.eq r12.validator r1;

    // Increment the microcredits in the bond state.
    add r12.microcredits r3 into r13;

    // Determine if the amount is at least 10 thousand credits.
    gte r13 10_000_000_000u64 into r14;
    // Enforce the amount is at least 10 thousand credits.
    assert.eq r14 true;

    // Construct the updated bond state.
    cast r1 r13 into r15 as bond_state;

    /* Account */

    // Get the balance of the caller.
    // If the account does not exist, this finalize scope will fail.
    get account[r0] into r16;
    // Decrement the balance of the caller.
    sub r16 r3 into r17;

    /* Delegated */

    // Get current total delegated amount.
    get.or_use delegated[r1] 0u64 into r18;
    // Add new bond amount to current delegation.
    add r3 r18 into r19;

    /* Unbonding */

    // Check if the validator exists in the unbonding state.
    contains unbonding[r1] into r20;
    // Ensure the validator currently is not unbonding.
    assert.eq r20 false;

    /* Writes */

    // Update the bond state for the caller.
    set r15 into bonded[r0];
    // Update the balance of the caller.
    set r17 into account[r0];
    // Update delegated amount.
    set r19 into delegated[r1];

/**********************************************************************************************************************/

// This function allows any staker to unbond their microcredits from a validator,
// callable only by using the withdrawal address of the staker or the validator.
//
// **Validators**: It will remove the validator if the self bonded balance falls below 100 credits
// or the total delegated balance falls below 10M credits.
// **Delegators**: It will remove the validator if the total delegated balance falls below 10M credits.
// It will remove the entire bond_state of the delegator if it falls below 10,000 credits.
//
// Validators are permitted to fully unbond any of their delegators. When a validator unbonds a delegator,
// the entire bonded balance is unbonded, regardless of the amount of microcredits and may end up removing the validator
// from the committee if the total delegated balance falls below 10M credits.
//
// The corresponding function for 'unbond_public' is 'claim_unbond_public'.
function unbond_public:
    // Input the staker's address.
    input r0 as address.public;
    // Input the amount of microcredits to unbond.
    input r1 as u64.public;

    // Unbond the specified amount of microcredits for the specified validator.
    async unbond_public self.caller r0 r1 into r2;
    // Output the finalize future.
    output r2 as credits.aleo/unbond_public.future;

finalize unbond_public:
    // Input the caller's address.
    input r0 as address.public;
    // Input the staker's address.
    input r1 as address.public;
    // Input the amount of microcredits to unbond.
    input r2 as u64.public;

    // Set the default unbonding state.
    add block.height 360u32 into r3;
    // Construct the default unbonding state.
    cast 0u64 r3 into r4 as unbond_state;

    // Retrieve the staker's bond state.
    // Note: If the bonded state does not exist, reject the transition.
    get bonded[r1] into r5;

    /* Check Caller's Permission */

    // Get the staker's withdrawal address.
    get withdraw[r1] into r6;
    // Check if the caller's address equals the staker's withdrawal address.
    is.eq r0 r6 into r7;

    // Check if the validator's withdrawal address has been set.
    // Note: This contains check must use `withdraw` and **not** `committee` to ensure the validator
    // can unbond delegators even when the validator is not in the committee. Of course, if the validator is
    // in the committee, then the validator must be able to unbond its delegators.
    contains withdraw[r5.validator] into r8;
    // Get the validator's withdrawal address from the bond state, using the zero address as the default.
    get.or_use withdraw[r5.validator] aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc into r9;
    // Check if the validator's withdrawal address matches the caller.
    is.eq r0 r9 into r10;
    // AND the withdraw address has been set (to prevent an edge case where a validator uses the zero address as a withdrawal address).
    and r8 r10 into r11;

    // Either the caller is the staker's withdrawal address OR the validator's withdrawal address is set to the caller.
    or r7 r11 into r12;
    // Enforce the permission as `true`.
    assert.eq r12 true;

    // Check if bonded to self (validator) or to a different address (delegator).
    is.eq r5.validator r1 into r13;
    branch.eq r13 true to unbond_validator;

    /* Unbond the Delegator */
    // {
        // Retrieve or initialize the unbonding state.
        get.or_use unbonding[r1] r4 into r14;
        // Retrieve the delegated amount in microcredits for the validator.
        get delegated[r5.validator] into r15;

        // Calculate new bonded microcredits.
        // Note: If the subtraction underflows, reject the transition.
        sub r5.microcredits r2 into r16;

        // Check if the delegator will fall below 10,000 bonded credits.
        lt r16 10_000_000_000u64 into r17;

        // If the validator is forcing the delegator to unbond OR the delegator will fall below 10,000 bonded credits.
        or r11 r17 into r18;

        // Determine the amount to unbond: requested amount if >= 10,000 credits, otherwise the full bonded amount.
        ternary r18 r5.microcredits r2 into r19;

        /* Unbonding */

        // Increment existing unbond amount by the new unbond amount.
        // Note: If the addition overflows, reject the transition.
        add r14.microcredits r19 into r20;
        // Construct the updated unbond state.
        cast r20 r3 into r21 as unbond_state;
        // Store the new unbonding state.
        set r21 into unbonding[r1];

        /* Delegated */

        // Calculate the new delegated total for the validator.
        // Note: If the subtraction underflows, reject the transition.
        sub r15 r19 into r22;
        // Store the new delegated total.
        set r22 into delegated[r5.validator];

        // Check if the new bonded state is falling below 10,000 credits, or if the validator is forcing an unbond on the delegator.
        branch.eq r18 true to remove_delegator;

        /* Decrement the Delegator */
        // {
            /* Bonded */

            // Construct the new bond state.
            cast r5.validator r16 into r23 as bond_state;
            // Set the new bond state.
            set r23 into bonded[r1];

            // Jump to the end of the unbond delegator process.
            branch.eq true true to end_unbond_delegator;
        // }

        position remove_delegator;
        // {
            /* Bonded */

            // Remove the bonded state.
            remove bonded[r1];

            /* Metadata */

            // Retrieve the current number of delegators.
            get metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] into r24;
            // Decrement the number of delegators by one.
            sub r24 1u32 into r25;
            // Store the new number of delegators.
            set r25 into metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0];
        // } Intentionally fall through to end_unbond_delegator

        position end_unbond_delegator;
        // {
            // Check if the new delegated total is at least 10M credits.
            gte r22 10_000_000_000_000u64 into r26;
            // Jump to end if the delegated total is at least 10M credits.
            branch.eq r26 true to end;
        // }
        // If the delegated total is below 10M credits, continue to unbonding the validator.
    //}

    /* Unbond the Validator */
    position unbond_validator;
    // {
        // Check if the committee contains the validator.
        contains committee[r5.validator] into r27;
        // Determine if the delegator unbonding from a validator is not in the committee.
        nor r13 r27 into r28;
        // Jump to the end, if the delegator unbonding from a validator is not in the committee.
        branch.eq r28 true to end;

        // Retrieve the committee state.
        // Note: If the committee state does not exist, reject the transition.
        get committee[r5.validator] into r29;
        // Retrieve the bond state of the validator.
        // Note: If the bonded state does not exist, reject the transition.
        get bonded[r5.validator] into r30;
        // Retrieve the total delegated balance for the validator.
        // Note: If the delegated state does not exist, reject the transition.
        get delegated[r5.validator] into r31;

        // Check if the current delegated amount is below 10M credits.
        // Note: This will only be `true` if a delegator unbond caused the validator to fall below 10M credits.
        lt r31 10_000_000_000_000u64 into r32;
        branch.eq r32 true to remove_validator;

        // Calculate the updated delegated total after unbonding.
        // Note: If the subtraction underflows, reject the transition.
        sub r31 r2 into r33;

        // Calculate the updated amount of microcredits after unbonding.
        sub r30.microcredits r2 into r34;

        // Check if the new bond state is below the required minimums.
        gte r34 100_000_000u64 into r35; // Minimum self bond requirement: 100 credits.
        gte r33 10_000_000_000_000u64 into r36; // Minimum total delegated requirement: 10M credits.

        // If either bond state is below the thresholds, remove the validator from the committee.
        and r35 r36 into r37;
        branch.eq r37 false to remove_validator;

        /* Unbonding */

        // Retrieve or initialize the unbonding state.
        get.or_use unbonding[r5.validator] r4 into r38;
        // Increment the unbond amount.
        // Note: If the addition overflows, reject the transition.
        add r38.microcredits r2 into r39;
        // Set the updated unbond state.
        cast r39 r3 into r40 as unbond_state;
        // Store the new unbonding state.
        set r40 into unbonding[r5.validator];

        /* Delegated */

        // Store the new delegated total.
        set r33 into delegated[r5.validator];

        /* Bonded */

        // Construct and store the new bond state.
        cast r5.validator r34 into r41 as bond_state;
        set r41 into bonded[r5.validator];

        // Jump to end.
        branch.eq true true to end;
    //}

    /* Remove the Validator from the Committee */
    position remove_validator;
    // {
        /* Committee */

        // Remove the validator from the committee.
        remove committee[r5.validator];

        /* Metadata */

        // Retrieve the current committee size.
        get metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] into r42;
        // Decrement the committee size by one.
        sub r42 1u32 into r43;
        // Store the new committee size.
        set r43 into metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc];

        /* Delegated */

        // Decrease the delegated total by the bonded amount.
        sub r31 r30.microcredits into r44;
        // Store the new delegated total.
        set r44 into delegated[r5.validator];

        /* Bonded */

        // Remove the bonded state.
        remove bonded[r5.validator];

        /* Unbonding */

        // Retrieve or initialize the unbonding state.
        get.or_use unbonding[r5.validator] r4 into r45;
        // Increment the unbond amount by the full bonded amount.
        // Note: If the addition overflows, reject the transition.
        add r30.microcredits r45.microcredits into r46;
        // Construct the updated unbond state.
        cast r46 r3 into r47 as unbond_state;
        // Store the new unbonding state.
        set r47 into unbonding[r5.validator];
    //}

    position end;

/**********************************************************************************************************************/

// The `claim_unbond_public` function allows any staker to claim their microcredits
// to their withdrawal address after the unbonding period.
//
// Note: Please be advised that this function is callable by any user for any staker.
//
// This function also removes the staker's withdrawal address if the staker no longer has any bonded balance.
function claim_unbond_public:
    // Input the staker's address.
    input r0 as address.public;

    // Claim the unbonded microcredits.
    async claim_unbond_public r0 into r1;
    // Output the finalize future.
    output r1 as credits.aleo/claim_unbond_public.future;

finalize claim_unbond_public:
    // Input the staker's address.
    input r0 as address.public;

    // Get the unbond state for the address, or fail if it does not exist.
    get unbonding[r0] into r1;
    // Determine if unbonding is complete.
    gte block.height r1.height into r2;
    // Enforce the unbonding is complete.
    assert.eq r2 true;

    /* Withdraw */

    // Get the withdrawal address for the address.
    get withdraw[r0] into r3;

    /* Account */

    // Add the unbonded amount to the withdrawal address public balance.
    // Increments `account[r3]` by `r1.microcredits`.
    // If `account[r3]` does not exist, 0u64 is used.
    // If `account[r3] + r1.microcredits` overflows, `claim_unbond_public` is reverted.
    get.or_use account[r3] 0u64 into r4;
    add r1.microcredits r4 into r5;
    set r5 into account[r3];

    /* Unbonding */

    // Remove the unbond state for the staker.
    remove unbonding[r0];

    // Check if the staker is still bonded.
    contains bonded[r0] into r6;
    // Ends the `claim_unbond_public` logic.
    branch.eq r6 true to end;

    /* Withdraw */

    // If the caller is no longer bonded, remove the withdrawal address.
    remove withdraw[r0];

    // The terminus.
    position end;

/**********************************************************************************************************************/

// This function allows a validator to set their state to be either opened or closed to new stakers.
// When the validator is open to new stakers, any staker (including the validator) can bond or unbond from the validator.
// When the validator is closed to new stakers, existing stakers can still bond or unbond from the validator, but new stakers cannot bond.
//
// This function serves two primary purposes:
// 1. Allow a validator to leave the committee, by closing themselves to stakers and then unbonding all of their stakers.
// 2. Allow a validator to maintain their % of stake, by closing themselves to allowing more stakers to bond to them.
function set_validator_state:
    // Input the 'is_open' state.
    input r0 as boolean.public;
    // Set the validator to be either open or closed to new stakers.
    async set_validator_state self.caller r0 into r1;
    // Output the finalize future.
    output r1 as credits.aleo/set_validator_state.future;

finalize set_validator_state:
    // Input the validator's address.
    input r0 as address.public;
    // Input the 'is_open' state.
    input r1 as boolean.public;

    // Get the committee state for the specified validator.
    // If the validator does not exist, this finalize scope will fail.
    get committee[r0] into r2;

    // Construct the updated committee state.
    cast r1 r2.commission into r3 as committee_state;
    // Update the committee state for the specified validator.
    set r3 into committee[r0];

/**********************************************************************************************************************/

// The `transfer_public` function sends the specified amount
// from the caller's `account` to the receiver's `account`.
function transfer_public:
    // Input the receiver.
    input r0 as address.public;
    // Input the amount.
    input r1 as u64.public;
    // Transfer the credits publicly.
    async transfer_public self.caller r0 r1 into r2;
    // Output the finalize future.
    output r2 as credits.aleo/transfer_public.future;

finalize transfer_public:
    // Input the caller.
    input r0 as address.public;
    // Input the receiver.
    input r1 as address.public;
    // Input the amount.
    input r2 as u64.public;
    // Decrements `account[r0]` by `r2`.
    // If `account[r0] - r2` underflows, `transfer_public` is reverted.
    get account[r0] into r3;
    sub r3 r2 into r4;
    set r4 into account[r0];
    // Increments `account[r1]` by `r2`.
    // If `account[r1]` does not exist, 0u64 is used.
    // If `account[r1] + r2` overflows, `transfer_public` is reverted.
    get.or_use account[r1] 0u64 into r5;
    add r5 r2 into r6;
    set r6 into account[r1];

/**********************************************************************************************************************/

// The `transfer_public_as_signer` function sends the specified amount
// from the signer's `account` to the receiver's `account`.
function transfer_public_as_signer:
    // Input the receiver.
    input r0 as address.public;
    // Input the amount.
    input r1 as u64.public;
    // Transfer the credits publicly.
    async transfer_public_as_signer self.signer r0 r1 into r2;
    // Output the finalize future.
    output r2 as credits.aleo/transfer_public_as_signer.future;

finalize transfer_public_as_signer:
    // Input the signer.
    input r0 as address.public;
    // Input the receiver.
    input r1 as address.public;
    // Input the amount.
    input r2 as u64.public;
    // Decrements `account[r0]` by `r2`.
    // If `account[r0] - r2` underflows, `transfer_public_as_signer` is reverted.
    get account[r0] into r3;
    sub r3 r2 into r4;
    set r4 into account[r0];
    // Increments `account[r1]` by `r2`.
    // If `account[r1]` does not exist, 0u64 is used.
    // If `account[r1] + r2` overflows, `transfer_public_as_signer` is reverted.
    get.or_use account[r1] 0u64 into r5;
    add r5 r2 into r6;
    set r6 into account[r1];

/**********************************************************************************************************************/

// The `transfer_private` function sends the specified amount
// from the sender's record to the receiver in a record.
function transfer_private:
    // Input the sender's record.
    input r0 as credits.record;
    // Input the receiver.
    input r1 as address.private;
    // Input the amount.
    input r2 as u64.private;
    // Checks the given record has a sufficient amount.
    // This `sub` operation is safe, and the proof will fail
    // if an underflow occurs. The destination register `r3` holds
    // the change amount to be returned to the sender.
    sub r0.microcredits r2 into r3;
    // Construct a record for the specified receiver.
    cast r1 r2 into r4 as credits.record;
    // Construct a record with the change amount for the sender.
    cast r0.owner r3 into r5 as credits.record;
    // Output the receiver's record.
    output r4 as credits.record;
    // Output the sender's change record.
    output r5 as credits.record;

/**********************************************************************************************************************/

// The `transfer_private_to_public` function turns a specified amount
// from a record into public credits for the specified receiver.
//
// This function preserves privacy for the sender's record, however
// it publicly reveals the receiver and the amount.
function transfer_private_to_public:
    // Input the sender's record.
    input r0 as credits.record;
    // Input the receiver.
    input r1 as address.public;
    // Input the amount.
    input r2 as u64.public;
    // Checks the given record has a sufficient amount.
    // This `sub` operation is safe, and the proof will fail
    // if an underflow occurs. The destination register `r3` holds
    // the change amount for the sender.
    sub r0.microcredits r2 into r3;
    // Construct a record with the change amount for the sender.
    cast r0.owner r3 into r4 as credits.record;
    // Increment the amount publicly for the receiver.
    async transfer_private_to_public r1 r2 into r5;
    // Output the sender's change record.
    output r4 as credits.record;
    // Output the finalize future.
    output r5 as credits.aleo/transfer_private_to_public.future;

finalize transfer_private_to_public:
    // Input the receiver.
    input r0 as address.public;
    // Input the amount.
    input r1 as u64.public;
    // Retrieve the balance of the receiver.
    // If `account[r0]` does not exist, 0u64 is used.
    get.or_use account[r0] 0u64 into r2;
    // Increments `account[r0]` by `r1`.
    // If `r1 + r2` overflows, `transfer_private_to_public` is reverted.
    add r1 r2 into r3;
    // Updates the balance of the sender.
    set r3 into account[r0];

/**********************************************************************************************************************/

// The `transfer_public_to_private` function turns a specified amount
// from the mapping `account` into a record for the specified receiver.
//
// This function publicly reveals the sender, the receiver, and the specified amount.
// However, subsequent methods using the receiver's record can preserve the receiver's privacy.
function transfer_public_to_private:
    // Input the receiver.
    input r0 as address.private;
    // Input the amount.
    input r1 as u64.public;
    // Construct a record for the receiver.
    cast r0 r1 into r2 as credits.record;
    // Decrement the balance of the sender publicly.
    async transfer_public_to_private self.caller r1 into r3;
    // Output the record of the receiver.
    output r2 as credits.record;
    // Output the finalize future.
    output r3 as credits.aleo/transfer_public_to_private.future;

finalize transfer_public_to_private:
    // Input the sender.
    input r0 as address.public;
    // Input the amount.
    input r1 as u64.public;
    // Retrieve the balance of the sender.
    get account[r0] into r2;
    // Decrements `account[r0]` by `r1`.
    // If `r2 - r1` underflows, `transfer_public_to_private` is reverted.
    sub r2 r1 into r3;
    // Updates the balance of the sender.
    set r3 into account[r0];

/**********************************************************************************************************************/

// The `join` function combines two records into one.
function join:
    // Input the first record.
    input r0 as credits.record;
    // Input the second record.
    input r1 as credits.record;
    // Combines the amount of the first record and the second record.
    // This `add` operation is safe, and the proof will fail
    // if an overflow occurs.
    add r0.microcredits r1.microcredits into r2;
    // Construct a record with the combined amount.
    cast r0.owner r2 into r3 as credits.record;
    // Output the record.
    output r3 as credits.record;

/**********************************************************************************************************************/

// The `split` function splits a record into two records. The given input amount will be stored in the first record,
// and the remaining amount will be stored in the second record, with the fee deducted from the remaining amount.
// If the caller executes a transaction that contains only a call to this function, then the transaction does not
// require a fee, unless the caller wishes to provide an additional fee. Transactions that contain multiple transitions
// (that include one or more calls to this function) will require a fee as per standard consensus rules.
function split:
    // Input the record.
    input r0 as credits.record;
    // Input the amount to split.
    input r1 as u64.private;
    // Checks the given record has a sufficient amount to split.
    // This `sub` operation is safe, and the proof will fail
    // if an underflow occurs.
    sub r0.microcredits r1 into r2;
    // Checks the given record has a sufficient fee to remove.
    // This `sub` operation is safe, and the proof will fail
    // if an underflow occurs.
    sub r2 10_000u64 into r3;
    // Construct the first record.
    cast r0.owner r1 into r4 as credits.record;
    // Construct the second record.
    cast r0.owner r3 into r5 as credits.record;
    // Output the first record.
    output r4 as credits.record;
    // Output the second record.
    output r5 as credits.record;

/**********************************************************************************************************************/

// The `fee_private` function charges the specified amount from the sender's record.
function fee_private:
    // Input the sender's record.
    input r0 as credits.record;
    // Input the amount.
    input r1 as u64.public;
    // Input the priority fee amount.
    input r2 as u64.public;
    // Input the deployment or execution ID.
    input r3 as field.public;
    // Ensure the amount is nonzero.
    assert.neq r1 0u64;
    // Ensure the deployment or execution ID is nonzero.
    assert.neq r3 0field;
    // Add the fee and priority fee amounts.
    add r1 r2 into r4;
    // Checks the given record has a sufficient amount.
    // This `sub` operation is safe, and the proof will fail
    // if an underflow occurs. The destination register `r3` holds
    // the change amount for the sender.
    sub r0.microcredits r4 into r5;
    // Construct a record with the change amount for the sender.
    cast r0.owner r5 into r6 as credits.record;
    // Output the sender's change record.
    output r6 as credits.record;

/**********************************************************************************************************************/

// The `fee_public` function charges the specified amount from the sender's account.
function fee_public:
    // Input the amount.
    input r0 as u64.public;
    // Input the priority fee amount.
    input r1 as u64.public;
    // Input the deployment or execution ID.
    input r2 as field.public;
    // Ensure the amount is nonzero.
    assert.neq r0 0u64;
    // Ensure the deployment or execution ID is nonzero.
    assert.neq r2 0field;
    // Add the fee and priority fee amounts.
    add r0 r1 into r3;
    // Decrement the balance of the sender publicly.
    async fee_public self.signer r3 into r4;
    // Output the finalize future.
    output r4 as credits.aleo/fee_public.future;

finalize fee_public:
    // Input the sender's address.
    input r0 as address.public;
    // Input the total fee amount.
    input r1 as u64.public;
    // Retrieve the balance of the sender.
    // If `account[r0]` does not exist, `fee_public` is reverted.
    get account[r0] into r2;
    // Decrements `account[r0]` by `r1`.
    // If `r2 - r1` underflows, `fee_public` is reverted.
    sub r2 r1 into r3;
    // Updates the balance of the sender.
    set r3 into account[r0];

/**********************************************************************************************************************/

// The `upgrade` function migrates the input record to the pool of records after the inclusion upgrade.
// The original record will be spent and the new record will be created with the same amount of credits.
// If the caller executes a transaction that contains only a call to this function, then the transaction does not
// require a fee, unless the caller wishes to provide an additional fee. Transactions that contain multiple transitions
// (that include one or more calls to this function) will require a fee as per standard consensus rules.
function upgrade:
    // Input the record.
    input r0 as credits.record;

    // Check if the record amount is at most 500,000 credits.
    lte r0.microcredits 1_000_000_000_000u64 into r1;
    // Ensure that the record amount is at most 500,000 credits.
    assert.eq r1 true;

    // Construct a record for the specified receiver.
    cast r0.owner r0.microcredits into r2 as credits.record;

    // Decrement the private pool of records before the inclusion upgrade.
    async upgrade r0.microcredits into r3;
    // Output the upgraded record.
    output r2 as credits.record;
    // Output the finalize future.
    output r3 as credits.aleo/upgrade.future;

finalize upgrade:
    // Input the record amount.
    input r0 as u64.public;

    // Get the remaining `credits.record` pool size.
    get.or_use pool[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] 0u64 into r1;
    // Increment the pool by the record amount.
    add r1 r0 into r2;
    // Determine if the total number of migrated credits is less than or equal to 4_000_000 credits.
    lte r2 4_000_000_000_000u64 into r3;
    // Enforce that the total number of migrated credits is less than or equal to 4_000_000 credits.
    assert.eq r3 true;

    // Set the new pool size.
    set r2 into pool[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc];

/**********************************************************************************************************************/