Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1829d3e
Change operation_xdr column from TEXT to BYTEA
aditya1702 Feb 5, 2026
c844520
Add XDRBytea type for storing XDR data as BYTEA
aditya1702 Feb 5, 2026
b5ac8ad
Update Operation struct to use XDRBytea type
aditya1702 Feb 5, 2026
c23554e
Update ConvertOperation to use XDRBytea type
aditya1702 Feb 5, 2026
23ba111
Update operations.go for BYTEA operation_xdr storage
aditya1702 Feb 5, 2026
a347cc7
Add forceResolver directive to operationXdr field
aditya1702 Feb 5, 2026
ddaeeee
Run gql-generate and fix test_utils.go type
aditya1702 Feb 5, 2026
aee79f2
Implement OperationXdr GraphQL resolver
aditya1702 Feb 5, 2026
00eeaa3
Update tests to use XDRBytea type
aditya1702 Feb 5, 2026
ec34cef
Update GraphQL resolver tests for XDRBytea type
aditya1702 Feb 5, 2026
b58b994
Fix test data to use valid base64-encoded XDR strings
aditya1702 Feb 5, 2026
525a59d
Change XDRBytea underlying type from string to []byte
aditya1702 Feb 5, 2026
5e221f7
Use MarshalBinary directly in ConvertOperation
aditya1702 Feb 5, 2026
ce12f8a
Simplify BatchInsert and BatchCopy in operations.go
aditya1702 Feb 5, 2026
52bdb28
Update utils_test.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
5bb41f3
Update operations_test.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
a5429a0
Update test_utils.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
41dfbf7
Update ingest_test.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
bd27099
Fix operations_test.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
762593e
Fix accounts_test.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
5f26b91
Fix transactions_test.go for XDRBytea []byte type
aditya1702 Feb 5, 2026
25ccec9
Update generated.go
aditya1702 Feb 5, 2026
b84442d
Fix XDRBytea.Scan buffer reuse bug
aditya1702 Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions internal/data/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,9 @@ func TestAccountModelBatchGetByOperationIDs(t *testing.T) {
require.NoError(t, err)

// Insert test operations (IDs don't need to be in TOID range here since we're just testing operations_accounts links)
_, err = m.DB.ExecContext(ctx, "INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at) VALUES ($1, 'PAYMENT', 'xdr1', 'op_success', true, 1, NOW()), ($2, 'PAYMENT', 'xdr2', 'op_success', true, 2, NOW())", operationID1, operationID2)
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
_, err = m.DB.ExecContext(ctx, "INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at) VALUES ($1, 'PAYMENT', $3, 'op_success', true, 1, NOW()), ($2, 'PAYMENT', $4, 'op_success', true, 2, NOW())", operationID1, operationID2, xdr1, xdr2)
require.NoError(t, err)

// Insert test operations_accounts links (account_id is BYTEA)
Expand Down Expand Up @@ -422,7 +424,9 @@ func TestAccountModelBatchGetByStateChangeIDs(t *testing.T) {
require.NoError(t, err)

// Insert test operations (IDs must be in TOID range for each transaction)
_, err = m.DB.ExecContext(ctx, "INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at) VALUES (4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, NOW()), (8193, 'PAYMENT', 'xdr2', 'op_success', true, 2, NOW())")
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
_, err = m.DB.ExecContext(ctx, "INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at) VALUES (4097, 'PAYMENT', $1, 'op_success', true, 1, NOW()), (8193, 'PAYMENT', $2, 'op_success', true, 2, NOW())", xdr1, xdr2)
require.NoError(t, err)

// Insert test state changes that reference the accounts (state_changes.account_id is TEXT)
Expand Down
8 changes: 4 additions & 4 deletions internal/data/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func (m *OperationModel) BatchInsert(
// 1. Flatten the operations into parallel slices
ids := make([]int64, len(operations))
operationTypes := make([]string, len(operations))
operationXDRs := make([]string, len(operations))
operationXDRs := make([][]byte, len(operations))
resultCodes := make([]string, len(operations))
successfulFlags := make([]bool, len(operations))
ledgerNumbers := make([]uint32, len(operations))
Expand All @@ -293,7 +293,7 @@ func (m *OperationModel) BatchInsert(
for i, op := range operations {
ids[i] = op.ID
operationTypes[i] = string(op.OperationType)
operationXDRs[i] = op.OperationXDR
operationXDRs[i] = []byte(op.OperationXDR)
resultCodes[i] = op.ResultCode
successfulFlags[i] = op.Successful
ledgerNumbers[i] = op.LedgerNumber
Expand Down Expand Up @@ -327,7 +327,7 @@ func (m *OperationModel) BatchInsert(
SELECT
UNNEST($1::bigint[]) AS id,
UNNEST($2::text[]) AS operation_type,
UNNEST($3::text[]) AS operation_xdr,
UNNEST($3::bytea[]) AS operation_xdr,
UNNEST($4::text[]) AS result_code,
UNNEST($5::boolean[]) AS successful,
UNNEST($6::bigint[]) AS ledger_number,
Expand Down Expand Up @@ -418,7 +418,7 @@ func (m *OperationModel) BatchCopy(
return []any{
pgtype.Int8{Int64: op.ID, Valid: true},
pgtype.Text{String: string(op.OperationType), Valid: true},
pgtype.Text{String: op.OperationXDR, Valid: true},
[]byte(op.OperationXDR),
pgtype.Text{String: op.ResultCode, Valid: true},
pgtype.Bool{Bool: op.Successful, Valid: true},
pgtype.Int4{Int32: int32(op.LedgerNumber), Valid: true},
Expand Down
90 changes: 55 additions & 35 deletions internal/data/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func generateTestOperations(n int, startID int64) ([]*types.Operation, map[int64
ops[i] = &types.Operation{
ID: opID,
OperationType: types.OperationTypePayment,
OperationXDR: fmt.Sprintf("operation_xdr_%d", i),
OperationXDR: types.XDRBytea([]byte(fmt.Sprintf("operation_xdr_%d", i))),
LedgerNumber: uint32(i + 1),
LedgerCreatedAt: now,
}
Expand Down Expand Up @@ -101,13 +101,13 @@ func Test_OperationModel_BatchInsert(t *testing.T) {
op1 := types.Operation{
ID: 4097, // in range (4096, 8192)
OperationType: types.OperationTypePayment,
OperationXDR: "operation1",
OperationXDR: types.XDRBytea([]byte("operation1")),
LedgerCreatedAt: now,
}
op2 := types.Operation{
ID: 8193, // in range (8192, 12288)
OperationType: types.OperationTypeCreateAccount,
OperationXDR: "operation2",
OperationXDR: types.XDRBytea([]byte("operation2")),
LedgerCreatedAt: now,
}

Expand Down Expand Up @@ -288,13 +288,13 @@ func Test_OperationModel_BatchCopy(t *testing.T) {
op1 := types.Operation{
ID: 4097, // in range (4096, 8192)
OperationType: types.OperationTypePayment,
OperationXDR: "operation1",
OperationXDR: types.XDRBytea([]byte("operation1")),
LedgerCreatedAt: now,
}
op2 := types.Operation{
ID: 8193, // in range (8192, 12288)
OperationType: types.OperationTypeCreateAccount,
OperationXDR: "operation2",
OperationXDR: types.XDRBytea([]byte("operation2")),
LedgerCreatedAt: now,
}

Expand Down Expand Up @@ -432,7 +432,7 @@ func Test_OperationModel_BatchCopy_DuplicateFails(t *testing.T) {
op1 := types.Operation{
ID: 999,
OperationType: types.OperationTypePayment,
OperationXDR: "operation_xdr_dup_test",
OperationXDR: types.XDRBytea([]byte("operation_xdr_dup_test")),
LedgerNumber: 1,
LedgerCreatedAt: now,
}
Expand Down Expand Up @@ -516,13 +516,16 @@ func TestOperationModel_GetAll(t *testing.T) {
require.NoError(t, err)

// Create test operations (IDs must be in TOID range for each transaction: (to_id, to_id + 4096))
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
xdr3 := types.XDRBytea([]byte("xdr3"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(2, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(4098, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1),
(8194, 'PAYMENT', 'xdr3', 'op_success', true, 3, $1)
`, now)
(2, 'PAYMENT', $2, 'op_success', true, 1, $1),
(4098, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1),
(8194, 'PAYMENT', $4, 'op_success', true, 3, $1)
`, now, xdr1, xdr2, xdr3)
require.NoError(t, err)

// Test GetAll without limit (gets all operations)
Expand Down Expand Up @@ -571,16 +574,22 @@ func TestOperationModel_BatchGetByToIDs(t *testing.T) {
// For tx1 (to_id=4096): ops 4097, 4098, 4099
// For tx2 (to_id=8192): ops 8193, 8194
// For tx3 (to_id=12288): op 12289
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
xdr3 := types.XDRBytea([]byte("xdr3"))
xdr4 := types.XDRBytea([]byte("xdr4"))
xdr5 := types.XDRBytea([]byte("xdr5"))
xdr6 := types.XDRBytea([]byte("xdr6"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1),
(4098, 'PAYMENT', 'xdr3', 'op_success', true, 3, $1),
(4099, 'MANAGE_SELL_OFFER', 'xdr4', 'op_success', true, 4, $1),
(8194, 'PAYMENT', 'xdr5', 'op_success', true, 5, $1),
(12289, 'CHANGE_TRUST', 'xdr6', 'op_success', true, 6, $1)
`, now)
(4097, 'PAYMENT', $2, 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1),
(4098, 'PAYMENT', $4, 'op_success', true, 3, $1),
(4099, 'MANAGE_SELL_OFFER', $5, 'op_success', true, 4, $1),
(8194, 'PAYMENT', $6, 'op_success', true, 5, $1),
(12289, 'CHANGE_TRUST', $7, 'op_success', true, 6, $1)
`, now, xdr1, xdr2, xdr3, xdr4, xdr5, xdr6)
require.NoError(t, err)

testCases := []struct {
Expand Down Expand Up @@ -762,21 +771,24 @@ func TestOperationModel_BatchGetByToID(t *testing.T) {
// Create test operations - IDs must be in TOID range for each transaction
// For tx1 (to_id=4096): ops 4097, 4098
// For tx2 (to_id=8192): op 8193
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
xdr3 := types.XDRBytea([]byte("xdr3"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1),
(4098, 'PAYMENT', 'xdr3', 'op_success', true, 3, $1)
`, now)
(4097, 'PAYMENT', $2, 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1),
(4098, 'PAYMENT', $4, 'op_success', true, 3, $1)
`, now, xdr1, xdr2, xdr3)
require.NoError(t, err)

// Test BatchGetByToID
operations, err := m.BatchGetByToID(ctx, 4096, "", nil, nil, ASC)
require.NoError(t, err)
assert.Len(t, operations, 2)
assert.Equal(t, "xdr1", operations[0].OperationXDR)
assert.Equal(t, "xdr3", operations[1].OperationXDR)
assert.Equal(t, xdr1.String(), operations[0].OperationXDR.String())
assert.Equal(t, xdr3.String(), operations[1].OperationXDR.String())
}

func TestOperationModel_BatchGetByAccountAddresses(t *testing.T) {
Expand Down Expand Up @@ -819,13 +831,16 @@ func TestOperationModel_BatchGetByAccountAddresses(t *testing.T) {
require.NoError(t, err)

// Create test operations (IDs must be in TOID range for each transaction)
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
xdr3 := types.XDRBytea([]byte("xdr3"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1),
(12289, 'PAYMENT', 'xdr3', 'op_success', true, 3, $1)
`, now)
(4097, 'PAYMENT', $2, 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1),
(12289, 'PAYMENT', $4, 'op_success', true, 3, $1)
`, now, xdr1, xdr2, xdr3)
require.NoError(t, err)

// Create test operations_accounts links
Expand Down Expand Up @@ -868,12 +883,14 @@ func TestOperationModel_GetByID(t *testing.T) {
require.NoError(t, err)

// Create test operations (IDs must be in TOID range for each transaction)
opXdr1 := types.XDRBytea([]byte("xdr1"))
opXdr2 := types.XDRBytea([]byte("xdr2"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1)
`, now)
(4097, 'PAYMENT', $2, 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1)
`, now, opXdr1, opXdr2)
require.NoError(t, err)

mockMetricsService := metrics.NewMockMetricsService()
Expand All @@ -889,7 +906,7 @@ func TestOperationModel_GetByID(t *testing.T) {
operation, err := m.GetByID(ctx, 4097, "")
require.NoError(t, err)
assert.Equal(t, int64(4097), operation.ID)
assert.Equal(t, "xdr1", operation.OperationXDR)
assert.Equal(t, opXdr1.String(), operation.OperationXDR.String())
assert.Equal(t, uint32(1), operation.LedgerNumber)
assert.WithinDuration(t, now, operation.LedgerCreatedAt, time.Second)
}
Expand Down Expand Up @@ -934,13 +951,16 @@ func TestOperationModel_BatchGetByStateChangeIDs(t *testing.T) {
require.NoError(t, err)

// Create test operations (IDs must be in TOID range for each transaction)
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
xdr3 := types.XDRBytea([]byte("xdr3"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1),
(12289, 'PAYMENT', 'xdr3', 'op_success', true, 3, $1)
`, now)
(4097, 'PAYMENT', $2, 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1),
(12289, 'PAYMENT', $4, 'op_success', true, 3, $1)
`, now, xdr1, xdr2, xdr3)
require.NoError(t, err)

// Create test state changes
Expand Down
11 changes: 7 additions & 4 deletions internal/data/transactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,13 +650,16 @@ func TestTransactionModel_BatchGetByOperationIDs(t *testing.T) {
// Create test operations (IDs must be in TOID range for each transaction)
// opTestHash1 (to_id=4096): ops 4097, 4098
// opTestHash2 (to_id=8192): op 8193
xdr1 := types.XDRBytea([]byte("xdr1"))
xdr2 := types.XDRBytea([]byte("xdr2"))
xdr3 := types.XDRBytea([]byte("xdr3"))
_, err = dbConnectionPool.ExecContext(ctx, `
INSERT INTO operations (id, operation_type, operation_xdr, result_code, successful, ledger_number, ledger_created_at)
VALUES
(4097, 'PAYMENT', 'xdr1', 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', 'xdr2', 'op_success', true, 2, $1),
(4098, 'PAYMENT', 'xdr3', 'op_success', true, 3, $1)
`, now)
(4097, 'PAYMENT', $2, 'op_success', true, 1, $1),
(8193, 'CREATE_ACCOUNT', $3, 'op_success', true, 2, $1),
(4098, 'PAYMENT', $4, 'op_success', true, 3, $1)
`, now, xdr1, xdr2, xdr3)
require.NoError(t, err)

// Test BatchGetByOperationIDs
Expand Down
2 changes: 1 addition & 1 deletion internal/db/migrations/2025-06-10.3-operations.sql
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CREATE TABLE operations (
'INVOKE_HOST_FUNCTION', 'EXTEND_FOOTPRINT_TTL', 'RESTORE_FOOTPRINT'
)
),
operation_xdr TEXT,
operation_xdr BYTEA,
result_code TEXT NOT NULL,
Comment on lines 19 to 21
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the column type in this existing migration file won’t update already-migrated databases, so production/dev DBs that have operation_xdr as TEXT will keep that type and will likely break once the app starts inserting BYTEA. Add a new forward migration that ALTER TABLE operations ALTER COLUMN operation_xdr TYPE BYTEA USING decode(operation_xdr, 'base64') (or equivalent), and avoid rewriting already-shipped migrations.

Copilot uses AI. Check for mistakes.
successful BOOLEAN NOT NULL,
ledger_number INTEGER NOT NULL,
Expand Down
4 changes: 2 additions & 2 deletions internal/indexer/processors/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ func ConvertOperation(
opIndex uint32,
opResults []xdr.OperationResult,
) (*types.Operation, error) {
xdrOpStr, err := xdr.MarshalBase64(op)
xdrBytes, err := op.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("marshalling operation %d: %w", opID, err)
}
Expand All @@ -350,7 +350,7 @@ func ConvertOperation(
return &types.Operation{
ID: opID,
OperationType: types.OperationTypeFromXDR(op.Body.Type),
OperationXDR: xdrOpStr,
OperationXDR: types.XDRBytea(xdrBytes),
ResultCode: resultCode,
Successful: successful,
LedgerCreatedAt: transaction.Ledger.ClosedAt(),
Expand Down
7 changes: 6 additions & 1 deletion internal/indexer/processors/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package processors

import (
"encoding/base64"
"testing"
"time"

Expand Down Expand Up @@ -101,10 +102,14 @@ func Test_ConvertOperation(t *testing.T) {
gotDataOp, err := ConvertOperation(&ingestTx, &op, opID, opIndex, opResults)
require.NoError(t, err)

// Decode expected base64 XDR to raw bytes for comparison
expectedXDRBytes, err := base64.StdEncoding.DecodeString(opXDRStr)
require.NoError(t, err)

wantDataOp := &types.Operation{
ID: opID,
OperationType: types.OperationTypeFromXDR(op.Body.Type),
OperationXDR: opXDRStr,
OperationXDR: types.XDRBytea(expectedXDRBytes),
ResultCode: OpSuccess,
Successful: true,
LedgerCreatedAt: time.Date(2025, time.June, 19, 0, 3, 16, 0, time.UTC),
Expand Down
36 changes: 35 additions & 1 deletion internal/indexer/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ package types
import (
"database/sql"
"database/sql/driver"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -166,6 +167,39 @@ func (h HashBytea) String() string {
return string(h)
}

// XDRBytea represents XDR data stored as BYTEA in the database.
// Storage format: raw XDR bytes (variable length)
// Go representation: raw bytes internally, base64 string via String()
type XDRBytea []byte

// Scan implements sql.Scanner - reads raw bytes from BYTEA column
func (x *XDRBytea) Scan(value any) error {
if value == nil {
*x = nil
return nil
}
bytes, ok := value.([]byte)
if !ok {
return fmt.Errorf("expected []byte, got %T", value)
}
*x = make([]byte, len(bytes))
copy(*x, bytes)
return nil
}

// Value implements driver.Valuer - returns raw bytes for BYTEA storage
func (x XDRBytea) Value() (driver.Value, error) {
if len(x) == 0 {
return nil, nil
}
return []byte(x), nil
}

// String returns the XDR as a base64 string.
func (x XDRBytea) String() string {
return base64.StdEncoding.EncodeToString(x)
}

type ContractType string

const (
Expand Down Expand Up @@ -372,7 +406,7 @@ type Operation struct {
// The parent transaction's to_id can be derived: ID &^ 0xFFF
ID int64 `json:"id,omitempty" db:"id"`
OperationType OperationType `json:"operationType,omitempty" db:"operation_type"`
OperationXDR string `json:"operationXdr,omitempty" db:"operation_xdr"`
OperationXDR XDRBytea `json:"operationXdr,omitempty" db:"operation_xdr"`
ResultCode string `json:"resultCode,omitempty" db:"result_code"`
Successful bool `json:"successful,omitempty" db:"successful"`
LedgerNumber uint32 `json:"ledgerNumber,omitempty" db:"ledger_number"`
Expand Down
Loading
Loading