Skip to content

Commit b5d47a9

Browse files
Merge pull request #188 from XRPLF/xrpl/feat/xls-66d
feat(xrpl): add xls-66d support
2 parents 5eb88b6 + 6b322f6 commit b5d47a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4304
-48
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
#### xrpl
1313

1414
- `AuthorizeChannel` to authorize a payment channel.
15+
- Added `Loan` and `LoanBroker` ledger entry types for the lending protocol.
16+
- Added loan transaction types:
17+
- `LoanSet` - Creates or updates a loan with terms including principal, interest rates, payment intervals, and fees.
18+
- `LoanDelete` - Deletes an existing loan.
19+
- `LoanManage` - Modifies loan state (default, impair, unimpair).
20+
- `LoanPay` - Submits a payment on a loan.
21+
- Added loan broker transaction types:
22+
- `LoanBrokerSet` - Creates or updates a loan broker with management fee rates, cover rates, and debt limits.
23+
- `LoanBrokerDelete` - Deletes a loan broker.
24+
- `LoanBrokerCoverDeposit` - Deposits first-loss capital into a loan broker.
25+
- `LoanBrokerCoverWithdraw` - Withdraws first-loss capital from a loan broker.
26+
- `LoanBrokerCoverClawback` - Claws back first-loss capital from a loan broker.
27+
- Added supporting types for loan transactions:
28+
- `XRPLNumber` - Represents XRPL numbers as strings.
29+
- `OwnerCount`, `CoverRate`, `InterestRate`, `PreviousPaymentDate` - Wrapper types for uint32 values.
30+
- `Data`, `GracePeriod`, `PaymentInterval`, `PaymentTotal`, `LoanBrokerID` - Additional wrapper types for loan-related fields.
1531

1632
### Fixed
1733

pkg/typecheck/typecheck.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package typecheck
44
import (
55
"regexp"
66
"strconv"
7+
"strings"
78
)
89

910
// IsUint8 checks if the given interface is a uint8.
@@ -72,3 +73,17 @@ func IsStringNumericUint(s string, base, bitSize int) bool {
7273
_, err := strconv.ParseUint(s, base, bitSize)
7374
return err == nil
7475
}
76+
77+
// xrplNumberPattern matches optional sign, digits, optional decimal, optional exponent (scientific).
78+
// Allows leading zeros; rejects empty string, lone sign, or missing digits.
79+
var xrplNumberPattern = regexp.MustCompile(`^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?$`)
80+
81+
// IsXRPLNumber checks if the value is a valid XRPL number string.
82+
// XRPL numbers are strings that represent numbers, including scientific notation.
83+
func IsXRPLNumber(value interface{}) bool {
84+
str, ok := value.(string)
85+
if !ok {
86+
return false
87+
}
88+
return xrplNumberPattern.MatchString(strings.TrimSpace(str))
89+
}

pkg/typecheck/typecheck_test.go

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,298 @@ func TestIsStringNumericUint(t *testing.T) {
500500
})
501501
}
502502
}
503+
504+
func TestIsXRPLNumber(t *testing.T) {
505+
tests := []struct {
506+
name string
507+
value interface{}
508+
want bool
509+
}{
510+
// Valid cases - integers
511+
{
512+
name: "pass - Valid positive integer",
513+
value: "123",
514+
want: true,
515+
},
516+
{
517+
name: "pass - Valid negative integer",
518+
value: "-456",
519+
want: true,
520+
},
521+
{
522+
name: "pass - Valid integer with plus sign",
523+
value: "+789",
524+
want: true,
525+
},
526+
{
527+
name: "pass - Valid zero",
528+
value: "0",
529+
want: true,
530+
},
531+
{
532+
name: "pass - Valid integer with leading zeros",
533+
value: "007",
534+
want: true,
535+
},
536+
// Valid cases - decimals
537+
{
538+
name: "pass - Valid positive decimal",
539+
value: "123.456",
540+
want: true,
541+
},
542+
{
543+
name: "pass - Valid negative decimal",
544+
value: "-987.654",
545+
want: true,
546+
},
547+
{
548+
name: "pass - Valid decimal with plus sign",
549+
value: "+3.14",
550+
want: true,
551+
},
552+
{
553+
name: "pass - Valid decimal with trailing dot",
554+
value: "123.",
555+
want: true,
556+
},
557+
{
558+
name: "pass - Valid decimal starting with dot",
559+
value: ".5",
560+
want: true,
561+
},
562+
{
563+
name: "pass - Valid negative decimal starting with dot",
564+
value: "-.5",
565+
want: true,
566+
},
567+
{
568+
name: "pass - Valid positive decimal starting with dot",
569+
value: "+.5",
570+
want: true,
571+
},
572+
{
573+
name: "pass - Valid zero decimal",
574+
value: "0.0",
575+
want: true,
576+
},
577+
// Valid cases - scientific notation
578+
{
579+
name: "pass - Valid positive scientific notation lowercase e",
580+
value: "+3.14e10",
581+
want: true,
582+
},
583+
{
584+
name: "pass - Valid negative scientific notation lowercase e",
585+
value: "-7.2e-9",
586+
want: true,
587+
},
588+
{
589+
name: "pass - Valid scientific notation uppercase E",
590+
value: "123E5",
591+
want: true,
592+
},
593+
{
594+
name: "pass - Valid scientific notation with positive exponent",
595+
value: "123e+5",
596+
want: true,
597+
},
598+
{
599+
name: "pass - Valid scientific notation with negative exponent",
600+
value: "123e-5",
601+
want: true,
602+
},
603+
{
604+
name: "pass - Valid scientific notation integer base",
605+
value: "123e10",
606+
want: true,
607+
},
608+
{
609+
name: "pass - Valid scientific notation decimal base",
610+
value: "1.5e10",
611+
want: true,
612+
},
613+
{
614+
name: "pass - Valid scientific notation starting with dot",
615+
value: ".5e10",
616+
want: true,
617+
},
618+
{
619+
name: "pass - Valid scientific notation with trailing dot",
620+
value: "123.e10",
621+
want: true,
622+
},
623+
// Valid cases - whitespace handling (should be trimmed)
624+
{
625+
name: "pass - Valid number with leading whitespace",
626+
value: " 123",
627+
want: true,
628+
},
629+
{
630+
name: "pass - Valid number with trailing whitespace",
631+
value: "123 ",
632+
want: true,
633+
},
634+
{
635+
name: "pass - Valid number with both leading and trailing whitespace",
636+
value: " 123.456 ",
637+
want: true,
638+
},
639+
{
640+
name: "pass - Valid number with tab whitespace",
641+
value: "\t123\t",
642+
want: true,
643+
},
644+
{
645+
name: "pass - Valid number with newline whitespace",
646+
value: "\n123\n",
647+
want: true,
648+
},
649+
// Invalid cases - non-string types
650+
{
651+
name: "fail - Non-string type (int)",
652+
value: 123,
653+
want: false,
654+
},
655+
{
656+
name: "fail - Non-string type (float64)",
657+
value: 123.456,
658+
want: false,
659+
},
660+
{
661+
name: "fail - Non-string type (bool)",
662+
value: true,
663+
want: false,
664+
},
665+
{
666+
name: "fail - Non-string type (nil)",
667+
value: nil,
668+
want: false,
669+
},
670+
// Invalid cases - empty strings
671+
{
672+
name: "fail - Empty string",
673+
value: "",
674+
want: false,
675+
},
676+
{
677+
name: "fail - Whitespace only string",
678+
value: " ",
679+
want: false,
680+
},
681+
{
682+
name: "fail - Tab only string",
683+
value: "\t",
684+
want: false,
685+
},
686+
{
687+
name: "fail - Newline only string",
688+
value: "\n",
689+
want: false,
690+
},
691+
// Invalid cases - just signs
692+
{
693+
name: "fail - Just plus sign",
694+
value: "+",
695+
want: false,
696+
},
697+
{
698+
name: "fail - Just minus sign",
699+
value: "-",
700+
want: false,
701+
},
702+
{
703+
name: "fail - Just plus sign with whitespace",
704+
value: " + ",
705+
want: false,
706+
},
707+
// Invalid cases - invalid formats
708+
{
709+
name: "fail - Multiple decimal points",
710+
value: "123.45.67",
711+
want: false,
712+
},
713+
{
714+
name: "fail - Non-numeric characters",
715+
value: "123abc",
716+
want: false,
717+
},
718+
{
719+
name: "fail - Non-numeric characters in decimal",
720+
value: "123.45abc",
721+
want: false,
722+
},
723+
{
724+
name: "fail - Invalid exponent format (missing digits)",
725+
value: "123e",
726+
want: false,
727+
},
728+
{
729+
name: "fail - Invalid exponent format (just e)",
730+
value: "e10",
731+
want: false,
732+
},
733+
{
734+
name: "fail - Invalid exponent format (double e)",
735+
value: "123ee10",
736+
want: false,
737+
},
738+
{
739+
name: "fail - Invalid exponent format (missing exponent digits)",
740+
value: "123e+",
741+
want: false,
742+
},
743+
{
744+
name: "fail - Invalid exponent format (missing exponent digits negative)",
745+
value: "123e-",
746+
want: false,
747+
},
748+
{
749+
name: "fail - Spaces in middle of number",
750+
value: "12 3",
751+
want: false,
752+
},
753+
{
754+
name: "fail - Spaces in middle of decimal",
755+
value: "12. 3",
756+
want: false,
757+
},
758+
{
759+
name: "fail - Spaces in middle of exponent",
760+
value: "123e 10",
761+
want: false,
762+
},
763+
{
764+
name: "fail - Special characters",
765+
value: "123!",
766+
want: false,
767+
},
768+
{
769+
name: "fail - Special characters in decimal",
770+
value: "123.45@",
771+
want: false,
772+
},
773+
{
774+
name: "fail - Invalid decimal format (just dot)",
775+
value: ".",
776+
want: false,
777+
},
778+
{
779+
name: "fail - Invalid decimal format (dot with sign only)",
780+
value: "+.",
781+
want: false,
782+
},
783+
{
784+
name: "fail - Invalid decimal format (dot with sign only negative)",
785+
value: "-.",
786+
want: false,
787+
},
788+
}
789+
790+
for _, tt := range tests {
791+
t.Run(tt.name, func(t *testing.T) {
792+
if got := IsXRPLNumber(tt.value); got != tt.want {
793+
t.Errorf("IsXRPLNumber(%v) = %v, want %v", tt.value, got, tt.want)
794+
}
795+
})
796+
}
797+
}

0 commit comments

Comments
 (0)