From 8a7124da6328d188df7b87da176281255bb137d0 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 4 Feb 2025 22:41:18 +0100 Subject: [PATCH 1/7] feat(xrpl): add ledger entry request --- xrpl/queries/ledger/ledger_entry.go | 56 +++++++++++++++++ xrpl/queries/ledger/ledger_entry_test.go | 78 ++++++++++++++++++++++++ xrpl/queries/ledger/types/entry.go | 72 ++++++++++++++++++++++ xrpl/transaction/types/xchain_bridge.go | 2 +- 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 xrpl/queries/ledger/ledger_entry.go create mode 100644 xrpl/queries/ledger/ledger_entry_test.go create mode 100644 xrpl/queries/ledger/types/entry.go diff --git a/xrpl/queries/ledger/ledger_entry.go b/xrpl/queries/ledger/ledger_entry.go new file mode 100644 index 00000000..7baeaced --- /dev/null +++ b/xrpl/queries/ledger/ledger_entry.go @@ -0,0 +1,56 @@ +package ledger + +import ( + "github.com/Peersyst/xrpl-go/xrpl/queries/common" + "github.com/Peersyst/xrpl-go/xrpl/queries/ledger/types" + "github.com/Peersyst/xrpl-go/xrpl/queries/version" +) + + + +type EntryRequest struct { + common.BaseRequest + MPTIssuance bool `json:"mp_issuance,omitempty"` + MPToken interface{}`json:"mp_token,omitempty"` + AMM types.EntryAssetPair `json:"amm,omitempty"` + IncludeDeleted bool `json:"include_deleted,omitempty"` + Binary bool `json:"binary,omitempty"` + Index string `json:"index,omitempty"` + AccountRoot string `json:"account_root,omitempty"` + Check string `json:"check,omitempty"` + Credential interface{} `json:"credential,omitempty"` + DepositPreauth interface{} `json:"deposit_preauth,omitempty"` + Did string `json:"did,omitempty"` + Directory interface{} `json:"directory,omitempty"` + Escrow interface{} `json:"escrow,omitempty"` + Offer interface{} `json:"offer,omitempty"` + PaymentChannel string `json:"payment_channel,omitempty"` + RippleState types.EntryRippleState `json:"ripple_state,omitempty"` + Ticket interface{} `json:"ticket,omitempty"` + NFTPage string `json:"nft_page,omitempty"` + BridgeAccount string `json:"bridge_account,omitempty"` + Bridge types.EntryXChainBridge `json:"bridge,omitempty"` + XChainOwnedClaimID interface{} `json:"xchain_owned_claim_id,omitempty"` + XChainOwnedCreateAccountClaimID interface{} `json:"xchain_owned_create_account_claim_id,omitempty"` +} + +func (e *EntryRequest) Method() string { + return "ledger_entry" +} + +func (e *EntryRequest) APIVersion() int { + return version.RippledAPIV2 +} + +func (e *EntryRequest) Validate() error { + return nil +} + +type EntryResponse struct { + Index string `json:"index"` + LedgerCurrentIndex common.LedgerIndex `json:"ledger_current_index"` + Node interface{} `json:"node,omitempty"` + NodeBinary string `json:"node_binary,omitempty"` + Validated bool `json:"validated,omitempty"` + DeletedLedgerIndex common.LedgerIndex `json:"deleted_ledger_index,omitempty"` +} \ No newline at end of file diff --git a/xrpl/queries/ledger/ledger_entry_test.go b/xrpl/queries/ledger/ledger_entry_test.go new file mode 100644 index 00000000..3662ebd9 --- /dev/null +++ b/xrpl/queries/ledger/ledger_entry_test.go @@ -0,0 +1,78 @@ +package ledger + +import ( + "testing" + + "github.com/Peersyst/xrpl-go/xrpl/ledger-entry-types" + types "github.com/Peersyst/xrpl-go/xrpl/queries/ledger/types" + "github.com/Peersyst/xrpl-go/xrpl/testutil" +) + +func TestLedgerEntryRequest(t *testing.T) { + s := EntryRequest{ + MPTIssuance: true, + Binary: true, + Index: "some_index", + AccountRoot: "some_account", + Check: "some_check", + Did: "some_did", + PaymentChannel: "some_channel", + NFTPage: "some_nft_page", + BridgeAccount: "some_bridge_account", + Bridge: types.EntryXChainBridge{ + LockingChainDoor: "door", + LockingChainIssue: "issue", + IssuingChainDoor: "door2", + IssuingChainIssue: "issue2", + }, + IncludeDeleted: true, + AMM: types.EntryAssetPair{ + Asset: ledger.Asset{ + Currency: "XRP", + }, + Asset2: ledger.Asset{ + Currency: "USD", + }, + }, + RippleState: types.EntryRippleState{ + Accounts: []string{"acc1", "acc2"}, + Currency: "XRP", + }, + } + j := `{ + "mp_issuance": true, + "amm": { + "asset": { + "currency": "XRP" + }, + "asset2": { + "currency": "USD" + } + }, + "include_deleted": true, + "binary": true, + "index": "some_index", + "account_root": "some_account", + "check": "some_check", + "did": "some_did", + "payment_channel": "some_channel", + "ripple_state": { + "accounts": [ + "acc1", + "acc2" + ], + "currency": "XRP" + }, + "nft_page": "some_nft_page", + "bridge_account": "some_bridge_account", + "bridge": { + "locking_chain_door": "door", + "locking_chain_issue": "issue", + "issuing_chain_door": "door2", + "issuing_chain_issue": "issue2" + } +}` + if err := testutil.Serialize(t, s, j); err != nil { + t.Error(err) + } +} \ No newline at end of file diff --git a/xrpl/queries/ledger/types/entry.go b/xrpl/queries/ledger/types/entry.go new file mode 100644 index 00000000..172ce941 --- /dev/null +++ b/xrpl/queries/ledger/types/entry.go @@ -0,0 +1,72 @@ +package types + +import ( + "github.com/Peersyst/xrpl-go/xrpl/ledger-entry-types" +) + +type EntryAssetPair struct { + Asset ledger.Asset `json:"asset"` + Asset2 ledger.Asset `json:"asset2"` +} + +type EntryMPToken struct { + MPTIssuanceID string `json:"mp_issuance_id"` + Account string `json:"account"` +} + +type EntryCredential struct { + Subject string `json:"subject"` + Issuer string `json:"issuer"` + CredentialType string `json:"credential_type"` +} + +type EntryDepositPreauth struct { + Owner string `json:"owner"` + Authorized string `json:"authorized"` +} + +type EntryDirectory struct { + // TODO: CHECK TYPE + SubIndex uint32 `json:"sub_index,omitempty"` + DirRoot string `json:"dir_root,omitempty"` + Owner string `json:"owner,omitempty"` +} + +type EntryEscrow struct { + Owner string `json:"owner"` + Seq string `json:"seq"` + +} + +type EntryOffer struct { + Account string `json:"account"` + Seq string `json:"seq"` +} + +type EntryRippleState struct { + Accounts []string `json:"accounts"` + Currency string `json:"currency"` +} + +type EntryTicket struct { + Owner string `json:"owner"` + TicketSequence string `json:"ticket_sequence"` +} + +type EntryXChainBridge struct { + LockingChainDoor string `json:"locking_chain_door"` + LockingChainIssue string `json:"locking_chain_issue"` + IssuingChainDoor string `json:"issuing_chain_door"` + IssuingChainIssue string `json:"issuing_chain_issue"` +} + +type EntryXChainOwnedClaimID struct { + EntryXChainBridge + XChainOwnedClaimID interface{} `json:"xchain_owned_claim_id"` +} + +type EntryXChainOwnedCreateAccountClaimID struct { + EntryXChainBridge + XChainOwnedCreateAccountClaimID interface{} `json:"xchain_owned_create_account_claim_id"` +} + diff --git a/xrpl/transaction/types/xchain_bridge.go b/xrpl/transaction/types/xchain_bridge.go index adca59e0..2c385437 100644 --- a/xrpl/transaction/types/xchain_bridge.go +++ b/xrpl/transaction/types/xchain_bridge.go @@ -16,7 +16,7 @@ var ( type XChainBridge struct { // The door account on the issuing chain. For an XRP-XRP bridge, this must be the // genesis account (the account that is created when the network is first started, which contains all of the XRP). - IssuingChainDoor Address + IssuingChainDoor Address // The asset that is minted and burned on the issuing chain. For an IOU-IOU bridge, // the issuer of the asset must be the door account on the issuing chain, to avoid supply issues. IssuingChainIssue Address From 4735c78dde6c8beaeb8811bb5b909bf1ea2d478c Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 4 Feb 2025 22:42:10 +0100 Subject: [PATCH 2/7] chore: update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c0e4a3..b4872cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +#### xrpl + +- Added `LedgerEntry` query. + +## [v0.1.3] + ### Added - Added `APIVersion` field to the `Client` struct. From bbf312dd052e4f825c3b004c280e42ab291995fd Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 4 Feb 2025 22:42:21 +0100 Subject: [PATCH 3/7] fix(xrpl): linting --- xrpl/queries/ledger/ledger_entry.go | 56 ++++++++++++------------ xrpl/queries/ledger/ledger_entry_test.go | 22 +++++----- xrpl/queries/ledger/types/entry.go | 26 +++++------ xrpl/transaction/types/xchain_bridge.go | 2 +- 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/xrpl/queries/ledger/ledger_entry.go b/xrpl/queries/ledger/ledger_entry.go index 7baeaced..c8b44696 100644 --- a/xrpl/queries/ledger/ledger_entry.go +++ b/xrpl/queries/ledger/ledger_entry.go @@ -6,32 +6,30 @@ import ( "github.com/Peersyst/xrpl-go/xrpl/queries/version" ) - - type EntryRequest struct { common.BaseRequest - MPTIssuance bool `json:"mp_issuance,omitempty"` - MPToken interface{}`json:"mp_token,omitempty"` - AMM types.EntryAssetPair `json:"amm,omitempty"` - IncludeDeleted bool `json:"include_deleted,omitempty"` - Binary bool `json:"binary,omitempty"` - Index string `json:"index,omitempty"` - AccountRoot string `json:"account_root,omitempty"` - Check string `json:"check,omitempty"` - Credential interface{} `json:"credential,omitempty"` - DepositPreauth interface{} `json:"deposit_preauth,omitempty"` - Did string `json:"did,omitempty"` - Directory interface{} `json:"directory,omitempty"` - Escrow interface{} `json:"escrow,omitempty"` - Offer interface{} `json:"offer,omitempty"` - PaymentChannel string `json:"payment_channel,omitempty"` - RippleState types.EntryRippleState `json:"ripple_state,omitempty"` - Ticket interface{} `json:"ticket,omitempty"` - NFTPage string `json:"nft_page,omitempty"` - BridgeAccount string `json:"bridge_account,omitempty"` - Bridge types.EntryXChainBridge `json:"bridge,omitempty"` - XChainOwnedClaimID interface{} `json:"xchain_owned_claim_id,omitempty"` - XChainOwnedCreateAccountClaimID interface{} `json:"xchain_owned_create_account_claim_id,omitempty"` + MPTIssuance bool `json:"mp_issuance,omitempty"` + MPToken interface{} `json:"mp_token,omitempty"` + AMM types.EntryAssetPair `json:"amm,omitempty"` + IncludeDeleted bool `json:"include_deleted,omitempty"` + Binary bool `json:"binary,omitempty"` + Index string `json:"index,omitempty"` + AccountRoot string `json:"account_root,omitempty"` + Check string `json:"check,omitempty"` + Credential interface{} `json:"credential,omitempty"` + DepositPreauth interface{} `json:"deposit_preauth,omitempty"` + Did string `json:"did,omitempty"` + Directory interface{} `json:"directory,omitempty"` + Escrow interface{} `json:"escrow,omitempty"` + Offer interface{} `json:"offer,omitempty"` + PaymentChannel string `json:"payment_channel,omitempty"` + RippleState types.EntryRippleState `json:"ripple_state,omitempty"` + Ticket interface{} `json:"ticket,omitempty"` + NFTPage string `json:"nft_page,omitempty"` + BridgeAccount string `json:"bridge_account,omitempty"` + Bridge types.EntryXChainBridge `json:"bridge,omitempty"` + XChainOwnedClaimID interface{} `json:"xchain_owned_claim_id,omitempty"` + XChainOwnedCreateAccountClaimID interface{} `json:"xchain_owned_create_account_claim_id,omitempty"` } func (e *EntryRequest) Method() string { @@ -47,10 +45,10 @@ func (e *EntryRequest) Validate() error { } type EntryResponse struct { - Index string `json:"index"` + Index string `json:"index"` LedgerCurrentIndex common.LedgerIndex `json:"ledger_current_index"` - Node interface{} `json:"node,omitempty"` - NodeBinary string `json:"node_binary,omitempty"` - Validated bool `json:"validated,omitempty"` + Node interface{} `json:"node,omitempty"` + NodeBinary string `json:"node_binary,omitempty"` + Validated bool `json:"validated,omitempty"` DeletedLedgerIndex common.LedgerIndex `json:"deleted_ledger_index,omitempty"` -} \ No newline at end of file +} diff --git a/xrpl/queries/ledger/ledger_entry_test.go b/xrpl/queries/ledger/ledger_entry_test.go index 3662ebd9..7fdfcc7a 100644 --- a/xrpl/queries/ledger/ledger_entry_test.go +++ b/xrpl/queries/ledger/ledger_entry_test.go @@ -10,19 +10,19 @@ import ( func TestLedgerEntryRequest(t *testing.T) { s := EntryRequest{ - MPTIssuance: true, - Binary: true, - Index: "some_index", - AccountRoot: "some_account", - Check: "some_check", - Did: "some_did", + MPTIssuance: true, + Binary: true, + Index: "some_index", + AccountRoot: "some_account", + Check: "some_check", + Did: "some_did", PaymentChannel: "some_channel", - NFTPage: "some_nft_page", - BridgeAccount: "some_bridge_account", + NFTPage: "some_nft_page", + BridgeAccount: "some_bridge_account", Bridge: types.EntryXChainBridge{ - LockingChainDoor: "door", + LockingChainDoor: "door", LockingChainIssue: "issue", - IssuingChainDoor: "door2", + IssuingChainDoor: "door2", IssuingChainIssue: "issue2", }, IncludeDeleted: true, @@ -75,4 +75,4 @@ func TestLedgerEntryRequest(t *testing.T) { if err := testutil.Serialize(t, s, j); err != nil { t.Error(err) } -} \ No newline at end of file +} diff --git a/xrpl/queries/ledger/types/entry.go b/xrpl/queries/ledger/types/entry.go index 172ce941..3f8e32f9 100644 --- a/xrpl/queries/ledger/types/entry.go +++ b/xrpl/queries/ledger/types/entry.go @@ -5,8 +5,8 @@ import ( ) type EntryAssetPair struct { - Asset ledger.Asset `json:"asset"` - Asset2 ledger.Asset `json:"asset2"` + Asset ledger.Asset `json:"asset"` + Asset2 ledger.Asset `json:"asset2"` } type EntryMPToken struct { @@ -15,13 +15,13 @@ type EntryMPToken struct { } type EntryCredential struct { - Subject string `json:"subject"` - Issuer string `json:"issuer"` + Subject string `json:"subject"` + Issuer string `json:"issuer"` CredentialType string `json:"credential_type"` } type EntryDepositPreauth struct { - Owner string `json:"owner"` + Owner string `json:"owner"` Authorized string `json:"authorized"` } @@ -29,34 +29,33 @@ type EntryDirectory struct { // TODO: CHECK TYPE SubIndex uint32 `json:"sub_index,omitempty"` DirRoot string `json:"dir_root,omitempty"` - Owner string `json:"owner,omitempty"` + Owner string `json:"owner,omitempty"` } type EntryEscrow struct { Owner string `json:"owner"` - Seq string `json:"seq"` - + Seq string `json:"seq"` } type EntryOffer struct { Account string `json:"account"` - Seq string `json:"seq"` + Seq string `json:"seq"` } type EntryRippleState struct { Accounts []string `json:"accounts"` - Currency string `json:"currency"` + Currency string `json:"currency"` } type EntryTicket struct { - Owner string `json:"owner"` + Owner string `json:"owner"` TicketSequence string `json:"ticket_sequence"` } type EntryXChainBridge struct { - LockingChainDoor string `json:"locking_chain_door"` + LockingChainDoor string `json:"locking_chain_door"` LockingChainIssue string `json:"locking_chain_issue"` - IssuingChainDoor string `json:"issuing_chain_door"` + IssuingChainDoor string `json:"issuing_chain_door"` IssuingChainIssue string `json:"issuing_chain_issue"` } @@ -69,4 +68,3 @@ type EntryXChainOwnedCreateAccountClaimID struct { EntryXChainBridge XChainOwnedCreateAccountClaimID interface{} `json:"xchain_owned_create_account_claim_id"` } - diff --git a/xrpl/transaction/types/xchain_bridge.go b/xrpl/transaction/types/xchain_bridge.go index 2c385437..adca59e0 100644 --- a/xrpl/transaction/types/xchain_bridge.go +++ b/xrpl/transaction/types/xchain_bridge.go @@ -16,7 +16,7 @@ var ( type XChainBridge struct { // The door account on the issuing chain. For an XRP-XRP bridge, this must be the // genesis account (the account that is created when the network is first started, which contains all of the XRP). - IssuingChainDoor Address + IssuingChainDoor Address // The asset that is minted and burned on the issuing chain. For an IOU-IOU bridge, // the issuer of the asset must be the door account on the issuing chain, to avoid supply issues. IssuingChainIssue Address From 20c17a2be65b39c530ce45a60846e5c7367dc07c Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 5 Feb 2025 08:32:46 +0100 Subject: [PATCH 4/7] fix(xrpl): comments --- xrpl/queries/ledger/ledger_entry.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xrpl/queries/ledger/ledger_entry.go b/xrpl/queries/ledger/ledger_entry.go index c8b44696..5bc23efa 100644 --- a/xrpl/queries/ledger/ledger_entry.go +++ b/xrpl/queries/ledger/ledger_entry.go @@ -6,6 +6,12 @@ import ( "github.com/Peersyst/xrpl-go/xrpl/queries/version" ) +// ############################################################################ +// Request +// ############################################################################ + +// The `ledger_entry` method returns a single ledger object from the XRP Ledger +// in its raw format. Expects a response in the form of a EntryResponse. type EntryRequest struct { common.BaseRequest MPTIssuance bool `json:"mp_issuance,omitempty"` @@ -44,6 +50,11 @@ func (e *EntryRequest) Validate() error { return nil } +// ############################################################################ +// Response +// ############################################################################ + +// The expected response from the ledger_entry method. type EntryResponse struct { Index string `json:"index"` LedgerCurrentIndex common.LedgerIndex `json:"ledger_current_index"` From 5f68e4667cee11667200965a7e251f361f6ec61e Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 5 Feb 2025 09:50:26 +0100 Subject: [PATCH 5/7] fix(xrpl): ledger `EntryRequest` json fields --- xrpl/queries/ledger/ledger_entry.go | 4 ++-- xrpl/queries/ledger/types/entry.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/xrpl/queries/ledger/ledger_entry.go b/xrpl/queries/ledger/ledger_entry.go index 5bc23efa..a065c808 100644 --- a/xrpl/queries/ledger/ledger_entry.go +++ b/xrpl/queries/ledger/ledger_entry.go @@ -14,8 +14,8 @@ import ( // in its raw format. Expects a response in the form of a EntryResponse. type EntryRequest struct { common.BaseRequest - MPTIssuance bool `json:"mp_issuance,omitempty"` - MPToken interface{} `json:"mp_token,omitempty"` + MPTIssuance bool `json:"mpt_issuance,omitempty"` + MPToken interface{} `json:"mptoken,omitempty"` AMM types.EntryAssetPair `json:"amm,omitempty"` IncludeDeleted bool `json:"include_deleted,omitempty"` Binary bool `json:"binary,omitempty"` diff --git a/xrpl/queries/ledger/types/entry.go b/xrpl/queries/ledger/types/entry.go index 3f8e32f9..d96344c0 100644 --- a/xrpl/queries/ledger/types/entry.go +++ b/xrpl/queries/ledger/types/entry.go @@ -26,7 +26,6 @@ type EntryDepositPreauth struct { } type EntryDirectory struct { - // TODO: CHECK TYPE SubIndex uint32 `json:"sub_index,omitempty"` DirRoot string `json:"dir_root,omitempty"` Owner string `json:"owner,omitempty"` From 89326c429a7a355aa0cb094b0c978991d536a548 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 5 Feb 2025 09:55:48 +0100 Subject: [PATCH 6/7] fix(xrpl): ledger entry test --- xrpl/queries/ledger/ledger_entry_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xrpl/queries/ledger/ledger_entry_test.go b/xrpl/queries/ledger/ledger_entry_test.go index 7fdfcc7a..868e9199 100644 --- a/xrpl/queries/ledger/ledger_entry_test.go +++ b/xrpl/queries/ledger/ledger_entry_test.go @@ -40,7 +40,7 @@ func TestLedgerEntryRequest(t *testing.T) { }, } j := `{ - "mp_issuance": true, + "mpt_issuance": true, "amm": { "asset": { "currency": "XRP" From 2c510c6d732ef824e06e9239ab040979d202dbd4 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 5 Feb 2025 15:37:31 +0100 Subject: [PATCH 7/7] fix(xrpl): add `GetLedgerEntry` client helpers --- xrpl/rpc/queries.go | 17 +++++++ xrpl/rpc/queries_test.go | 71 ++++++++++++++++++++++++++ xrpl/websocket/queries.go | 16 ++++++ xrpl/websocket/queries_test.go | 91 ++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) diff --git a/xrpl/rpc/queries.go b/xrpl/rpc/queries.go index 20162b9e..417f45b4 100644 --- a/xrpl/rpc/queries.go +++ b/xrpl/rpc/queries.go @@ -196,6 +196,23 @@ func (c *Client) GetLedgerIndex() (common.LedgerIndex, error) { return lr.LedgerIndex, err } +// GetLedgerEntry retrieves information about a specific ledger entry. +// It takes a LedgerEntryRequest as input and returns a LedgerEntryResponse, +// along with any error encountered. +func (c *Client) GetLedgerEntry(req *ledger.EntryRequest) (*ledger.EntryResponse, error) { + res, err := c.Request(req) + if err != nil { + return nil, err + } + + var lr ledger.EntryResponse + err = res.GetResult(&lr) + if err != nil { + return nil, err + } + return &lr, nil +} + // GetClosedLedger retrieves information about the last closed ledger. // It returns a ClosedResponse containing the ledger information and any error encountered. func (c *Client) GetClosedLedger() (*ledger.ClosedResponse, error) { diff --git a/xrpl/rpc/queries_test.go b/xrpl/rpc/queries_test.go index b000a6ab..c2142be7 100644 --- a/xrpl/rpc/queries_test.go +++ b/xrpl/rpc/queries_test.go @@ -996,6 +996,77 @@ func TestClient_GetLedgerIndex(t *testing.T) { } } +func TestClient_GetLedgerEntry(t *testing.T) { + tests := []struct { + name string + mockResponse string + mockStatus int + request *ledgerqueries.EntryRequest + expected *ledgerqueries.EntryResponse + expectedError string + }{ + { + name: "successful response", + mockResponse: `{ + "result": { + "index": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + "ledger_current_index": 61809073, + "node_binary": "test", + "validated": true, + "deleted_ledger_index": 0 + } + }`, + mockStatus: 200, + request: &ledgerqueries.EntryRequest{ + Index: "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + }, + expected: &ledgerqueries.EntryResponse{ + Index: "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + LedgerCurrentIndex: 61809073, + NodeBinary: "test", + Validated: true, + DeletedLedgerIndex: 0, + }, + }, + { + name: "error response", + mockResponse: `{ + "result": { + "error": "entryNotFound", + "status": "error" + } + }`, + mockStatus: 200, + request: &ledgerqueries.EntryRequest{}, + expectedError: "entryNotFound", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mc := testutil.JSONRPCMockClient{} + mc.DoFunc = testutil.MockResponse(tt.mockResponse, tt.mockStatus, &mc) + + cfg, err := NewClientConfig("http://testnode/", WithHTTPClient(&mc)) + require.NoError(t, err) + + client := NewClient(cfg) + + resp, err := client.GetLedgerEntry(tt.request) + + if tt.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedError) + return + } + + require.NoError(t, err) + require.Equal(t, tt.expected, resp) + }) + } +} + + func TestClient_GetClosedLedger(t *testing.T) { tests := []struct { name string diff --git a/xrpl/websocket/queries.go b/xrpl/websocket/queries.go index bf29f22b..02e0bdac 100644 --- a/xrpl/websocket/queries.go +++ b/xrpl/websocket/queries.go @@ -200,6 +200,22 @@ func (c *Client) GetLedgerIndex() (common.LedgerIndex, error) { return lr.LedgerIndex, err } +// GetLedgerEntry retrieves information about a specific ledger entry. +// It takes a LedgerEntryRequest as input and returns a LedgerEntryResponse, +// along with any error encountered. +func (c *Client) GetLedgerEntry(req *ledger.EntryRequest) (*ledger.EntryResponse, error) { + res, err := c.Request(req) + if err != nil { + return nil, err + } + var lr ledger.EntryResponse + err = res.GetResult(&lr) + if err != nil { + return nil, err + } + return &lr, nil +} + // GetClosedLedger retrieves information about the last closed ledger. // It returns a ClosedResponse containing the ledger information and any error encountered. func (c *Client) GetClosedLedger() (*ledger.ClosedResponse, error) { diff --git a/xrpl/websocket/queries_test.go b/xrpl/websocket/queries_test.go index 2b30ff09..505e6007 100644 --- a/xrpl/websocket/queries_test.go +++ b/xrpl/websocket/queries_test.go @@ -658,6 +658,97 @@ func TestClient_GetLedgerIndex(t *testing.T) { } } +func TestClient_GetLedgerEntry(t *testing.T) { + tests := []struct { + name string + serverMessages []map[string]any + request *ledgerqueries.EntryRequest + expected *ledgerqueries.EntryResponse + expectedErr error + }{ + { + name: "Valid ledger entry", + serverMessages: []map[string]any{ + { + "id": 1, + "result": map[string]any{ + "index": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + "ledger_current_index": uint32(61809073), + "node_binary": "test", + "validated": true, + "deleted_ledger_index": uint32(0), + }, + }, + }, + request: &ledgerqueries.EntryRequest{ + Index: "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + }, + expected: &ledgerqueries.EntryResponse{ + Index: "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + LedgerCurrentIndex: 61809073, + NodeBinary: "test", + Validated: true, + DeletedLedgerIndex: 0, + }, + expectedErr: nil, + }, + { + name: "Error response", + serverMessages: []map[string]any{{"error": "Entry not found"}}, + request: &ledgerqueries.EntryRequest{ + Index: "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", + }, + expected: nil, + expectedErr: errors.New("incorrect id"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ws := &testutil.MockWebSocketServer{Msgs: tt.serverMessages} + s := ws.TestWebSocketServer(func(c *websocket.Conn) { + for _, m := range tt.serverMessages { + err := c.WriteJSON(m) + if err != nil { + t.Errorf("error writing message: %v", err) + } + } + }) + defer s.Close() + + url, _ := testutil.ConvertHTTPToWS(s.URL) + cl := &Client{ + cfg: ClientConfig{ + host: url, + }, + } + + if err := cl.Connect(); err != nil { + t.Errorf("Error connecting to server: %v", err) + } + + result, err := cl.GetLedgerEntry(tt.request) + + if tt.expectedErr != nil { + if err == nil || err.Error() != tt.expectedErr.Error() { + t.Errorf("Expected error %v, but got %v", tt.expectedErr, err) + } + } else { + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + + if !reflect.DeepEqual(tt.expected, result) { + t.Errorf("Expected %+v, but got %+v", tt.expected, result) + } + + cl.Disconnect() + }) + } +} + + func TestClient_GetAccountNFTs(t *testing.T) { tests := []struct { name string