From 314b605e87aecae83f999a3b9daa54859265b6d1 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Fri, 10 Jan 2025 12:59:28 -0700 Subject: [PATCH 01/12] xdrill for operations --- go.mod | 1 + go.sum | 2 + ingest/ledger_operation.go | 1431 ++++++++++++++++++++++++++++++ ingest/ledger_operation_test.go | 1474 +++++++++++++++++++++++++++++++ ingest/ledger_transaction.go | 160 ++++ xdr/hash.go | 16 +- xdr/operation_trace_result.go | 68 ++ 7 files changed, 3151 insertions(+), 1 deletion(-) create mode 100644 ingest/ledger_operation.go create mode 100644 ingest/ledger_operation_test.go create mode 100644 xdr/operation_trace_result.go diff --git a/go.mod b/go.mod index 341c85543e..6d7e5fab5d 100644 --- a/go.mod +++ b/go.mod @@ -58,6 +58,7 @@ require ( require ( github.com/cenkalti/backoff/v4 v4.3.0 + github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 github.com/fsouza/fake-gcs-server v1.49.2 diff --git a/go.sum b/go.sum index a3c8ce08fa..b071601459 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/fscache v0.10.1 h1:hDv+RGyvD+UDKyRYuLoVNbuRTnf2SrA2K3VyR1br9lk= diff --git a/ingest/ledger_operation.go b/ingest/ledger_operation.go new file mode 100644 index 0000000000..f59add11e2 --- /dev/null +++ b/ingest/ledger_operation.go @@ -0,0 +1,1431 @@ +package ingest + +import ( + "encoding/base64" + "fmt" + "math/big" + "strconv" + + "github.com/dgryski/go-farm" + "github.com/stellar/go/amount" + "github.com/stellar/go/support/contractevents" + "github.com/stellar/go/toid" + "github.com/stellar/go/xdr" +) + +type LedgerOperation struct { + OperationIndex int32 + Operation xdr.Operation + Transaction *LedgerTransaction + NetworkPassphrase string +} + +func (o *LedgerOperation) sourceAccountXDR() xdr.MuxedAccount { + sourceAccount := o.Operation.SourceAccount + if sourceAccount != nil { + return *sourceAccount + } + + return o.Transaction.Envelope.SourceAccount() +} + +func (o *LedgerOperation) SourceAccount() string { + muxedAccount := o.sourceAccountXDR() + providedID := muxedAccount.ToAccountId() + pointerToID := &providedID + + return pointerToID.Address() +} + +func (o *LedgerOperation) Type() int32 { + return int32(o.Operation.Body.Type) +} + +func (o *LedgerOperation) TypeString() string { + return xdr.OperationTypeToStringMap[o.Type()] +} + +func (o *LedgerOperation) ID() int64 { + //operationIndex needs +1 increment to stay in sync with ingest package + return toid.New(int32(o.Transaction.Ledger.LedgerSequence()), int32(o.Transaction.Index), o.OperationIndex+1).ToInt64() +} + +func (o *LedgerOperation) SourceAccountMuxed() (string, bool) { + muxedAccount := o.sourceAccountXDR() + if muxedAccount.Type != xdr.CryptoKeyTypeKeyTypeMuxedEd25519 { + return "", false + } + + return muxedAccount.Address(), true +} + +func (o *LedgerOperation) OperationResultCode() string { + var operationResultCode string + operationResults, ok := o.Transaction.Result.Result.OperationResults() + if ok { + operationResultCode = operationResults[o.OperationIndex].Code.String() + } + + return operationResultCode +} + +func (o *LedgerOperation) OperationTraceCode() (string, error) { + var operationTraceCode string + + operationResults, ok := o.Transaction.Result.Result.OperationResults() + if ok { + operationResultTr, ok := operationResults[o.OperationIndex].GetTr() + if ok { + operationTraceCode, err := operationResultTr.MapOperationResultTr() + if err != nil { + return "", err + } + return operationTraceCode, nil + } + } + + return operationTraceCode, nil +} + +func (o *LedgerOperation) OperationDetails() (map[string]interface{}, error) { + var err error + details := map[string]interface{}{} + + switch o.Operation.Body.Type { + case xdr.OperationTypeCreateAccount: + details, err = o.CreateAccountDetails() + if err != nil { + return details, err + } + case xdr.OperationTypePayment: + details, err = o.PaymentDetails() + if err != nil { + return details, err + } + case xdr.OperationTypePathPaymentStrictReceive: + details, err = o.PathPaymentStrictReceiveDetails() + if err != nil { + return details, err + } + case xdr.OperationTypePathPaymentStrictSend: + details, err = o.PathPaymentStrictSendDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeManageBuyOffer: + details, err = o.ManageBuyOfferDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeManageSellOffer: + details, err = o.ManageSellOfferDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeCreatePassiveSellOffer: + details, err = o.CreatePassiveSellOfferDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeSetOptions: + details, err = o.SetOptionsDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeChangeTrust: + details, err = o.ChangeTrustDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeAllowTrust: + details, err = o.AllowTrustDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeAccountMerge: + details, err = o.AccountMergeDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeInflation: + details, err = o.InflationDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeManageData: + details, err = o.ManageDataDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeBumpSequence: + details, err = o.BumpSequenceDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeCreateClaimableBalance: + details, err = o.CreateClaimableBalanceDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeClaimClaimableBalance: + details, err = o.ClaimClaimableBalanceDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeBeginSponsoringFutureReserves: + details, err = o.BeginSponsoringFutureReservesDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeEndSponsoringFutureReserves: + details, err = o.EndSponsoringFutureReserveDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeRevokeSponsorship: + details, err = o.RevokeSponsorshipDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeClawback: + details, err = o.ClawbackDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeClawbackClaimableBalance: + details, err = o.ClawbackClaimableBalanceDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeSetTrustLineFlags: + details, err = o.SetTrustLineFlagsDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeLiquidityPoolDeposit: + details, err = o.LiquidityPoolDepositDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeLiquidityPoolWithdraw: + details, err = o.LiquidityPoolWithdrawDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeInvokeHostFunction: + details, err = o.InvokeHostFunctionDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeExtendFootprintTtl: + details, err = o.ExtendFootprintTtlDetails() + if err != nil { + return details, err + } + case xdr.OperationTypeRestoreFootprint: + details, err = o.RestoreFootprintDetails() + if err != nil { + return details, err + } + default: + return details, fmt.Errorf("unknown operation type: %s", o.Operation.Body.Type.String()) + } + + return details, nil +} + +func (o *LedgerOperation) CreateAccountDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetCreateAccountOp() + if !ok { + return details, fmt.Errorf("could not access CreateAccount info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "funder"); err != nil { + return details, err + } + details["account"] = op.Destination.Address() + details["starting_balance"] = o.ConvertStroopValueToReal(op.StartingBalance) + + return details, nil +} + +func (o *LedgerOperation) addAccountAndMuxedAccountDetails(result map[string]interface{}, a xdr.MuxedAccount, prefix string) error { + account_id := a.ToAccountId() + result[prefix] = account_id.Address() + prefix = o.FormatPrefix(prefix) + if a.Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 { + muxedAccountAddress, err := a.GetAddress() + if err != nil { + return err + } + result[prefix+"muxed"] = muxedAccountAddress + muxedAccountId, err := a.GetId() + if err != nil { + return err + } + result[prefix+"muxed_id"] = muxedAccountId + } + return nil +} + +func (o *LedgerOperation) PaymentDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetPaymentOp() + if !ok { + return details, fmt.Errorf("could not access Payment info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "from"); err != nil { + return details, err + } + if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { + return details, err + } + details["amount"] = o.ConvertStroopValueToReal(op.Amount) + if err := o.addAssetDetailsToOperationDetails(details, op.Asset, ""); err != nil { + return details, err + } + + return details, nil +} + +func (o *LedgerOperation) addAssetDetailsToOperationDetails(result map[string]interface{}, asset xdr.Asset, prefix string) error { + var assetType, code, issuer string + err := asset.Extract(&assetType, &code, &issuer) + if err != nil { + return err + } + + prefix = o.FormatPrefix(prefix) + result[prefix+"asset_type"] = assetType + + if asset.Type == xdr.AssetTypeAssetTypeNative { + result[prefix+"asset_id"] = int64(-5706705804583548011) + return nil + } + + result[prefix+"asset_code"] = code + result[prefix+"asset_issuer"] = issuer + result[prefix+"asset_id"] = o.FarmHashAsset(code, issuer, assetType) + + return nil +} + +func (o *LedgerOperation) PathPaymentStrictReceiveDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetPathPaymentStrictReceiveOp() + if !ok { + return details, fmt.Errorf("could not access PathPaymentStrictReceive info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "from"); err != nil { + return details, err + } + if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { + return details, err + } + details["amount"] = o.ConvertStroopValueToReal(op.DestAmount) + details["source_amount"] = amount.String(0) + details["source_max"] = o.ConvertStroopValueToReal(op.SendMax) + if err := o.addAssetDetailsToOperationDetails(details, op.DestAsset, ""); err != nil { + return details, err + } + if err := o.addAssetDetailsToOperationDetails(details, op.SendAsset, "source"); err != nil { + return details, err + } + + if o.Transaction.Successful() { + allOperationResults, ok := o.Transaction.Result.OperationResults() + if !ok { + return details, fmt.Errorf("could not access any results for this transaction") + } + currentOperationResult := allOperationResults[o.OperationIndex] + resultBody, ok := currentOperationResult.GetTr() + if !ok { + return details, fmt.Errorf("could not access result body for this operation (index %d)", o.OperationIndex) + } + result, ok := resultBody.GetPathPaymentStrictReceiveResult() + if !ok { + return details, fmt.Errorf("could not access PathPaymentStrictReceive result info for this operation (index %d)", o.OperationIndex) + } + details["source_amount"] = o.ConvertStroopValueToReal(result.SendAmount()) + } + + details["path"] = o.TransformPath(op.Path) + return details, nil +} + +func (o *LedgerOperation) PathPaymentStrictSendDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetPathPaymentStrictSendOp() + if !ok { + return details, fmt.Errorf("could not access PathPaymentStrictSend info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "from"); err != nil { + return details, err + } + if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { + return details, err + } + details["amount"] = float64(0) + details["source_amount"] = o.ConvertStroopValueToReal(op.SendAmount) + details["destination_min"] = amount.String(op.DestMin) + if err := o.addAssetDetailsToOperationDetails(details, op.DestAsset, ""); err != nil { + return details, err + } + if err := o.addAssetDetailsToOperationDetails(details, op.SendAsset, "source"); err != nil { + return details, err + } + + if o.Transaction.Successful() { + allOperationResults, ok := o.Transaction.Result.OperationResults() + if !ok { + return details, fmt.Errorf("could not access any results for this transaction") + } + currentOperationResult := allOperationResults[o.OperationIndex] + resultBody, ok := currentOperationResult.GetTr() + if !ok { + return details, fmt.Errorf("could not access result body for this operation (index %d)", o.OperationIndex) + } + result, ok := resultBody.GetPathPaymentStrictSendResult() + if !ok { + return details, fmt.Errorf("could not access GetPathPaymentStrictSendResult result info for this operation (index %d)", o.OperationIndex) + } + details["amount"] = o.ConvertStroopValueToReal(result.DestAmount()) + } + + details["path"] = o.TransformPath(op.Path) + + return details, nil +} +func (o *LedgerOperation) ManageBuyOfferDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetManageBuyOfferOp() + if !ok { + return details, fmt.Errorf("could not access ManageBuyOffer info for this operation (index %d)", o.OperationIndex) + } + + details["offer_id"] = int64(op.OfferId) + details["amount"] = o.ConvertStroopValueToReal(op.BuyAmount) + if err := o.addPriceDetails(details, op.Price, ""); err != nil { + return details, err + } + + if err := o.addAssetDetailsToOperationDetails(details, op.Buying, "buying"); err != nil { + return details, err + } + if err := o.addAssetDetailsToOperationDetails(details, op.Selling, "selling"); err != nil { + return details, err + } + + return details, nil +} + +func (o *LedgerOperation) addPriceDetails(result map[string]interface{}, price xdr.Price, prefix string) error { + prefix = o.FormatPrefix(prefix) + parsedPrice, err := strconv.ParseFloat(price.String(), 64) + if err != nil { + return err + } + result[prefix+"price"] = parsedPrice + result[prefix+"price_r"] = Price{ + Numerator: int32(price.N), + Denominator: int32(price.D), + } + return nil +} + +func (o *LedgerOperation) ManageSellOfferDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetManageSellOfferOp() + if !ok { + return details, fmt.Errorf("could not access ManageSellOffer info for this operation (index %d)", o.OperationIndex) + } + + details["offer_id"] = int64(op.OfferId) + details["amount"] = o.ConvertStroopValueToReal(op.Amount) + if err := o.addPriceDetails(details, op.Price, ""); err != nil { + return details, err + } + + if err := o.addAssetDetailsToOperationDetails(details, op.Buying, "buying"); err != nil { + return details, err + } + if err := o.addAssetDetailsToOperationDetails(details, op.Selling, "selling"); err != nil { + return details, err + } + + return details, nil +} +func (o *LedgerOperation) CreatePassiveSellOfferDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetCreatePassiveSellOfferOp() + if !ok { + return details, fmt.Errorf("could not access CreatePassiveSellOffer info for this operation (index %d)", o.OperationIndex) + } + + details["amount"] = o.ConvertStroopValueToReal(op.Amount) + if err := o.addPriceDetails(details, op.Price, ""); err != nil { + return details, err + } + + if err := o.addAssetDetailsToOperationDetails(details, op.Buying, "buying"); err != nil { + return details, err + } + if err := o.addAssetDetailsToOperationDetails(details, op.Selling, "selling"); err != nil { + return details, err + } + + return details, nil +} +func (o *LedgerOperation) SetOptionsDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetSetOptionsOp() + if !ok { + return details, fmt.Errorf("could not access GetSetOptions info for this operation (index %d)", o.OperationIndex) + } + + if op.InflationDest != nil { + details["inflation_dest"] = op.InflationDest.Address() + } + + if op.SetFlags != nil && *op.SetFlags > 0 { + o.addOperationFlagToOperationDetails(details, uint32(*op.SetFlags), "set") + } + + if op.ClearFlags != nil && *op.ClearFlags > 0 { + o.addOperationFlagToOperationDetails(details, uint32(*op.ClearFlags), "clear") + } + + if op.MasterWeight != nil { + details["master_key_weight"] = uint32(*op.MasterWeight) + } + + if op.LowThreshold != nil { + details["low_threshold"] = uint32(*op.LowThreshold) + } + + if op.MedThreshold != nil { + details["med_threshold"] = uint32(*op.MedThreshold) + } + + if op.HighThreshold != nil { + details["high_threshold"] = uint32(*op.HighThreshold) + } + + if op.HomeDomain != nil { + details["home_domain"] = string(*op.HomeDomain) + } + + if op.Signer != nil { + details["signer_key"] = op.Signer.Key.Address() + details["signer_weight"] = uint32(op.Signer.Weight) + } + + return details, nil +} + +func (o *LedgerOperation) addOperationFlagToOperationDetails(result map[string]interface{}, flag uint32, prefix string) { + intFlags := make([]int32, 0) + stringFlags := make([]string, 0) + + if (int64(flag) & int64(xdr.AccountFlagsAuthRequiredFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthRequiredFlag)) + stringFlags = append(stringFlags, "auth_required") + } + + if (int64(flag) & int64(xdr.AccountFlagsAuthRevocableFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthRevocableFlag)) + stringFlags = append(stringFlags, "auth_revocable") + } + + if (int64(flag) & int64(xdr.AccountFlagsAuthImmutableFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthImmutableFlag)) + stringFlags = append(stringFlags, "auth_immutable") + } + + if (int64(flag) & int64(xdr.AccountFlagsAuthClawbackEnabledFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthClawbackEnabledFlag)) + stringFlags = append(stringFlags, "auth_clawback_enabled") + } + + prefix = o.FormatPrefix(prefix) + result[prefix+"flags"] = intFlags + result[prefix+"flags_s"] = stringFlags +} + +func (o *LedgerOperation) ChangeTrustDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetChangeTrustOp() + if !ok { + return details, fmt.Errorf("could not access GetChangeTrust info for this operation (index %d)", o.OperationIndex) + } + + if op.Line.Type == xdr.AssetTypeAssetTypePoolShare { + if err := o.addLiquidityPoolAssetDetails(details, *op.Line.LiquidityPool); err != nil { + return details, err + } + } else { + if err := o.addAssetDetailsToOperationDetails(details, op.Line.ToAsset(), ""); err != nil { + return details, err + } + details["trustee"] = details["asset_issuer"] + } + + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "trustor"); err != nil { + return details, err + } + + details["limit"] = o.ConvertStroopValueToReal(op.Limit) + + return details, nil +} + +func (o *LedgerOperation) addLiquidityPoolAssetDetails(result map[string]interface{}, lpp xdr.LiquidityPoolParameters) error { + result["asset_type"] = "liquidity_pool_shares" + if lpp.Type != xdr.LiquidityPoolTypeLiquidityPoolConstantProduct { + return fmt.Errorf("unknown liquidity pool type %d", lpp.Type) + } + cp := lpp.ConstantProduct + poolID, err := xdr.NewPoolId(cp.AssetA, cp.AssetB, cp.Fee) + if err != nil { + return err + } + + result["liquidity_pool_id"] = o.PoolIDToString(poolID) + + return nil +} + +func (o *LedgerOperation) AllowTrustDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetAllowTrustOp() + if !ok { + return details, fmt.Errorf("could not access AllowTrust info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAssetDetailsToOperationDetails(details, op.Asset.ToAsset(o.sourceAccountXDR().ToAccountId()), ""); err != nil { + return details, err + } + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "trustee"); err != nil { + return details, err + } + details["trustor"] = op.Trustor.Address() + shouldAuth := xdr.TrustLineFlags(op.Authorize).IsAuthorized() + details["authorize"] = shouldAuth + shouldAuthLiabilities := xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag() + if shouldAuthLiabilities { + details["authorize_to_maintain_liabilities"] = shouldAuthLiabilities + } + shouldClawbackEnabled := xdr.TrustLineFlags(op.Authorize).IsClawbackEnabledFlag() + if shouldClawbackEnabled { + details["clawback_enabled"] = shouldClawbackEnabled + } + + return details, nil +} + +func (o *LedgerOperation) AccountMergeDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + destinationAccount, ok := o.Operation.Body.GetDestination() + if !ok { + return details, fmt.Errorf("could not access Destination info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "account"); err != nil { + return details, err + } + if err := o.addAccountAndMuxedAccountDetails(details, destinationAccount, "into"); err != nil { + return details, err + } + + return details, nil +} + +// Inflation operations don't have information that affects the details struct +func (o *LedgerOperation) InflationDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + return details, nil +} + +func (o *LedgerOperation) ManageDataDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetManageDataOp() + if !ok { + return details, fmt.Errorf("could not access GetManageData info for this operation (index %d)", o.OperationIndex) + } + + details["name"] = string(op.DataName) + if op.DataValue != nil { + details["value"] = base64.StdEncoding.EncodeToString(*op.DataValue) + } else { + details["value"] = nil + } + + return details, nil +} + +func (o *LedgerOperation) BumpSequenceDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetBumpSequenceOp() + if !ok { + return details, fmt.Errorf("could not access BumpSequence info for this operation (index %d)", o.OperationIndex) + } + + details["bump_to"] = fmt.Sprintf("%d", op.BumpTo) + + return details, nil +} +func (o *LedgerOperation) CreateClaimableBalanceDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetCreateClaimableBalanceOp() + if !ok { + return details, fmt.Errorf("could not access CreateClaimableBalance info for this operation (index %d)", o.OperationIndex) + } + + details["asset"] = op.Asset.StringCanonical() + details["amount"] = o.ConvertStroopValueToReal(op.Amount) + details["claimants"] = o.TransformClaimants(op.Claimants) + + return details, nil +} + +func (o *LedgerOperation) ClaimClaimableBalanceDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetClaimClaimableBalanceOp() + if !ok { + return details, fmt.Errorf("could not access ClaimClaimableBalance info for this operation (index %d)", o.OperationIndex) + } + + balanceID, err := xdr.MarshalHex(op.BalanceId) + if err != nil { + return details, fmt.Errorf("invalid balanceId in op: %d", o.OperationIndex) + } + details["balance_id"] = balanceID + if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "claimant"); err != nil { + return details, err + } + + return details, nil +} + +func (o *LedgerOperation) BeginSponsoringFutureReservesDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetBeginSponsoringFutureReservesOp() + if !ok { + return details, fmt.Errorf("could not access BeginSponsoringFutureReserves info for this operation (index %d)", o.OperationIndex) + } + + details["sponsored_id"] = op.SponsoredId.Address() + + return details, nil +} + +func (o *LedgerOperation) EndSponsoringFutureReserveDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + beginSponsorOp := o.findInitatingBeginSponsoringOp() + if beginSponsorOp != nil { + beginSponsorshipSource := o.sourceAccountXDR() + if err := o.addAccountAndMuxedAccountDetails(details, beginSponsorshipSource, "begin_sponsor"); err != nil { + return details, err + } + } + + return details, nil +} + +func (o *LedgerOperation) findInitatingBeginSponsoringOp() *SponsorshipOutput { + if !o.Transaction.Successful() { + // Failed transactions may not have a compliant sandwich structure + // we can rely on (e.g. invalid nesting or a being operation with the wrong sponsoree ID) + // and thus we bail out since we could return incorrect information. + return nil + } + sponsoree := o.sourceAccountXDR().ToAccountId() + operations := o.Transaction.Envelope.Operations() + for i := int(o.OperationIndex) - 1; i >= 0; i-- { + if beginOp, ok := operations[i].Body.GetBeginSponsoringFutureReservesOp(); ok && + beginOp.SponsoredId.Address() == sponsoree.Address() { + result := SponsorshipOutput{ + Operation: operations[i], + OperationIndex: uint32(i), + } + return &result + } + } + return nil +} + +func (o *LedgerOperation) RevokeSponsorshipDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetRevokeSponsorshipOp() + if !ok { + return details, fmt.Errorf("could not access RevokeSponsorship info for this operation (index %d)", o.OperationIndex) + } + + switch op.Type { + case xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry: + if err := o.addLedgerKeyToDetails(details, *op.LedgerKey); err != nil { + return details, err + } + case xdr.RevokeSponsorshipTypeRevokeSponsorshipSigner: + details["signer_account_id"] = op.Signer.AccountId.Address() + details["signer_key"] = op.Signer.SignerKey.Address() + } + + return details, nil +} + +func (o *LedgerOperation) addLedgerKeyToDetails(result map[string]interface{}, ledgerKey xdr.LedgerKey) error { + switch ledgerKey.Type { + case xdr.LedgerEntryTypeAccount: + result["account_id"] = ledgerKey.Account.AccountId.Address() + case xdr.LedgerEntryTypeClaimableBalance: + marshalHex, err := xdr.MarshalHex(ledgerKey.ClaimableBalance.BalanceId) + if err != nil { + return fmt.Errorf("in claimable balance: %w", err) + } + result["claimable_balance_id"] = marshalHex + case xdr.LedgerEntryTypeData: + result["data_account_id"] = ledgerKey.Data.AccountId.Address() + result["data_name"] = string(ledgerKey.Data.DataName) + case xdr.LedgerEntryTypeOffer: + result["offer_id"] = int64(ledgerKey.Offer.OfferId) + case xdr.LedgerEntryTypeTrustline: + result["trustline_account_id"] = ledgerKey.TrustLine.AccountId.Address() + if ledgerKey.TrustLine.Asset.Type == xdr.AssetTypeAssetTypePoolShare { + result["trustline_liquidity_pool_id"] = o.PoolIDToString(*ledgerKey.TrustLine.Asset.LiquidityPoolId) + } else { + result["trustline_asset"] = ledgerKey.TrustLine.Asset.ToAsset().StringCanonical() + } + case xdr.LedgerEntryTypeLiquidityPool: + result["liquidity_pool_id"] = o.PoolIDToString(ledgerKey.LiquidityPool.LiquidityPoolId) + } + return nil +} + +func (o *LedgerOperation) ClawbackDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetClawbackOp() + if !ok { + return details, fmt.Errorf("could not access Clawback info for this operation (index %d)", o.OperationIndex) + } + + if err := o.addAssetDetailsToOperationDetails(details, op.Asset, ""); err != nil { + return details, err + } + if err := o.addAccountAndMuxedAccountDetails(details, op.From, "from"); err != nil { + return details, err + } + details["amount"] = o.ConvertStroopValueToReal(op.Amount) + + return details, nil +} +func (o *LedgerOperation) ClawbackClaimableBalanceDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetClawbackClaimableBalanceOp() + if !ok { + return details, fmt.Errorf("could not access ClawbackClaimableBalance info for this operation (index %d)", o.OperationIndex) + } + + balanceID, err := xdr.MarshalHex(op.BalanceId) + if err != nil { + return details, fmt.Errorf("invalid balanceId in op: %d", o.OperationIndex) + } + details["balance_id"] = balanceID + + return details, nil +} +func (o *LedgerOperation) SetTrustLineFlagsDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetSetTrustLineFlagsOp() + if !ok { + return details, fmt.Errorf("could not access SetTrustLineFlags info for this operation (index %d)", o.OperationIndex) + } + + details["trustor"] = op.Trustor.Address() + if err := o.addAssetDetailsToOperationDetails(details, op.Asset, ""); err != nil { + return details, err + } + if op.SetFlags > 0 { + o.addTrustLineFlagToDetails(details, xdr.TrustLineFlags(op.SetFlags), "set") + + } + if op.ClearFlags > 0 { + o.addTrustLineFlagToDetails(details, xdr.TrustLineFlags(op.ClearFlags), "clear") + } + + return details, nil +} + +func (o *LedgerOperation) addTrustLineFlagToDetails(result map[string]interface{}, f xdr.TrustLineFlags, prefix string) { + var ( + n []int32 + s []string + ) + + if f.IsAuthorized() { + n = append(n, int32(xdr.TrustLineFlagsAuthorizedFlag)) + s = append(s, "authorized") + } + + if f.IsAuthorizedToMaintainLiabilitiesFlag() { + n = append(n, int32(xdr.TrustLineFlagsAuthorizedToMaintainLiabilitiesFlag)) + s = append(s, "authorized_to_maintain_liabilities") + } + + if f.IsClawbackEnabledFlag() { + n = append(n, int32(xdr.TrustLineFlagsTrustlineClawbackEnabledFlag)) + s = append(s, "clawback_enabled") + } + + prefix = o.FormatPrefix(prefix) + result[prefix+"flags"] = n + result[prefix+"flags_s"] = s +} + +func (o *LedgerOperation) LiquidityPoolDepositDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetLiquidityPoolDepositOp() + if !ok { + return details, fmt.Errorf("could not access LiquidityPoolDeposit info for this operation (index %d)", o.OperationIndex) + } + + details["liquidity_pool_id"] = o.PoolIDToString(op.LiquidityPoolId) + var ( + assetA, assetB xdr.Asset + depositedA, depositedB xdr.Int64 + sharesReceived xdr.Int64 + ) + if o.Transaction.Successful() { + // we will use the defaults (omitted asset and 0 amounts) if the transaction failed + lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + if err != nil { + return nil, err + } + params := lp.Body.ConstantProduct.Params + assetA, assetB = params.AssetA, params.AssetB + depositedA, depositedB = delta.ReserveA, delta.ReserveB + sharesReceived = delta.TotalPoolShares + } + + // Process ReserveA Details + if err := o.addAssetDetailsToOperationDetails(details, assetA, "reserve_a"); err != nil { + return details, err + } + details["reserve_a_max_amount"] = o.ConvertStroopValueToReal(op.MaxAmountA) + depositA, err := strconv.ParseFloat(amount.String(depositedA), 64) + if err != nil { + return details, err + } + details["reserve_a_deposit_amount"] = depositA + + //Process ReserveB Details + if err := o.addAssetDetailsToOperationDetails(details, assetB, "reserve_b"); err != nil { + return details, err + } + details["reserve_b_max_amount"] = o.ConvertStroopValueToReal(op.MaxAmountB) + depositB, err := strconv.ParseFloat(amount.String(depositedB), 64) + if err != nil { + return details, err + } + details["reserve_b_deposit_amount"] = depositB + + if err := o.addPriceDetails(details, op.MinPrice, "min"); err != nil { + return details, err + } + if err := o.addPriceDetails(details, op.MaxPrice, "max"); err != nil { + return details, err + } + + sharesToFloat, err := strconv.ParseFloat(amount.String(sharesReceived), 64) + if err != nil { + return details, err + } + details["shares_received"] = sharesToFloat + + return details, nil +} + +// operation xdr.Operation, operationIndex int32, transaction LedgerTransaction, ledgerSeq int32 +func (o *LedgerOperation) getLiquidityPoolAndProductDelta(lpID *xdr.PoolId) (*xdr.LiquidityPoolEntry, *LiquidityPoolDelta, error) { + changes, err := o.Transaction.GetOperationChanges(uint32(o.OperationIndex)) + if err != nil { + return nil, nil, err + } + + for _, c := range changes { + if c.Type != xdr.LedgerEntryTypeLiquidityPool { + continue + } + // The delta can be caused by a full removal or full creation of the liquidity pool + var lp *xdr.LiquidityPoolEntry + var preA, preB, preShares xdr.Int64 + if c.Pre != nil { + if lpID != nil && c.Pre.Data.LiquidityPool.LiquidityPoolId != *lpID { + // if we were looking for specific pool id, then check on it + continue + } + lp = c.Pre.Data.LiquidityPool + if c.Pre.Data.LiquidityPool.Body.Type != xdr.LiquidityPoolTypeLiquidityPoolConstantProduct { + return nil, nil, fmt.Errorf("unexpected liquity pool body type %d", c.Pre.Data.LiquidityPool.Body.Type) + } + cpPre := c.Pre.Data.LiquidityPool.Body.ConstantProduct + preA, preB, preShares = cpPre.ReserveA, cpPre.ReserveB, cpPre.TotalPoolShares + } + var postA, postB, postShares xdr.Int64 + if c.Post != nil { + if lpID != nil && c.Post.Data.LiquidityPool.LiquidityPoolId != *lpID { + // if we were looking for specific pool id, then check on it + continue + } + lp = c.Post.Data.LiquidityPool + if c.Post.Data.LiquidityPool.Body.Type != xdr.LiquidityPoolTypeLiquidityPoolConstantProduct { + return nil, nil, fmt.Errorf("unexpected liquity pool body type %d", c.Post.Data.LiquidityPool.Body.Type) + } + cpPost := c.Post.Data.LiquidityPool.Body.ConstantProduct + postA, postB, postShares = cpPost.ReserveA, cpPost.ReserveB, cpPost.TotalPoolShares + } + delta := &LiquidityPoolDelta{ + ReserveA: postA - preA, + ReserveB: postB - preB, + TotalPoolShares: postShares - preShares, + } + return lp, delta, nil + } + + return nil, nil, fmt.Errorf("liquidity pool change not found") +} + +func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetLiquidityPoolWithdrawOp() + if !ok { + return details, fmt.Errorf("could not access LiquidityPoolWithdraw info for this operation (index %d)", o.OperationIndex) + } + + details["liquidity_pool_id"] = o.PoolIDToString(op.LiquidityPoolId) + var ( + assetA, assetB xdr.Asset + receivedA, receivedB xdr.Int64 + ) + if o.Transaction.Successful() { + // we will use the defaults (omitted asset and 0 amounts) if the transaction failed + lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + if err != nil { + return nil, err + } + params := lp.Body.ConstantProduct.Params + assetA, assetB = params.AssetA, params.AssetB + receivedA, receivedB = -delta.ReserveA, -delta.ReserveB + } + // Process AssetA Details + if err := o.addAssetDetailsToOperationDetails(details, assetA, "reserve_a"); err != nil { + return details, err + } + details["reserve_a_min_amount"] = o.ConvertStroopValueToReal(op.MinAmountA) + details["reserve_a_withdraw_amount"] = o.ConvertStroopValueToReal(receivedA) + + // Process AssetB Details + if err := o.addAssetDetailsToOperationDetails(details, assetB, "reserve_b"); err != nil { + return details, err + } + details["reserve_b_min_amount"] = o.ConvertStroopValueToReal(op.MinAmountB) + details["reserve_b_withdraw_amount"] = o.ConvertStroopValueToReal(receivedB) + + details["shares"] = o.ConvertStroopValueToReal(op.Amount) + + return details, nil +} + +func (o *LedgerOperation) InvokeHostFunctionDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetInvokeHostFunctionOp() + if !ok { + return details, fmt.Errorf("could not access InvokeHostFunction info for this operation (index %d)", o.OperationIndex) + } + + details["function"] = op.HostFunction.Type.String() + + switch op.HostFunction.Type { + case xdr.HostFunctionTypeHostFunctionTypeInvokeContract: + invokeArgs := op.HostFunction.MustInvokeContract() + args := make([]xdr.ScVal, 0, len(invokeArgs.Args)+2) + args = append(args, xdr.ScVal{Type: xdr.ScValTypeScvAddress, Address: &invokeArgs.ContractAddress}) + args = append(args, xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &invokeArgs.FunctionName}) + args = append(args, invokeArgs.Args...) + + details["type"] = "invoke_contract" + + contractId, err := invokeArgs.ContractAddress.String() + if err != nil { + return nil, err + } + + details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() + details["contract_id"] = contractId + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + details["contract_code_hash"] = contractCodeHash + } + + details["parameters"], details["parameters_decoded"] = o.serializeParameters(args) + + if balanceChanges, err := o.parseAssetBalanceChangesFromContractEvents(); err != nil { + return nil, err + } else { + details["asset_balance_changes"] = balanceChanges + } + + case xdr.HostFunctionTypeHostFunctionTypeCreateContract: + args := op.HostFunction.MustCreateContract() + details["type"] = "create_contract" + + details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + details["contract_id"] = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + details["contract_code_hash"] = contractCodeHash + } + + preimageTypeMap := o.switchContractIdPreimageType(args.ContractIdPreimage) + for key, val := range preimageTypeMap { + if _, ok := preimageTypeMap[key]; ok { + details[key] = val + } + } + case xdr.HostFunctionTypeHostFunctionTypeUploadContractWasm: + details["type"] = "upload_wasm" + details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + details["contract_code_hash"] = contractCodeHash + } + case xdr.HostFunctionTypeHostFunctionTypeCreateContractV2: + args := op.HostFunction.MustCreateContractV2() + details["type"] = "create_contract_v2" + + details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + details["contract_id"] = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + details["contract_code_hash"] = contractCodeHash + } + + // ConstructorArgs is a list of ScVals + // This will initially be handled the same as InvokeContractParams until a different + // model is found necessary. + constructorArgs := args.ConstructorArgs + details["parameters"], details["parameters_decoded"] = o.serializeParameters(constructorArgs) + + preimageTypeMap := o.switchContractIdPreimageType(args.ContractIdPreimage) + for key, val := range preimageTypeMap { + if _, ok := preimageTypeMap[key]; ok { + details[key] = val + } + } + default: + panic(fmt.Errorf("unknown host function type: %s", op.HostFunction.Type)) + } + + return details, nil +} + +func (o *LedgerOperation) ExtendFootprintTtlDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + op, ok := o.Operation.Body.GetExtendFootprintTtlOp() + if !ok { + return details, fmt.Errorf("could not access ExtendFootprintTtl info for this operation (index %d)", o.OperationIndex) + } + + details["type"] = "extend_footprint_ttl" + details["extend_to"] = int32(op.ExtendTo) + + details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + details["contract_id"] = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + details["contract_code_hash"] = contractCodeHash + } + + return details, nil +} +func (o *LedgerOperation) RestoreFootprintDetails() (map[string]interface{}, error) { + details := map[string]interface{}{} + _, ok := o.Operation.Body.GetRestoreFootprintOp() + if !ok { + return details, fmt.Errorf("could not access InvokeHostFunction info for this operation (index %d)", o.OperationIndex) + } + + details["type"] = "restore_footprint" + + details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + details["contract_id"] = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + details["contract_code_hash"] = contractCodeHash + } + + return details, nil +} + +func (o *LedgerOperation) serializeParameters(args []xdr.ScVal) ([]map[string]string, []map[string]string) { + params := make([]map[string]string, 0, len(args)) + paramsDecoded := make([]map[string]string, 0, len(args)) + + for _, param := range args { + serializedParam := map[string]string{} + serializedParam["value"] = "n/a" + serializedParam["type"] = "n/a" + + serializedParamDecoded := map[string]string{} + serializedParamDecoded["value"] = "n/a" + serializedParamDecoded["type"] = "n/a" + + if scValTypeName, ok := param.ArmForSwitch(int32(param.Type)); ok { + serializedParam["type"] = scValTypeName + serializedParamDecoded["type"] = scValTypeName + if raw, err := param.MarshalBinary(); err == nil { + serializedParam["value"] = base64.StdEncoding.EncodeToString(raw) + serializedParamDecoded["value"] = param.String() + } + } + params = append(params, serializedParam) + paramsDecoded = append(paramsDecoded, serializedParamDecoded) + } + + return params, paramsDecoded +} + +func (o *LedgerOperation) parseAssetBalanceChangesFromContractEvents() ([]map[string]interface{}, error) { + balanceChanges := []map[string]interface{}{} + + diagnosticEvents, err := o.Transaction.GetDiagnosticEvents() + if err != nil { + // this operation in this context must be an InvokeHostFunctionOp, therefore V3Meta should be present + // as it's in same soroban model, so if any err, it's real, + return nil, err + } + + for _, contractEvent := range o.filterEvents(diagnosticEvents) { + // Parse the xdr contract event to contractevents.StellarAssetContractEvent model + + // has some convenience like to/from attributes are expressed in strkey format for accounts(G...) and contracts(C...) + if sacEvent, err := contractevents.NewStellarAssetContractEvent(&contractEvent, o.NetworkPassphrase); err == nil { + switch sacEvent.GetType() { + case contractevents.EventTypeTransfer: + transferEvt := sacEvent.(*contractevents.TransferEvent) + balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry(transferEvt.From, transferEvt.To, transferEvt.Amount, transferEvt.Asset, "transfer")) + case contractevents.EventTypeMint: + mintEvt := sacEvent.(*contractevents.MintEvent) + balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry("", mintEvt.To, mintEvt.Amount, mintEvt.Asset, "mint")) + case contractevents.EventTypeClawback: + clawbackEvt := sacEvent.(*contractevents.ClawbackEvent) + balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry(clawbackEvt.From, "", clawbackEvt.Amount, clawbackEvt.Asset, "clawback")) + case contractevents.EventTypeBurn: + burnEvt := sacEvent.(*contractevents.BurnEvent) + balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry(burnEvt.From, "", burnEvt.Amount, burnEvt.Asset, "burn")) + } + } + } + + return balanceChanges, nil +} + +func (o *LedgerOperation) filterEvents(diagnosticEvents []xdr.DiagnosticEvent) []xdr.ContractEvent { + var filtered []xdr.ContractEvent + for _, diagnosticEvent := range diagnosticEvents { + if !diagnosticEvent.InSuccessfulContractCall || diagnosticEvent.Event.Type != xdr.ContractEventTypeContract { + continue + } + filtered = append(filtered, diagnosticEvent.Event) + } + return filtered +} + +// fromAccount - strkey format of contract or address +// toAccount - strkey format of contract or address, or nillable +// amountChanged - absolute value that asset balance changed +// asset - the fully qualified issuer:code for asset that had balance change +// changeType - the type of source sac event that triggered this change +// +// return - a balance changed record expressed as map of key/value's +func (o *LedgerOperation) createSACBalanceChangeEntry(fromAccount string, toAccount string, amountChanged xdr.Int128Parts, asset xdr.Asset, changeType string) map[string]interface{} { + balanceChange := map[string]interface{}{} + + if fromAccount != "" { + balanceChange["from"] = fromAccount + } + if toAccount != "" { + balanceChange["to"] = toAccount + } + + balanceChange["type"] = changeType + balanceChange["amount"] = amount.String128(amountChanged) + o.addAssetDetails(balanceChange, asset, "") + return balanceChange +} + +// addAssetDetails sets the details for `a` on `result` using keys with `prefix` +func (o *LedgerOperation) addAssetDetails(result map[string]interface{}, a xdr.Asset, prefix string) error { + var ( + assetType string + code string + issuer string + ) + err := a.Extract(&assetType, &code, &issuer) + if err != nil { + err = fmt.Errorf("xdr.Asset.Extract error: %w", err) + return err + } + result[prefix+"asset_type"] = assetType + + if a.Type == xdr.AssetTypeAssetTypeNative { + return nil + } + + result[prefix+"asset_code"] = code + result[prefix+"asset_issuer"] = issuer + return nil +} + +func (o *LedgerOperation) switchContractIdPreimageType(contractIdPreimage xdr.ContractIdPreimage) map[string]interface{} { + details := map[string]interface{}{} + + switch contractIdPreimage.Type { + case xdr.ContractIdPreimageTypeContractIdPreimageFromAddress: + fromAddress := contractIdPreimage.MustFromAddress() + address, err := fromAddress.Address.String() + if err != nil { + panic(fmt.Errorf("error obtaining address for: %s", contractIdPreimage.Type)) + } + details["from"] = "address" + details["address"] = address + case xdr.ContractIdPreimageTypeContractIdPreimageFromAsset: + details["from"] = "asset" + details["asset"] = contractIdPreimage.MustFromAsset().StringCanonical() + default: + panic(fmt.Errorf("unknown contract id type: %s", contractIdPreimage.Type)) + } + + return details +} + +func (o *LedgerOperation) ConvertStroopValueToReal(input xdr.Int64) float64 { + output, _ := big.NewRat(int64(input), int64(10000000)).Float64() + return output +} + +func (o *LedgerOperation) FormatPrefix(p string) string { + if p != "" { + p += "_" + } + return p +} + +func (o *LedgerOperation) FarmHashAsset(assetCode, assetIssuer, assetType string) int64 { + asset := fmt.Sprintf("%s%s%s", assetCode, assetIssuer, assetType) + hash := farm.Fingerprint64([]byte(asset)) + + return int64(hash) +} + +// Path is a representation of an asset without an ID that forms part of a path in a path payment +type Path struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` +} + +func (o *LedgerOperation) TransformPath(initialPath []xdr.Asset) []Path { + if len(initialPath) == 0 { + return nil + } + var path = make([]Path, 0) + for _, pathAsset := range initialPath { + var assetType, code, issuer string + err := pathAsset.Extract(&assetType, &code, &issuer) + if err != nil { + return nil + } + + path = append(path, Path{ + AssetType: assetType, + AssetIssuer: issuer, + AssetCode: code, + }) + } + return path +} + +type Price struct { + Numerator int32 `json:"n"` + Denominator int32 `json:"d"` +} + +func (o *LedgerOperation) PoolIDToString(id xdr.PoolId) string { + return xdr.Hash(id).HexString() +} + +type Claimant struct { + Destination string `json:"destination"` + Predicate xdr.ClaimPredicate `json:"predicate"` +} + +func (o *LedgerOperation) TransformClaimants(claimants []xdr.Claimant) []Claimant { + var transformed []Claimant + for _, c := range claimants { + switch c.Type { + case 0: + transformed = append(transformed, Claimant{ + Destination: c.V0.Destination.Address(), + Predicate: c.V0.Predicate, + }) + } + } + return transformed +} + +type SponsorshipOutput struct { + Operation xdr.Operation + OperationIndex uint32 +} + +type LiquidityPoolDelta struct { + ReserveA xdr.Int64 + ReserveB xdr.Int64 + TotalPoolShares xdr.Int64 +} diff --git a/ingest/ledger_operation_test.go b/ingest/ledger_operation_test.go new file mode 100644 index 0000000000..18c4760219 --- /dev/null +++ b/ingest/ledger_operation_test.go @@ -0,0 +1,1474 @@ +package ingest + +import ( + "testing" + + "github.com/stellar/go/xdr" + "github.com/stretchr/testify/assert" +) + +func TestOperation(t *testing.T) { + testOutput := resultTestOutput() + for i, op := range operationTestInput() { + ledgerOperation := LedgerOperation{ + OperationIndex: int32(i), + Operation: op, + Transaction: transactionTestInput(), + NetworkPassphrase: "", + } + + result, err := ledgerOperation.OperationDetails() + assert.Equal(t, testOutput[i].err, err) + assert.Equal(t, testOutput[i].result, result) + } +} + +func ledgerTestInput() (lcm xdr.LedgerCloseMeta) { + lcm = xdr.LedgerCloseMeta{ + V: 1, + V1: &xdr.LedgerCloseMetaV1{ + LedgerHeader: xdr.LedgerHeaderHistoryEntry{ + Header: xdr.LedgerHeader{ + LedgerSeq: 30578981, + LedgerVersion: 22, + }, + }, + }, + } + + return lcm +} + +func transactionTestInput() *LedgerTransaction { + testAccountAddress := "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA" + testAccountID, _ := xdr.AddressToAccountId(testAccountAddress) + dummyBool := true + + ed25519 := xdr.Uint256([32]byte{0x11, 0x22, 0x33}) + muxedAccount := xdr.MuxedAccount{ + Type: 256, + Ed25519: &ed25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: xdr.Uint64(123), + Ed25519: ed25519, + }, + } + + memoText := "test memo" + minSeqNum := xdr.SequenceNumber(123) + + transaction := &LedgerTransaction{ + Index: 1, + Envelope: xdr.TransactionEnvelope{ + Type: xdr.EnvelopeTypeEnvelopeTypeTx, + V1: &xdr.TransactionV1Envelope{ + Signatures: []xdr.DecoratedSignature{ + { + Signature: []byte{0x11, 0x22}, + }, + }, + Tx: xdr.Transaction{ + SourceAccount: muxedAccount, + SeqNum: xdr.SequenceNumber(30578981), + Fee: xdr.Uint32(4560), + Operations: []xdr.Operation{ + { + SourceAccount: &muxedAccount, + Body: xdr.OperationBody{}, + }, + { + SourceAccount: &muxedAccount, + Body: xdr.OperationBody{}, + }, + { + SourceAccount: &muxedAccount, + Body: xdr.OperationBody{}, + }, + }, + Memo: xdr.Memo{ + Type: xdr.MemoTypeMemoText, + Text: &memoText, + }, + Cond: xdr.Preconditions{ + Type: 2, + V2: &xdr.PreconditionsV2{ + TimeBounds: &xdr.TimeBounds{ + MinTime: xdr.TimePoint(1), + MaxTime: xdr.TimePoint(10), + }, + LedgerBounds: &xdr.LedgerBounds{ + MinLedger: 2, + MaxLedger: 20, + }, + MinSeqNum: &minSeqNum, + MinSeqAge: 456, + MinSeqLedgerGap: 789, + }, + }, + Ext: xdr.TransactionExt{ + V: 1, + SorobanData: &xdr.SorobanTransactionData{ + Resources: xdr.SorobanResources{ + Instructions: 123, + ReadBytes: 456, + WriteBytes: 789, + Footprint: xdr.LedgerFootprint{ + ReadOnly: []xdr.LedgerKey{ + { + Type: 6, + ContractData: &xdr.LedgerKeyContractData{ + Contract: xdr.ScAddress{ + Type: 1, + ContractId: &xdr.Hash{0x12, 0x34}, + }, + Key: xdr.ScVal{ + Type: 0, + B: &dummyBool, + }, + }, + }, + }, + }, + }, + ResourceFee: 1234, + }, + }, + }, + }, + }, + Result: xdr.TransactionResultPair{ + TransactionHash: xdr.Hash{0x11, 0x22, 0x33}, + Result: xdr.TransactionResult{ + FeeCharged: xdr.Int64(789), + Result: xdr.TransactionResultResult{ + Code: 0, + Results: &[]xdr.OperationResult{ + {}, + {}, + {}, + { + Code: 0, + Tr: &xdr.OperationResultTr{ + Type: 2, + PathPaymentStrictReceiveResult: &xdr.PathPaymentStrictReceiveResult{ + Code: 0, + Success: &xdr.PathPaymentStrictReceiveResultSuccess{}, + NoIssuer: &xdr.Asset{}, + }, + }, + }, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + { + Code: 0, + Tr: &xdr.OperationResultTr{ + Type: 13, + PathPaymentStrictSendResult: &xdr.PathPaymentStrictSendResult{ + Code: 0, + Success: &xdr.PathPaymentStrictSendResultSuccess{ + Last: xdr.SimplePaymentResult{ + Amount: 640000000, + }, + }, + NoIssuer: &xdr.Asset{}, + }, + }, + }, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + { + Code: 0, + Tr: &xdr.OperationResultTr{ + Type: 22, + LiquidityPoolDepositResult: &xdr.LiquidityPoolDepositResult{ + Code: 0, + }, + }, + }, + { + Code: 0, + Tr: &xdr.OperationResultTr{ + Type: 23, + LiquidityPoolWithdrawResult: &xdr.LiquidityPoolWithdrawResult{ + Code: 0, + }, + }, + }, + {}, + }, + }, + }, + }, + FeeChanges: xdr.LedgerEntryChanges{ + { + Type: xdr.LedgerEntryChangeTypeLedgerEntryState, + State: &xdr.LedgerEntry{ + Data: xdr.LedgerEntryData{ + Type: xdr.LedgerEntryTypeAccount, + Account: &xdr.AccountEntry{ + AccountId: xdr.AccountId{ + Type: 0, + Ed25519: &ed25519, + }, + Balance: 1000, + }, + }, + }, + }, + {}, + }, + UnsafeMeta: xdr.TransactionMeta{ + V: 3, + V3: &xdr.TransactionMetaV3{ + TxChangesAfter: xdr.LedgerEntryChanges{}, + SorobanMeta: &xdr.SorobanTransactionMeta{ + Ext: xdr.SorobanTransactionMetaExt{ + V: 1, + V1: &xdr.SorobanTransactionMetaExtV1{ + TotalNonRefundableResourceFeeCharged: 321, + TotalRefundableResourceFeeCharged: 123, + RentFeeCharged: 456, + }, + }, + }, + Operations: []xdr.OperationMeta{ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + { + Changes: []xdr.LedgerEntryChange{ + { + Type: 3, + State: &xdr.LedgerEntry{ + Data: xdr.LedgerEntryData{ + Type: 5, + LiquidityPool: &xdr.LiquidityPoolEntry{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + Body: xdr.LiquidityPoolEntryBody{ + Type: 0, + ConstantProduct: &xdr.LiquidityPoolEntryConstantProduct{ + Params: xdr.LiquidityPoolConstantProductParameters{ + AssetA: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + AssetB: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + Fee: 1, + }, + ReserveA: 1, + ReserveB: 1, + TotalPoolShares: 1, + PoolSharesTrustLineCount: 1, + }, + }, + }, + }, + }, + }, + { + Type: 1, + Updated: &xdr.LedgerEntry{ + Data: xdr.LedgerEntryData{ + Type: 5, + LiquidityPool: &xdr.LiquidityPoolEntry{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + Body: xdr.LiquidityPoolEntryBody{ + Type: 0, + ConstantProduct: &xdr.LiquidityPoolEntryConstantProduct{ + Params: xdr.LiquidityPoolConstantProductParameters{ + AssetA: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + AssetB: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + Fee: 1, + }, + ReserveA: 2, + ReserveB: 2, + TotalPoolShares: 2, + PoolSharesTrustLineCount: 1, + }, + }, + }, + }, + }, + }, + }, + }, + { + Changes: []xdr.LedgerEntryChange{ + { + Type: 3, + State: &xdr.LedgerEntry{ + Data: xdr.LedgerEntryData{ + Type: 5, + LiquidityPool: &xdr.LiquidityPoolEntry{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + Body: xdr.LiquidityPoolEntryBody{ + Type: 0, + ConstantProduct: &xdr.LiquidityPoolEntryConstantProduct{ + Params: xdr.LiquidityPoolConstantProductParameters{ + AssetA: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + AssetB: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + Fee: 1, + }, + ReserveA: 1, + ReserveB: 1, + TotalPoolShares: 1, + PoolSharesTrustLineCount: 1, + }, + }, + }, + }, + }, + }, + { + Type: 1, + Updated: &xdr.LedgerEntry{ + Data: xdr.LedgerEntryData{ + Type: 5, + LiquidityPool: &xdr.LiquidityPoolEntry{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + Body: xdr.LiquidityPoolEntryBody{ + Type: 0, + ConstantProduct: &xdr.LiquidityPoolEntryConstantProduct{ + Params: xdr.LiquidityPoolConstantProductParameters{ + AssetA: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + AssetB: xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + }, + Fee: 1, + }, + ReserveA: 2, + ReserveB: 2, + TotalPoolShares: 2, + PoolSharesTrustLineCount: 1, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + LedgerVersion: 22, + Ledger: ledgerTestInput(), + Hash: xdr.Hash{}, + } + + return transaction +} + +type testOutput struct { + err error + result map[string]interface{} +} + +func operationTestInput() []xdr.Operation { + testAccountAddress := "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA" + testAccountID, _ := xdr.AddressToAccountId(testAccountAddress) + testAccountMuxed := testAccountID.ToMuxedAccount() + + sourceAccountAddress := "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN" + sourceAccountID, _ := xdr.AddressToAccountId(sourceAccountAddress) + sourceAccountMuxed := sourceAccountID.ToMuxedAccount() + + usdtAsset := xdr.Asset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x44, 0x54}), + Issuer: testAccountID, + }, + } + assetCode, _ := usdtAsset.ToAssetCode("USDT") + nativeAsset := xdr.MustNewNativeAsset() + + clearFlags := xdr.Uint32(3) + setFlags := xdr.Uint32(4) + masterWeight := xdr.Uint32(3) + lowThresh := xdr.Uint32(1) + medThresh := xdr.Uint32(3) + highThresh := xdr.Uint32(5) + homeDomain := xdr.String32("2019=DRA;n-test") + signerKey, _ := xdr.NewSignerKey(xdr.SignerKeyTypeSignerKeyTypeEd25519, xdr.Uint256([32]byte{})) + signer := xdr.Signer{ + Key: signerKey, + Weight: xdr.Uint32(1), + } + + usdtChangeTrustAsset := xdr.ChangeTrustAsset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x53, 0x44}), + Issuer: testAccountID, + }, + } + + usdtLiquidityPoolShare := xdr.ChangeTrustAsset{ + Type: xdr.AssetTypeAssetTypePoolShare, + LiquidityPool: &xdr.LiquidityPoolParameters{ + Type: xdr.LiquidityPoolTypeLiquidityPoolConstantProduct, + ConstantProduct: &xdr.LiquidityPoolConstantProductParameters{ + AssetA: nativeAsset, + AssetB: usdtAsset, + Fee: 30, + }, + }, + } + + dataValue := xdr.DataValue([]byte{0x76, 0x61, 0x6c, 0x75, 0x65}) + + testClaimant := xdr.Claimant{ + Type: xdr.ClaimantTypeClaimantTypeV0, + V0: &xdr.ClaimantV0{ + Destination: testAccountID, + Predicate: xdr.ClaimPredicate{ + Type: xdr.ClaimPredicateTypeClaimPredicateUnconditional, + }, + }, + } + + claimableBalance := xdr.ClaimableBalanceId{ + Type: xdr.ClaimableBalanceIdTypeClaimableBalanceIdTypeV0, + V0: &xdr.Hash{1, 2, 3, 4, 5, 6, 7, 8, 9}, + } + + contractHash := xdr.Hash{0x12, 0x34, 0x56, 0x78} + salt := [32]byte{0x12, 0x34, 0x56} + wasm := []byte{0x12, 0x34} + dummyBool := true + + operation := []xdr.Operation{ + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeCreateAccount, + CreateAccountOp: &xdr.CreateAccountOp{ + StartingBalance: 25000000, + Destination: testAccountID, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: testAccountMuxed, + Asset: usdtAsset, + Amount: 350000000, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypePayment, + PaymentOp: &xdr.PaymentOp{ + Destination: testAccountMuxed, + Asset: nativeAsset, + Amount: 350000000, + }, + }, + }, + { + SourceAccount: &sourceAccountMuxed, + Body: xdr.OperationBody{ + Type: xdr.OperationTypePathPaymentStrictReceive, + PathPaymentStrictReceiveOp: &xdr.PathPaymentStrictReceiveOp{ + SendAsset: nativeAsset, + SendMax: 8951495900, + Destination: testAccountMuxed, + DestAsset: nativeAsset, + DestAmount: 8951495900, + Path: []xdr.Asset{usdtAsset}, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeManageSellOffer, + ManageSellOfferOp: &xdr.ManageSellOfferOp{ + Selling: usdtAsset, + Buying: nativeAsset, + Amount: 765860000, + Price: xdr.Price{ + N: 128523, + D: 250000, + }, + OfferId: 0, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeCreatePassiveSellOffer, + CreatePassiveSellOfferOp: &xdr.CreatePassiveSellOfferOp{ + Selling: nativeAsset, + Buying: usdtAsset, + Amount: 631595000, + Price: xdr.Price{ + N: 99583200, + D: 1257990000, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeSetOptions, + SetOptionsOp: &xdr.SetOptionsOp{ + InflationDest: &testAccountID, + ClearFlags: &clearFlags, + SetFlags: &setFlags, + MasterWeight: &masterWeight, + LowThreshold: &lowThresh, + MedThreshold: &medThresh, + HighThreshold: &highThresh, + HomeDomain: &homeDomain, + Signer: &signer, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeChangeTrust, + ChangeTrustOp: &xdr.ChangeTrustOp{ + Line: usdtChangeTrustAsset, + Limit: xdr.Int64(500000000000000000), + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeChangeTrust, + ChangeTrustOp: &xdr.ChangeTrustOp{ + Line: usdtLiquidityPoolShare, + Limit: xdr.Int64(500000000000000000), + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeAllowTrust, + AllowTrustOp: &xdr.AllowTrustOp{ + Trustor: testAccountID, + Asset: assetCode, + Authorize: xdr.Uint32(1), + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeAccountMerge, + Destination: &testAccountMuxed, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInflation, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeManageData, + ManageDataOp: &xdr.ManageDataOp{ + DataName: "test", + DataValue: &dataValue, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeBumpSequence, + BumpSequenceOp: &xdr.BumpSequenceOp{ + BumpTo: xdr.SequenceNumber(100), + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeManageBuyOffer, + ManageBuyOfferOp: &xdr.ManageBuyOfferOp{ + Selling: usdtAsset, + Buying: nativeAsset, + BuyAmount: 7654501001, + Price: xdr.Price{ + N: 635863285, + D: 1818402817, + }, + OfferId: 100, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypePathPaymentStrictSend, + PathPaymentStrictSendOp: &xdr.PathPaymentStrictSendOp{ + SendAsset: nativeAsset, + SendAmount: 1598182, + Destination: testAccountMuxed, + DestAsset: nativeAsset, + DestMin: 4280460538, + Path: []xdr.Asset{usdtAsset}, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeCreateClaimableBalance, + CreateClaimableBalanceOp: &xdr.CreateClaimableBalanceOp{ + Asset: usdtAsset, + Amount: 1234567890000, + Claimants: []xdr.Claimant{testClaimant}, + }, + }, + }, + { + SourceAccount: &sourceAccountMuxed, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeClaimClaimableBalance, + ClaimClaimableBalanceOp: &xdr.ClaimClaimableBalanceOp{ + BalanceId: claimableBalance, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeBeginSponsoringFutureReserves, + BeginSponsoringFutureReservesOp: &xdr.BeginSponsoringFutureReservesOp{ + SponsoredId: testAccountID, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipSigner, + Signer: &xdr.RevokeSponsorshipOpSigner{ + AccountId: testAccountID, + SignerKey: signer.Key, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry, + LedgerKey: &xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeAccount, + Account: &xdr.LedgerKeyAccount{ + AccountId: testAccountID, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry, + LedgerKey: &xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeClaimableBalance, + ClaimableBalance: &xdr.LedgerKeyClaimableBalance{ + BalanceId: claimableBalance, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry, + LedgerKey: &xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeData, + Data: &xdr.LedgerKeyData{ + AccountId: testAccountID, + DataName: "test", + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry, + LedgerKey: &xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeOffer, + Offer: &xdr.LedgerKeyOffer{ + SellerId: testAccountID, + OfferId: 100, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry, + LedgerKey: &xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeTrustline, + TrustLine: &xdr.LedgerKeyTrustLine{ + AccountId: testAccountID, + Asset: xdr.TrustLineAsset{ + Type: xdr.AssetTypeAssetTypeCreditAlphanum4, + AlphaNum4: &xdr.AlphaNum4{ + AssetCode: xdr.AssetCode4([4]byte{0x55, 0x53, 0x54, 0x54}), + Issuer: testAccountID, + }, + }, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRevokeSponsorship, + RevokeSponsorshipOp: &xdr.RevokeSponsorshipOp{ + Type: xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry, + LedgerKey: &xdr.LedgerKey{ + Type: xdr.LedgerEntryTypeLiquidityPool, + LiquidityPool: &xdr.LedgerKeyLiquidityPool{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeClawback, + ClawbackOp: &xdr.ClawbackOp{ + Asset: usdtAsset, + From: testAccountMuxed, + Amount: 1598182, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeClawbackClaimableBalance, + ClawbackClaimableBalanceOp: &xdr.ClawbackClaimableBalanceOp{ + BalanceId: claimableBalance, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeSetTrustLineFlags, + SetTrustLineFlagsOp: &xdr.SetTrustLineFlagsOp{ + Trustor: testAccountID, + Asset: usdtAsset, + SetFlags: setFlags, + ClearFlags: clearFlags, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeLiquidityPoolDeposit, + LiquidityPoolDepositOp: &xdr.LiquidityPoolDepositOp{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + MaxAmountA: 1000, + MaxAmountB: 100, + MinPrice: xdr.Price{ + N: 1, + D: 1000000, + }, + MaxPrice: xdr.Price{ + N: 1000000, + D: 1, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeLiquidityPoolWithdraw, + LiquidityPoolWithdrawOp: &xdr.LiquidityPoolWithdrawOp{ + LiquidityPoolId: xdr.PoolId{1, 2, 3, 4, 5, 6, 7, 8, 9}, + Amount: 4, + MinAmountA: 1, + MinAmountB: 1, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + InvokeHostFunctionOp: &xdr.InvokeHostFunctionOp{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract, + InvokeContract: &xdr.InvokeContractArgs{ + ContractAddress: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &contractHash, + }, + FunctionName: "test", + Args: []xdr.ScVal{}, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + InvokeHostFunctionOp: &xdr.InvokeHostFunctionOp{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeCreateContract, + CreateContract: &xdr.CreateContractArgs{ + ContractIdPreimage: xdr.ContractIdPreimage{ + Type: xdr.ContractIdPreimageTypeContractIdPreimageFromAddress, + FromAddress: &xdr.ContractIdPreimageFromAddress{ + Address: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &contractHash, + }, + Salt: salt, + }, + }, + Executable: xdr.ContractExecutable{}, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + InvokeHostFunctionOp: &xdr.InvokeHostFunctionOp{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeCreateContract, + CreateContract: &xdr.CreateContractArgs{ + ContractIdPreimage: xdr.ContractIdPreimage{ + Type: xdr.ContractIdPreimageTypeContractIdPreimageFromAsset, + FromAsset: &usdtAsset, + }, + Executable: xdr.ContractExecutable{}, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + InvokeHostFunctionOp: &xdr.InvokeHostFunctionOp{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeCreateContractV2, + CreateContractV2: &xdr.CreateContractArgsV2{ + ContractIdPreimage: xdr.ContractIdPreimage{ + Type: xdr.ContractIdPreimageTypeContractIdPreimageFromAsset, + FromAsset: &usdtAsset, + }, + Executable: xdr.ContractExecutable{}, + ConstructorArgs: []xdr.ScVal{ + { + Type: xdr.ScValTypeScvBool, + B: &dummyBool, + }, + }, + }, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + InvokeHostFunctionOp: &xdr.InvokeHostFunctionOp{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeUploadContractWasm, + Wasm: &wasm, + }, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeExtendFootprintTtl, + ExtendFootprintTtlOp: &xdr.ExtendFootprintTtlOp{ + Ext: xdr.ExtensionPoint{ + V: 0, + }, + ExtendTo: 1234, + }, + }, + }, + { + SourceAccount: nil, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeRestoreFootprint, + RestoreFootprintOp: &xdr.RestoreFootprintOp{ + Ext: xdr.ExtensionPoint{ + V: 0, + }, + }, + }, + }, + } + + return operation +} + +func resultTestOutput() []testOutput { + output := []testOutput{ + { + err: nil, + result: map[string]interface{}{ + "account": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "funder": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "funder_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "funder_muxed_id": uint64(123), + "starting_balance": 2.5}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": float64(35), + "asset_code": "USDT", + "asset_id": int64(-8205667356306085451), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", + "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "from_muxed_id": uint64(123), + "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": float64(35), + "asset_id": int64(-5706705804583548011), + "asset_type": "native", + "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "from_muxed_id": uint64(123), + "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": 895.14959, + "asset_id": int64(-5706705804583548011), + "asset_type": "native", + "from": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", + "path": []Path{Path{AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4"}}, + "source_amount": float64(0), + "source_asset_id": int64(-5706705804583548011), + "source_asset_type": "native", + "source_max": 895.14959, + "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": float64(76.586), + "buying_asset_id": int64(-5706705804583548011), + "buying_asset_type": "native", + "offer_id": int64(0), + "price": 0.514092, + "price_r": Price{Numerator: 128523, + Denominator: 250000}, + "selling_asset_code": "USDT", + "selling_asset_id": int64(-8205667356306085451), + "selling_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "selling_asset_type": "credit_alphanum4"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": float64(63.1595), + "buying_asset_code": "USDT", + "buying_asset_id": int64(-8205667356306085451), + "buying_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "buying_asset_type": "credit_alphanum4", + "price": 0.0791606, + "price_r": Price{Numerator: 99583200, + Denominator: 1257990000}, + "selling_asset_id": int64(-5706705804583548011), + "selling_asset_type": "native"}, + }, + { + err: nil, + result: map[string]interface{}{ + "clear_flags": []int32{1, + 2}, + "clear_flags_s": []string{"auth_required", + "auth_revocable"}, + "high_threshold": uint32(5), + "home_domain": "2019=DRA;n-test", + "inflation_dest": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "low_threshold": uint32(1), + "master_key_weight": uint32(3), + "med_threshold": uint32(3), + "set_flags": []int32{4}, + "set_flags_s": []string{"auth_immutable"}, + "signer_key": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + "signer_weight": uint32(1)}, + }, + { + err: nil, + result: map[string]interface{}{ + "asset_code": "USSD", + "asset_id": int64(6690054458235693884), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", + "limit": 5e+10, + "trustee": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "trustor": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "trustor_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "trustor_muxed_id": uint64(123)}, + }, + { + err: nil, + result: map[string]interface{}{ + "asset_type": "liquidity_pool_shares", + "limit": 5e+10, + "liquidity_pool_id": "1c261d6c75930204a73b480c3020ab525e9be48ce93de6194cf69fb06f07452d", + "trustor": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "trustor_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "trustor_muxed_id": uint64(123)}, + }, + { + err: nil, + result: map[string]interface{}{ + "asset_code": "USDT", + "asset_id": int64(8181787832768848499), + "asset_issuer": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "asset_type": "credit_alphanum4", + "authorize": true, + "trustee": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "trustee_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "trustee_muxed_id": uint64(123), + "trustor": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "account": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "account_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "account_muxed_id": uint64(123), + "into": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{}, + }, + { + err: nil, + result: map[string]interface{}{ + "name": "test", + "value": "dmFsdWU=", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "bump_to": "100", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": float64(765.4501001), + "buying_asset_id": int64(-5706705804583548011), + "buying_asset_type": "native", + "offer_id": int64(100), + "price": 0.3496823, + "price_r": Price{Numerator: 635863285, + Denominator: 1818402817}, + "selling_asset_code": "USDT", + "selling_asset_id": int64(-8205667356306085451), + "selling_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "selling_asset_type": "credit_alphanum4"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": float64(64), + "asset_id": int64(-5706705804583548011), + "asset_type": "native", + "destination_min": "428.0460538", + "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + "from_muxed_id": uint64(123), + "path": []Path{{AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4"}}, + "source_amount": 0.1598182, + "source_asset_id": int64(-5706705804583548011), + "source_asset_type": "native", + "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": 123456.789, + "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "claimants": []Claimant{{Destination: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + Predicate: xdr.ClaimPredicate{Type: 0, + AndPredicates: (*[]xdr.ClaimPredicate)(nil), + OrPredicates: (*[]xdr.ClaimPredicate)(nil), + NotPredicate: (**xdr.ClaimPredicate)(nil), + AbsBefore: (*xdr.Int64)(nil), + RelBefore: (*xdr.Int64)(nil)}}}}, + }, + { + err: nil, + result: map[string]interface{}{ + "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", + "claimant": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN"}, + }, + { + err: nil, + result: map[string]interface{}{ + "sponsored_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "signer_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "signer_key": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"}, + }, + { + err: nil, + result: map[string]interface{}{ + "account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "claimable_balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000"}, + }, + { + err: nil, + result: map[string]interface{}{ + "data_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "data_name": "test"}, + }, + { + err: nil, + result: map[string]interface{}{ + "offer_id": int64(100)}, + }, + { + err: nil, + result: map[string]interface{}{ + "trustline_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "trustline_asset": "USTT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000"}, + }, + { + err: nil, + result: map[string]interface{}{ + "amount": 0.1598182, + "asset_code": "USDT", + "asset_id": int64(-8205667356306085451), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", + "from": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + }, + { + err: nil, + result: map[string]interface{}{ + "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000"}, + }, + { + err: nil, + result: map[string]interface{}{ + "asset_code": "USDT", + "asset_id": int64(-8205667356306085451), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", + "clear_flags": []int32{1, 2}, + "clear_flags_s": []string{"authorized", "authorized_to_maintain_liabilities"}, + "set_flags": []int32{4}, + "set_flags_s": []string{"clawback_enabled"}, + "trustor": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}}, + { + err: nil, + result: map[string]interface{}{ + "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000", + "max_price": 1e+06, + "max_price_r": Price{Numerator: 1000000, Denominator: 1}, + "min_price": 1e-06, + "min_price_r": Price{Numerator: 1, Denominator: 1000000}, + "reserve_a_asset_code": "USDT", + "reserve_a_asset_id": int64(-8205667356306085451), + "reserve_a_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "reserve_a_asset_type": "credit_alphanum4", + "reserve_a_deposit_amount": 1e-07, + "reserve_a_max_amount": 0.0001, + "reserve_b_asset_code": "USDT", + "reserve_b_asset_id": int64(-8205667356306085451), + "reserve_b_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "reserve_b_asset_type": "credit_alphanum4", + "reserve_b_deposit_amount": 1e-07, + "reserve_b_max_amount": 1e-05, + "shares_received": 1e-07, + }, + }, + { + err: nil, + result: map[string]interface{}{ + "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000", + "reserve_a_asset_code": "USDT", + "reserve_a_asset_id": int64(-8205667356306085451), + "reserve_a_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "reserve_a_asset_type": "credit_alphanum4", + "reserve_a_min_amount": 1e-07, + "reserve_a_withdraw_amount": -1e-07, + "reserve_b_asset_code": "USDT", + "reserve_b_asset_id": int64(-8205667356306085451), + "reserve_b_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "reserve_b_asset_type": "credit_alphanum4", + "reserve_b_min_amount": 1e-07, + "reserve_b_withdraw_amount": -1e-07, + "shares": 4e-07, + }, + }, + { + err: nil, + result: map[string]interface{}{ + "asset_balance_changes": []map[string]interface{}{}, + "contract_id": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", + "function": "HostFunctionTypeHostFunctionTypeInvokeContract", + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "parameters": []map[string]string{ + { + "type": "Address", + "value": "AAAAEgAAAAESNFZ4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + }, + { + "type": "Sym", + "value": "AAAADwAAAAR0ZXN0", + }, + }, + "parameters_decoded": []map[string]string{ + { + "type": "Address", + "value": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", + }, + { + "type": "Sym", + "value": "test", + }, + }, + "type": "invoke_contract", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "address": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", + "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + "from": "address", + "function": "HostFunctionTypeHostFunctionTypeCreateContract", + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "type": "create_contract", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + "from": "asset", + "function": "HostFunctionTypeHostFunctionTypeCreateContract", + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "type": "create_contract", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + "from": "asset", + "function": "HostFunctionTypeHostFunctionTypeCreateContractV2", + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "parameters": []map[string]string{ + { + "type": "B", + "value": "AAAAAAAAAAE=", + }, + }, + "parameters_decoded": []map[string]string{ + { + "type": "B", + "value": "true", + }, + }, + "type": "create_contract_v2", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "function": "HostFunctionTypeHostFunctionTypeUploadContractWasm", + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "type": "upload_wasm", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "extend_to": int32(1234), + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + "type": "extend_footprint_ttl", + }, + }, + { + err: nil, + result: map[string]interface{}{ + "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + "type": "restore_footprint", + }, + }, + } + + return output +} diff --git a/ingest/ledger_transaction.go b/ingest/ledger_transaction.go index 36f82b952f..8490163699 100644 --- a/ingest/ledger_transaction.go +++ b/ingest/ledger_transaction.go @@ -579,3 +579,163 @@ func (t *LedgerTransaction) NewMaxFee() (uint32, bool) { func (t *LedgerTransaction) Successful() bool { return t.Result.Successful() } + +func (t *LedgerTransaction) GetOperations(networkPassphrase string) []LedgerOperation { + var ledgerOperations []LedgerOperation + + for i, operation := range t.Envelope.Operations() { + ledgerOperation := LedgerOperation{ + OperationIndex: int32(i), + Operation: operation, + Transaction: t, + NetworkPassphrase: networkPassphrase, + } + ledgerOperations = append(ledgerOperations, ledgerOperation) + } + + return ledgerOperations +} + +func (t *LedgerTransaction) GetTransactionV1Envelope() (xdr.TransactionV1Envelope, bool) { + switch t.Envelope.Type { + case xdr.EnvelopeTypeEnvelopeTypeTx: + switch t.Envelope.Type { + case 2: + return *t.Envelope.V1, true + default: + return xdr.TransactionV1Envelope{}, false + } + case xdr.EnvelopeTypeEnvelopeTypeTxFeeBump: + return t.Envelope.MustFeeBump().Tx.InnerTx.MustV1(), true + default: + return xdr.TransactionV1Envelope{}, false + } +} + +func (t *LedgerTransaction) LedgerKeyHashFromTxEnvelope() []string { + var ledgerKeyHash []string + + v1Envelope, ok := t.GetTransactionV1Envelope() + if !ok { + return ledgerKeyHash + } + + for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadOnly { + ledgerKeyBase64, err := ledgerKey.MarshalBinaryBase64() + if err != nil { + panic(err) + } + if ledgerKeyBase64 != "" { + ledgerKeyHash = append(ledgerKeyHash, ledgerKeyBase64) + } + } + + for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadWrite { + ledgerKeyBase64, err := ledgerKey.MarshalBinaryBase64() + if err != nil { + panic(err) + } + if ledgerKeyBase64 != "" { + ledgerKeyHash = append(ledgerKeyHash, ledgerKeyBase64) + } + } + + return ledgerKeyHash +} + +func (t *LedgerTransaction) ContractCodeHashFromTxEnvelope() (string, bool) { + v1Envelope, ok := t.GetTransactionV1Envelope() + if !ok { + return "", false + } + for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadOnly { + contractCode, ok := t.contractCodeFromContractData(ledgerKey) + if !ok { + return "", false + } + if contractCode != "" { + return contractCode, true + } + } + + for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadWrite { + contractCode, ok := t.contractCodeFromContractData(ledgerKey) + if !ok { + return "", false + } + if contractCode != "" { + return contractCode, true + } + } + + return "", true +} + +func (t *LedgerTransaction) contractCodeFromContractData(ledgerKey xdr.LedgerKey) (string, bool) { + contractCode, ok := ledgerKey.GetContractCode() + if !ok { + return "", false + } + + codeHash, err := contractCode.Hash.MarshalBinaryBase64() + if err != nil { + panic(err) + } + + return codeHash, true +} + +func (t *LedgerTransaction) contractIdFromTxEnvelope() (string, bool) { + v1Envelope, ok := t.GetTransactionV1Envelope() + if !ok { + return "", false + } + for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadWrite { + contractId, ok := t.contractIdFromContractData(ledgerKey) + if !ok { + return "", false + } + + if contractId != "" { + return contractId, true + } + } + + for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadOnly { + contractId, ok := t.contractIdFromContractData(ledgerKey) + if !ok { + return "", false + } + + if contractId != "" { + return contractId, true + } + } + + return "", true +} + +func (t *LedgerTransaction) contractIdFromContractData(ledgerKey xdr.LedgerKey) (string, bool) { + contractData, ok := ledgerKey.GetContractData() + if !ok { + return "", false + } + contractIdHash, ok := contractData.Contract.GetContractId() + if !ok { + return "", false + } + + var contractIdByte []byte + var contractId string + var err error + contractIdByte, err = contractIdHash.MarshalBinary() + if err != nil { + panic(err) + } + contractId, err = strkey.Encode(strkey.VersionByteContract, contractIdByte) + if err != nil { + panic(err) + } + + return contractId, true +} diff --git a/xdr/hash.go b/xdr/hash.go index 2a15c18c9c..c0107163b6 100644 --- a/xdr/hash.go +++ b/xdr/hash.go @@ -1,6 +1,9 @@ package xdr -import "encoding/hex" +import ( + "encoding/base64" + "encoding/hex" +) func (h Hash) HexString() string { return hex.EncodeToString(h[:]) @@ -17,3 +20,14 @@ func (s Hash) Equals(o Hash) bool { } return true } + +// MarshalBinaryBase64 marshals XDR into a binary form and then encodes it +// using base64. +func (h Hash) MarshalBinaryBase64() (string, error) { + b, err := h.MarshalBinary() + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(b), nil +} diff --git a/xdr/operation_trace_result.go b/xdr/operation_trace_result.go new file mode 100644 index 0000000000..f5c68c7989 --- /dev/null +++ b/xdr/operation_trace_result.go @@ -0,0 +1,68 @@ +package xdr + +import "fmt" + +func (o OperationResultTr) MapOperationResultTr() (string, error) { + var operationTraceDescription string + operationType := o.Type + + switch operationType { + case OperationTypeCreateAccount: + operationTraceDescription = o.CreateAccountResult.Code.String() + case OperationTypePayment: + operationTraceDescription = o.PaymentResult.Code.String() + case OperationTypePathPaymentStrictReceive: + operationTraceDescription = o.PathPaymentStrictReceiveResult.Code.String() + case OperationTypePathPaymentStrictSend: + operationTraceDescription = o.PathPaymentStrictSendResult.Code.String() + case OperationTypeManageBuyOffer: + operationTraceDescription = o.ManageBuyOfferResult.Code.String() + case OperationTypeManageSellOffer: + operationTraceDescription = o.ManageSellOfferResult.Code.String() + case OperationTypeCreatePassiveSellOffer: + operationTraceDescription = o.CreatePassiveSellOfferResult.Code.String() + case OperationTypeSetOptions: + operationTraceDescription = o.SetOptionsResult.Code.String() + case OperationTypeChangeTrust: + operationTraceDescription = o.ChangeTrustResult.Code.String() + case OperationTypeAllowTrust: + operationTraceDescription = o.AllowTrustResult.Code.String() + case OperationTypeAccountMerge: + operationTraceDescription = o.AccountMergeResult.Code.String() + case OperationTypeInflation: + operationTraceDescription = o.InflationResult.Code.String() + case OperationTypeManageData: + operationTraceDescription = o.ManageDataResult.Code.String() + case OperationTypeBumpSequence: + operationTraceDescription = o.BumpSeqResult.Code.String() + case OperationTypeCreateClaimableBalance: + operationTraceDescription = o.CreateClaimableBalanceResult.Code.String() + case OperationTypeClaimClaimableBalance: + operationTraceDescription = o.ClaimClaimableBalanceResult.Code.String() + case OperationTypeBeginSponsoringFutureReserves: + operationTraceDescription = o.BeginSponsoringFutureReservesResult.Code.String() + case OperationTypeEndSponsoringFutureReserves: + operationTraceDescription = o.EndSponsoringFutureReservesResult.Code.String() + case OperationTypeRevokeSponsorship: + operationTraceDescription = o.RevokeSponsorshipResult.Code.String() + case OperationTypeClawback: + operationTraceDescription = o.ClawbackResult.Code.String() + case OperationTypeClawbackClaimableBalance: + operationTraceDescription = o.ClawbackClaimableBalanceResult.Code.String() + case OperationTypeSetTrustLineFlags: + operationTraceDescription = o.SetTrustLineFlagsResult.Code.String() + case OperationTypeLiquidityPoolDeposit: + operationTraceDescription = o.LiquidityPoolDepositResult.Code.String() + case OperationTypeLiquidityPoolWithdraw: + operationTraceDescription = o.LiquidityPoolWithdrawResult.Code.String() + case OperationTypeInvokeHostFunction: + operationTraceDescription = o.InvokeHostFunctionResult.Code.String() + case OperationTypeExtendFootprintTtl: + operationTraceDescription = o.ExtendFootprintTtlResult.Code.String() + case OperationTypeRestoreFootprint: + operationTraceDescription = o.RestoreFootprintResult.Code.String() + default: + return operationTraceDescription, fmt.Errorf("unknown operation type: %s", o.Type.String()) + } + return operationTraceDescription, nil +} From 3bc82f44da9be4d41bf74f14fc2fa8a78f9925e9 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Tue, 21 Jan 2025 13:50:01 -0500 Subject: [PATCH 02/12] update tests --- ingest/ledger_operation_test.go | 133 ++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 31 deletions(-) diff --git a/ingest/ledger_operation_test.go b/ingest/ledger_operation_test.go index 18c4760219..83978ee7fa 100644 --- a/ingest/ledger_operation_test.go +++ b/ingest/ledger_operation_test.go @@ -8,6 +8,34 @@ import ( ) func TestOperation(t *testing.T) { + o := LedgerOperation{ + OperationIndex: int32(0), + Operation: operationTestInput()[1], + Transaction: transactionTestInput(), + NetworkPassphrase: "", + } + + assert.Equal(t, "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", o.SourceAccount()) + assert.Equal(t, int32(1), o.Type()) + assert.Equal(t, "OperationTypePayment", o.TypeString()) + assert.Equal(t, int64(131335723340009473), o.ID()) + + var ok bool + var sourceAccountMuxed string + sourceAccountMuxed, ok = o.SourceAccountMuxed() + assert.Equal(t, true, ok) + assert.Equal(t, "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", sourceAccountMuxed) + + assert.Equal(t, "OperationResultCodeOpInner", o.OperationResultCode()) + + var err error + var operationTraceCode string + operationTraceCode, err = o.OperationTraceCode() + assert.Equal(t, nil, err) + assert.Equal(t, "PathPaymentStrictReceiveResultCodePathPaymentStrictReceiveSuccess", operationTraceCode) +} + +func TestOperationDetails(t *testing.T) { testOutput := resultTestOutput() for i, op := range operationTestInput() { ledgerOperation := LedgerOperation{ @@ -143,7 +171,17 @@ func transactionTestInput() *LedgerTransaction { Result: xdr.TransactionResultResult{ Code: 0, Results: &[]xdr.OperationResult{ - {}, + { + Code: 0, + Tr: &xdr.OperationResultTr{ + Type: 2, + PathPaymentStrictReceiveResult: &xdr.PathPaymentStrictReceiveResult{ + Code: 0, + Success: &xdr.PathPaymentStrictReceiveResultSuccess{}, + NoIssuer: &xdr.Asset{}, + }, + }, + }, {}, {}, { @@ -1086,9 +1124,13 @@ func resultTestOutput() []testOutput { "asset_id": int64(-5706705804583548011), "asset_type": "native", "from": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", - "path": []Path{Path{AssetCode: "USDT", - AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - AssetType: "credit_alphanum4"}}, + "path": []Path{ + { + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + }, + }, "source_amount": float64(0), "source_asset_id": int64(-5706705804583548011), "source_asset_type": "native", @@ -1103,8 +1145,10 @@ func resultTestOutput() []testOutput { "buying_asset_type": "native", "offer_id": int64(0), "price": 0.514092, - "price_r": Price{Numerator: 128523, - Denominator: 250000}, + "price_r": Price{ + Numerator: 128523, + Denominator: 250000, + }, "selling_asset_code": "USDT", "selling_asset_id": int64(-8205667356306085451), "selling_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", @@ -1119,8 +1163,10 @@ func resultTestOutput() []testOutput { "buying_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "buying_asset_type": "credit_alphanum4", "price": 0.0791606, - "price_r": Price{Numerator: 99583200, - Denominator: 1257990000}, + "price_r": Price{ + Numerator: 99583200, + Denominator: 1257990000, + }, "selling_asset_id": int64(-5706705804583548011), "selling_asset_type": "native"}, }, @@ -1211,8 +1257,10 @@ func resultTestOutput() []testOutput { "buying_asset_type": "native", "offer_id": int64(100), "price": 0.3496823, - "price_r": Price{Numerator: 635863285, - Denominator: 1818402817}, + "price_r": Price{ + Numerator: 635863285, + Denominator: 1818402817, + }, "selling_asset_code": "USDT", "selling_asset_id": int64(-8205667356306085451), "selling_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", @@ -1228,9 +1276,13 @@ func resultTestOutput() []testOutput { "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", "from_muxed_id": uint64(123), - "path": []Path{{AssetCode: "USDT", - AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - AssetType: "credit_alphanum4"}}, + "path": []Path{ + { + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + }, + }, "source_amount": 0.1598182, "source_asset_id": int64(-5706705804583548011), "source_asset_type": "native", @@ -1241,19 +1293,27 @@ func resultTestOutput() []testOutput { result: map[string]interface{}{ "amount": 123456.789, "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "claimants": []Claimant{{Destination: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - Predicate: xdr.ClaimPredicate{Type: 0, - AndPredicates: (*[]xdr.ClaimPredicate)(nil), - OrPredicates: (*[]xdr.ClaimPredicate)(nil), - NotPredicate: (**xdr.ClaimPredicate)(nil), - AbsBefore: (*xdr.Int64)(nil), - RelBefore: (*xdr.Int64)(nil)}}}}, + "claimants": []Claimant{ + { + Destination: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + Predicate: xdr.ClaimPredicate{ + Type: 0, + AndPredicates: (*[]xdr.ClaimPredicate)(nil), + OrPredicates: (*[]xdr.ClaimPredicate)(nil), + NotPredicate: (**xdr.ClaimPredicate)(nil), + AbsBefore: (*xdr.Int64)(nil), + RelBefore: (*xdr.Int64)(nil), + }, + }, + }, + }, }, { err: nil, result: map[string]interface{}{ "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", - "claimant": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN"}, + "claimant": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", + }, }, { err: nil, @@ -1265,39 +1325,46 @@ func resultTestOutput() []testOutput { err: nil, result: map[string]interface{}{ "signer_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "signer_key": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"}, + "signer_key": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + }, }, { err: nil, result: map[string]interface{}{ - "account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + "account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + }, }, { err: nil, result: map[string]interface{}{ - "claimable_balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000"}, + "claimable_balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", + }, }, { err: nil, result: map[string]interface{}{ "data_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "data_name": "test"}, + "data_name": "test", + }, }, { err: nil, result: map[string]interface{}{ - "offer_id": int64(100)}, + "offer_id": int64(100), + }, }, { err: nil, result: map[string]interface{}{ "trustline_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "trustline_asset": "USTT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + "trustline_asset": "USTT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + }, }, { err: nil, result: map[string]interface{}{ - "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000"}, + "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000", + }, }, { err: nil, @@ -1307,12 +1374,14 @@ func resultTestOutput() []testOutput { "asset_id": int64(-8205667356306085451), "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "asset_type": "credit_alphanum4", - "from": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + "from": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + }, }, { err: nil, result: map[string]interface{}{ - "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000"}, + "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", + }, }, { err: nil, @@ -1325,7 +1394,9 @@ func resultTestOutput() []testOutput { "clear_flags_s": []string{"authorized", "authorized_to_maintain_liabilities"}, "set_flags": []int32{4}, "set_flags_s": []string{"clawback_enabled"}, - "trustor": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}}, + "trustor": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + }, + }, { err: nil, result: map[string]interface{}{ From c68cd9a766a4b492d6469ee3c119e2935eb72818 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Thu, 23 Jan 2025 13:37:20 -0500 Subject: [PATCH 03/12] updates from comments --- ingest/ledger_operation.go | 150 ++++++++++++++++---------------- ingest/ledger_operation_test.go | 58 +++++++----- 2 files changed, 112 insertions(+), 96 deletions(-) diff --git a/ingest/ledger_operation.go b/ingest/ledger_operation.go index f59add11e2..cf7f29f799 100644 --- a/ingest/ledger_operation.go +++ b/ingest/ledger_operation.go @@ -71,10 +71,13 @@ func (o *LedgerOperation) OperationResultCode() string { func (o *LedgerOperation) OperationTraceCode() (string, error) { var operationTraceCode string + var operationResults []xdr.OperationResult + var ok bool - operationResults, ok := o.Transaction.Result.Result.OperationResults() + operationResults, ok = o.Transaction.Result.Result.OperationResults() if ok { - operationResultTr, ok := operationResults[o.OperationIndex].GetTr() + var operationResultTr xdr.OperationResultTr + operationResultTr, ok = operationResults[o.OperationIndex].GetTr() if ok { operationTraceCode, err := operationResultTr.MapOperationResultTr() if err != nil { @@ -245,7 +248,7 @@ func (o *LedgerOperation) CreateAccountDetails() (map[string]interface{}, error) return details, err } details["account"] = op.Destination.Address() - details["starting_balance"] = o.ConvertStroopValueToReal(op.StartingBalance) + details["starting_balance"] = int64(op.StartingBalance) return details, nil } @@ -282,36 +285,14 @@ func (o *LedgerOperation) PaymentDetails() (map[string]interface{}, error) { if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { return details, err } - details["amount"] = o.ConvertStroopValueToReal(op.Amount) - if err := o.addAssetDetailsToOperationDetails(details, op.Asset, ""); err != nil { + details["amount"] = int64(op.Amount) + if err := o.addAssetDetails(details, op.Asset, ""); err != nil { return details, err } return details, nil } -func (o *LedgerOperation) addAssetDetailsToOperationDetails(result map[string]interface{}, asset xdr.Asset, prefix string) error { - var assetType, code, issuer string - err := asset.Extract(&assetType, &code, &issuer) - if err != nil { - return err - } - - prefix = o.FormatPrefix(prefix) - result[prefix+"asset_type"] = assetType - - if asset.Type == xdr.AssetTypeAssetTypeNative { - result[prefix+"asset_id"] = int64(-5706705804583548011) - return nil - } - - result[prefix+"asset_code"] = code - result[prefix+"asset_issuer"] = issuer - result[prefix+"asset_id"] = o.FarmHashAsset(code, issuer, assetType) - - return nil -} - func (o *LedgerOperation) PathPaymentStrictReceiveDetails() (map[string]interface{}, error) { details := map[string]interface{}{} op, ok := o.Operation.Body.GetPathPaymentStrictReceiveOp() @@ -325,13 +306,13 @@ func (o *LedgerOperation) PathPaymentStrictReceiveDetails() (map[string]interfac if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { return details, err } - details["amount"] = o.ConvertStroopValueToReal(op.DestAmount) + details["amount"] = int64(op.DestAmount) details["source_amount"] = amount.String(0) - details["source_max"] = o.ConvertStroopValueToReal(op.SendMax) - if err := o.addAssetDetailsToOperationDetails(details, op.DestAsset, ""); err != nil { + details["source_max"] = int64(op.SendMax) + if err := o.addAssetDetails(details, op.DestAsset, ""); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.SendAsset, "source"); err != nil { + if err := o.addAssetDetails(details, op.SendAsset, "source"); err != nil { return details, err } @@ -349,7 +330,7 @@ func (o *LedgerOperation) PathPaymentStrictReceiveDetails() (map[string]interfac if !ok { return details, fmt.Errorf("could not access PathPaymentStrictReceive result info for this operation (index %d)", o.OperationIndex) } - details["source_amount"] = o.ConvertStroopValueToReal(result.SendAmount()) + details["source_amount"] = int64(result.SendAmount()) } details["path"] = o.TransformPath(op.Path) @@ -370,12 +351,12 @@ func (o *LedgerOperation) PathPaymentStrictSendDetails() (map[string]interface{} return details, err } details["amount"] = float64(0) - details["source_amount"] = o.ConvertStroopValueToReal(op.SendAmount) + details["source_amount"] = int64(op.SendAmount) details["destination_min"] = amount.String(op.DestMin) - if err := o.addAssetDetailsToOperationDetails(details, op.DestAsset, ""); err != nil { + if err := o.addAssetDetails(details, op.DestAsset, ""); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.SendAsset, "source"); err != nil { + if err := o.addAssetDetails(details, op.SendAsset, "source"); err != nil { return details, err } @@ -393,7 +374,7 @@ func (o *LedgerOperation) PathPaymentStrictSendDetails() (map[string]interface{} if !ok { return details, fmt.Errorf("could not access GetPathPaymentStrictSendResult result info for this operation (index %d)", o.OperationIndex) } - details["amount"] = o.ConvertStroopValueToReal(result.DestAmount()) + details["amount"] = int64(result.DestAmount()) } details["path"] = o.TransformPath(op.Path) @@ -408,15 +389,15 @@ func (o *LedgerOperation) ManageBuyOfferDetails() (map[string]interface{}, error } details["offer_id"] = int64(op.OfferId) - details["amount"] = o.ConvertStroopValueToReal(op.BuyAmount) + details["amount"] = int64(op.BuyAmount) if err := o.addPriceDetails(details, op.Price, ""); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.Buying, "buying"); err != nil { + if err := o.addAssetDetails(details, op.Buying, "buying"); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.Selling, "selling"); err != nil { + if err := o.addAssetDetails(details, op.Selling, "selling"); err != nil { return details, err } @@ -445,15 +426,15 @@ func (o *LedgerOperation) ManageSellOfferDetails() (map[string]interface{}, erro } details["offer_id"] = int64(op.OfferId) - details["amount"] = o.ConvertStroopValueToReal(op.Amount) + details["amount"] = int64(op.Amount) if err := o.addPriceDetails(details, op.Price, ""); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.Buying, "buying"); err != nil { + if err := o.addAssetDetails(details, op.Buying, "buying"); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.Selling, "selling"); err != nil { + if err := o.addAssetDetails(details, op.Selling, "selling"); err != nil { return details, err } @@ -466,15 +447,15 @@ func (o *LedgerOperation) CreatePassiveSellOfferDetails() (map[string]interface{ return details, fmt.Errorf("could not access CreatePassiveSellOffer info for this operation (index %d)", o.OperationIndex) } - details["amount"] = o.ConvertStroopValueToReal(op.Amount) + details["amount"] = int64(op.Amount) if err := o.addPriceDetails(details, op.Price, ""); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.Buying, "buying"); err != nil { + if err := o.addAssetDetails(details, op.Buying, "buying"); err != nil { return details, err } - if err := o.addAssetDetailsToOperationDetails(details, op.Selling, "selling"); err != nil { + if err := o.addAssetDetails(details, op.Selling, "selling"); err != nil { return details, err } @@ -568,7 +549,7 @@ func (o *LedgerOperation) ChangeTrustDetails() (map[string]interface{}, error) { return details, err } } else { - if err := o.addAssetDetailsToOperationDetails(details, op.Line.ToAsset(), ""); err != nil { + if err := o.addAssetDetails(details, op.Line.ToAsset(), ""); err != nil { return details, err } details["trustee"] = details["asset_issuer"] @@ -578,7 +559,7 @@ func (o *LedgerOperation) ChangeTrustDetails() (map[string]interface{}, error) { return details, err } - details["limit"] = o.ConvertStroopValueToReal(op.Limit) + details["limit"] = int64(op.Limit) return details, nil } @@ -606,7 +587,7 @@ func (o *LedgerOperation) AllowTrustDetails() (map[string]interface{}, error) { return details, fmt.Errorf("could not access AllowTrust info for this operation (index %d)", o.OperationIndex) } - if err := o.addAssetDetailsToOperationDetails(details, op.Asset.ToAsset(o.sourceAccountXDR().ToAccountId()), ""); err != nil { + if err := o.addAssetDetails(details, op.Asset.ToAsset(o.sourceAccountXDR().ToAccountId()), ""); err != nil { return details, err } if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "trustee"); err != nil { @@ -686,7 +667,12 @@ func (o *LedgerOperation) CreateClaimableBalanceDetails() (map[string]interface{ } details["asset"] = op.Asset.StringCanonical() - details["amount"] = o.ConvertStroopValueToReal(op.Amount) + err := o.addAssetDetails(details, op.Asset, "") + if err != nil { + return details, err + } + + details["amount"] = int64(op.Amount) details["claimants"] = o.TransformClaimants(op.Claimants) return details, nil @@ -813,13 +799,13 @@ func (o *LedgerOperation) ClawbackDetails() (map[string]interface{}, error) { return details, fmt.Errorf("could not access Clawback info for this operation (index %d)", o.OperationIndex) } - if err := o.addAssetDetailsToOperationDetails(details, op.Asset, ""); err != nil { + if err := o.addAssetDetails(details, op.Asset, ""); err != nil { return details, err } if err := o.addAccountAndMuxedAccountDetails(details, op.From, "from"); err != nil { return details, err } - details["amount"] = o.ConvertStroopValueToReal(op.Amount) + details["amount"] = int64(op.Amount) return details, nil } @@ -846,7 +832,7 @@ func (o *LedgerOperation) SetTrustLineFlagsDetails() (map[string]interface{}, er } details["trustor"] = op.Trustor.Address() - if err := o.addAssetDetailsToOperationDetails(details, op.Asset, ""); err != nil { + if err := o.addAssetDetails(details, op.Asset, ""); err != nil { return details, err } if op.SetFlags > 0 { @@ -912,10 +898,10 @@ func (o *LedgerOperation) LiquidityPoolDepositDetails() (map[string]interface{}, } // Process ReserveA Details - if err := o.addAssetDetailsToOperationDetails(details, assetA, "reserve_a"); err != nil { + if err := o.addAssetDetails(details, assetA, "reserve_a"); err != nil { return details, err } - details["reserve_a_max_amount"] = o.ConvertStroopValueToReal(op.MaxAmountA) + details["reserve_a_max_amount"] = int64(op.MaxAmountA) depositA, err := strconv.ParseFloat(amount.String(depositedA), 64) if err != nil { return details, err @@ -923,10 +909,10 @@ func (o *LedgerOperation) LiquidityPoolDepositDetails() (map[string]interface{}, details["reserve_a_deposit_amount"] = depositA //Process ReserveB Details - if err := o.addAssetDetailsToOperationDetails(details, assetB, "reserve_b"); err != nil { + if err := o.addAssetDetails(details, assetB, "reserve_b"); err != nil { return details, err } - details["reserve_b_max_amount"] = o.ConvertStroopValueToReal(op.MaxAmountB) + details["reserve_b_max_amount"] = int64(op.MaxAmountB) depositB, err := strconv.ParseFloat(amount.String(depositedB), 64) if err != nil { return details, err @@ -1022,20 +1008,20 @@ func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (map[string]interface{} receivedA, receivedB = -delta.ReserveA, -delta.ReserveB } // Process AssetA Details - if err := o.addAssetDetailsToOperationDetails(details, assetA, "reserve_a"); err != nil { + if err := o.addAssetDetails(details, assetA, "reserve_a"); err != nil { return details, err } - details["reserve_a_min_amount"] = o.ConvertStroopValueToReal(op.MinAmountA) - details["reserve_a_withdraw_amount"] = o.ConvertStroopValueToReal(receivedA) + details["reserve_a_min_amount"] = int64(op.MinAmountA) + details["reserve_a_withdraw_amount"] = int64(receivedA) // Process AssetB Details - if err := o.addAssetDetailsToOperationDetails(details, assetB, "reserve_b"); err != nil { + if err := o.addAssetDetails(details, assetB, "reserve_b"); err != nil { return details, err } - details["reserve_b_min_amount"] = o.ConvertStroopValueToReal(op.MinAmountB) - details["reserve_b_withdraw_amount"] = o.ConvertStroopValueToReal(receivedB) + details["reserve_b_min_amount"] = int64(op.MinAmountB) + details["reserve_b_withdraw_amount"] = int64(receivedB) - details["shares"] = o.ConvertStroopValueToReal(op.Amount) + details["shares"] = int64(op.Amount) return details, nil } @@ -1074,12 +1060,13 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (map[string]interface{}, e details["parameters"], details["parameters_decoded"] = o.serializeParameters(args) - if balanceChanges, err := o.parseAssetBalanceChangesFromContractEvents(); err != nil { + balanceChanges, err := o.parseAssetBalanceChangesFromContractEvents() + if err != nil { return nil, err - } else { - details["asset_balance_changes"] = balanceChanges } + details["asset_balance_changes"] = balanceChanges + case xdr.HostFunctionTypeHostFunctionTypeCreateContract: args := op.HostFunction.MustCreateContract() details["type"] = "create_contract" @@ -1098,7 +1085,11 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (map[string]interface{}, e details["contract_code_hash"] = contractCodeHash } - preimageTypeMap := o.switchContractIdPreimageType(args.ContractIdPreimage) + preimageTypeMap, err := o.switchContractIdPreimageType(args.ContractIdPreimage) + if err != nil { + return details, nil + } + for key, val := range preimageTypeMap { if _, ok := preimageTypeMap[key]; ok { details[key] = val @@ -1137,7 +1128,11 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (map[string]interface{}, e constructorArgs := args.ConstructorArgs details["parameters"], details["parameters_decoded"] = o.serializeParameters(constructorArgs) - preimageTypeMap := o.switchContractIdPreimageType(args.ContractIdPreimage) + preimageTypeMap, err := o.switchContractIdPreimageType(args.ContractIdPreimage) + if err != nil { + return details, nil + } + for key, val := range preimageTypeMap { if _, ok := preimageTypeMap[key]; ok { details[key] = val @@ -1180,7 +1175,7 @@ func (o *LedgerOperation) RestoreFootprintDetails() (map[string]interface{}, err details := map[string]interface{}{} _, ok := o.Operation.Body.GetRestoreFootprintOp() if !ok { - return details, fmt.Errorf("could not access InvokeHostFunction info for this operation (index %d)", o.OperationIndex) + return details, fmt.Errorf("could not access RestoreFootprint info for this operation (index %d)", o.OperationIndex) } details["type"] = "restore_footprint" @@ -1311,18 +1306,23 @@ func (o *LedgerOperation) addAssetDetails(result map[string]interface{}, a xdr.A err = fmt.Errorf("xdr.Asset.Extract error: %w", err) return err } + + prefix = o.FormatPrefix(prefix) result[prefix+"asset_type"] = assetType if a.Type == xdr.AssetTypeAssetTypeNative { + result[prefix+"asset_id"] = int64(-5706705804583548011) return nil } result[prefix+"asset_code"] = code result[prefix+"asset_issuer"] = issuer + result[prefix+"asset_id"] = o.FarmHashAsset(code, issuer, assetType) + return nil } -func (o *LedgerOperation) switchContractIdPreimageType(contractIdPreimage xdr.ContractIdPreimage) map[string]interface{} { +func (o *LedgerOperation) switchContractIdPreimageType(contractIdPreimage xdr.ContractIdPreimage) (map[string]interface{}, error) { details := map[string]interface{}{} switch contractIdPreimage.Type { @@ -1330,21 +1330,25 @@ func (o *LedgerOperation) switchContractIdPreimageType(contractIdPreimage xdr.Co fromAddress := contractIdPreimage.MustFromAddress() address, err := fromAddress.Address.String() if err != nil { - panic(fmt.Errorf("error obtaining address for: %s", contractIdPreimage.Type)) + return details, err } details["from"] = "address" details["address"] = address case xdr.ContractIdPreimageTypeContractIdPreimageFromAsset: details["from"] = "asset" details["asset"] = contractIdPreimage.MustFromAsset().StringCanonical() + err := o.addAssetDetails(details, contractIdPreimage.MustFromAsset(), "") + if err != nil { + return details, err + } default: panic(fmt.Errorf("unknown contract id type: %s", contractIdPreimage.Type)) } - return details + return details, nil } -func (o *LedgerOperation) ConvertStroopValueToReal(input xdr.Int64) float64 { +func (o *LedgerOperation) ConvertStroopValueToReal(input int64) float64 { output, _ := big.NewRat(int64(input), int64(10000000)).Float64() return output } diff --git a/ingest/ledger_operation_test.go b/ingest/ledger_operation_test.go index 83978ee7fa..d7127104dc 100644 --- a/ingest/ledger_operation_test.go +++ b/ingest/ledger_operation_test.go @@ -1091,12 +1091,12 @@ func resultTestOutput() []testOutput { "funder": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", "funder_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", "funder_muxed_id": uint64(123), - "starting_balance": 2.5}, + "starting_balance": int64(25000000)}, }, { err: nil, result: map[string]interface{}{ - "amount": float64(35), + "amount": int64(350000000), "asset_code": "USDT", "asset_id": int64(-8205667356306085451), "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", @@ -1109,7 +1109,7 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": float64(35), + "amount": int64(350000000), "asset_id": int64(-5706705804583548011), "asset_type": "native", "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", @@ -1120,7 +1120,7 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": 895.14959, + "amount": int64(8951495900), "asset_id": int64(-5706705804583548011), "asset_type": "native", "from": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", @@ -1131,16 +1131,16 @@ func resultTestOutput() []testOutput { AssetType: "credit_alphanum4", }, }, - "source_amount": float64(0), + "source_amount": int64(0), "source_asset_id": int64(-5706705804583548011), "source_asset_type": "native", - "source_max": 895.14959, + "source_max": int64(8951495900), "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, result: map[string]interface{}{ - "amount": float64(76.586), + "amount": int64(765860000), "buying_asset_id": int64(-5706705804583548011), "buying_asset_type": "native", "offer_id": int64(0), @@ -1157,7 +1157,7 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": float64(63.1595), + "amount": int64(631595000), "buying_asset_code": "USDT", "buying_asset_id": int64(-8205667356306085451), "buying_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", @@ -1195,7 +1195,7 @@ func resultTestOutput() []testOutput { "asset_id": int64(6690054458235693884), "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "asset_type": "credit_alphanum4", - "limit": 5e+10, + "limit": int64(500000000000000000), "trustee": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "trustor": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", "trustor_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", @@ -1205,7 +1205,7 @@ func resultTestOutput() []testOutput { err: nil, result: map[string]interface{}{ "asset_type": "liquidity_pool_shares", - "limit": 5e+10, + "limit": int64(500000000000000000), "liquidity_pool_id": "1c261d6c75930204a73b480c3020ab525e9be48ce93de6194cf69fb06f07452d", "trustor": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", "trustor_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", @@ -1252,7 +1252,7 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": float64(765.4501001), + "amount": int64(7654501001), "buying_asset_id": int64(-5706705804583548011), "buying_asset_type": "native", "offer_id": int64(100), @@ -1269,7 +1269,7 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": float64(64), + "amount": int64(640000000), "asset_id": int64(-5706705804583548011), "asset_type": "native", "destination_min": "428.0460538", @@ -1283,7 +1283,7 @@ func resultTestOutput() []testOutput { AssetType: "credit_alphanum4", }, }, - "source_amount": 0.1598182, + "source_amount": int64(1598182), "source_asset_id": int64(-5706705804583548011), "source_asset_type": "native", "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, @@ -1291,8 +1291,12 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": 123456.789, - "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "amount": int64(1234567890000), + "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_code": "USDT", + "asset_id": int64(-8205667356306085451), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", "claimants": []Claimant{ { Destination: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", @@ -1369,7 +1373,7 @@ func resultTestOutput() []testOutput { { err: nil, result: map[string]interface{}{ - "amount": 0.1598182, + "amount": int64(1598182), "asset_code": "USDT", "asset_id": int64(-8205667356306085451), "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", @@ -1410,13 +1414,13 @@ func resultTestOutput() []testOutput { "reserve_a_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "reserve_a_asset_type": "credit_alphanum4", "reserve_a_deposit_amount": 1e-07, - "reserve_a_max_amount": 0.0001, + "reserve_a_max_amount": int64(1000), "reserve_b_asset_code": "USDT", "reserve_b_asset_id": int64(-8205667356306085451), "reserve_b_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "reserve_b_asset_type": "credit_alphanum4", "reserve_b_deposit_amount": 1e-07, - "reserve_b_max_amount": 1e-05, + "reserve_b_max_amount": int64(100), "shares_received": 1e-07, }, }, @@ -1428,15 +1432,15 @@ func resultTestOutput() []testOutput { "reserve_a_asset_id": int64(-8205667356306085451), "reserve_a_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "reserve_a_asset_type": "credit_alphanum4", - "reserve_a_min_amount": 1e-07, - "reserve_a_withdraw_amount": -1e-07, + "reserve_a_min_amount": int64(1), + "reserve_a_withdraw_amount": int64(-1), "reserve_b_asset_code": "USDT", "reserve_b_asset_id": int64(-8205667356306085451), "reserve_b_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", "reserve_b_asset_type": "credit_alphanum4", - "reserve_b_min_amount": 1e-07, - "reserve_b_withdraw_amount": -1e-07, - "shares": 4e-07, + "reserve_b_min_amount": int64(1), + "reserve_b_withdraw_amount": int64(-1), + "shares": int64(4), }, }, { @@ -1484,6 +1488,10 @@ func resultTestOutput() []testOutput { err: nil, result: map[string]interface{}{ "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_code": "USDT", + "asset_id": int64(-8205667356306085451), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", "from": "asset", "function": "HostFunctionTypeHostFunctionTypeCreateContract", @@ -1495,6 +1503,10 @@ func resultTestOutput() []testOutput { err: nil, result: map[string]interface{}{ "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_code": "USDT", + "asset_id": int64(-8205667356306085451), + "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + "asset_type": "credit_alphanum4", "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", "from": "asset", "function": "HostFunctionTypeHostFunctionTypeCreateContractV2", From 86d5d78136fd3088261b1834e223b621cae53675 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Mon, 3 Feb 2025 17:52:20 -0500 Subject: [PATCH 04/12] Add structs for each operation --- ingest/account_merge_details.go | 47 + ingest/allow_trust_details.go | 58 + ...begin_sponsoring_future_reserve_details.go | 18 + ingest/bump_sequence_details.go | 18 + ingest/change_trust_details.go | 85 ++ ingest/claim_claimable_balance_details.go | 46 + ingest/clawback_claimable_balance_details.go | 25 + ingest/clawback_details.go | 47 + ingest/create_account_details.go | 36 + ingest/create_claimable_balance_details.go | 38 + ingest/create_passive_sell_offer_details.go | 60 + .../end_sponsoring_future_reserve_details.go | 51 + ingest/extend_footprint_ttl_details.go | 38 + ingest/inflation_details.go | 7 + ingest/invoke_host_function_details.go | 144 +++ ingest/ledger_operation.go | 1077 ++--------------- ingest/liquidity_pool_deposit_details.go | 110 ++ ingest/liquidity_pool_withdraw_details.go | 85 ++ ingest/manage_buy_offer_details.go | 62 + ingest/manage_data_details.go | 28 + ingest/manage_sell_offer_details.go | 63 + ingest/path_payment_strict_receive_details.go | 100 ++ ingest/path_payment_strict_send_details.go | 100 ++ ingest/payment_details.go | 60 + ingest/restore_footprint_details.go | 36 + ingest/revoke_sponsorship_details.go | 37 + ingest/set_options_details.go | 97 ++ ingest/set_trustline_flags_details.go | 73 ++ 28 files changed, 1700 insertions(+), 946 deletions(-) create mode 100644 ingest/account_merge_details.go create mode 100644 ingest/allow_trust_details.go create mode 100644 ingest/begin_sponsoring_future_reserve_details.go create mode 100644 ingest/bump_sequence_details.go create mode 100644 ingest/change_trust_details.go create mode 100644 ingest/claim_claimable_balance_details.go create mode 100644 ingest/clawback_claimable_balance_details.go create mode 100644 ingest/clawback_details.go create mode 100644 ingest/create_account_details.go create mode 100644 ingest/create_claimable_balance_details.go create mode 100644 ingest/create_passive_sell_offer_details.go create mode 100644 ingest/end_sponsoring_future_reserve_details.go create mode 100644 ingest/extend_footprint_ttl_details.go create mode 100644 ingest/inflation_details.go create mode 100644 ingest/invoke_host_function_details.go create mode 100644 ingest/liquidity_pool_deposit_details.go create mode 100644 ingest/liquidity_pool_withdraw_details.go create mode 100644 ingest/manage_buy_offer_details.go create mode 100644 ingest/manage_data_details.go create mode 100644 ingest/manage_sell_offer_details.go create mode 100644 ingest/path_payment_strict_receive_details.go create mode 100644 ingest/path_payment_strict_send_details.go create mode 100644 ingest/payment_details.go create mode 100644 ingest/restore_footprint_details.go create mode 100644 ingest/revoke_sponsorship_details.go create mode 100644 ingest/set_options_details.go create mode 100644 ingest/set_trustline_flags_details.go diff --git a/ingest/account_merge_details.go b/ingest/account_merge_details.go new file mode 100644 index 0000000000..fc70291188 --- /dev/null +++ b/ingest/account_merge_details.go @@ -0,0 +1,47 @@ +package ingest + +import "fmt" + +type AccountMergeDetail struct { + Account string `json:"account"` + AccountMuxed string `json:"account_muxed"` + AccountMuxedID uint64 `json:"account_muxed_id"` + Into string `json:"into"` + IntoMuxed string `json:"into_muxed"` + IntoMuxedID uint64 `json:"into_muxed_id"` +} + +func (o *LedgerOperation) AccountMergeDetails() (AccountMergeDetail, error) { + destinationAccount, ok := o.Operation.Body.GetDestination() + if !ok { + return AccountMergeDetail{}, fmt.Errorf("could not access Destination info for this operation (index %d)", o.OperationIndex) + } + + accountMergeDetail := AccountMergeDetail{ + Account: o.SourceAccount(), + Into: destinationAccount.Address(), + } + + var err error + var accountMuxed string + var accountMuxedID uint64 + accountMuxed, accountMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return AccountMergeDetail{}, err + } + + accountMergeDetail.AccountMuxed = accountMuxed + accountMergeDetail.AccountMuxedID = accountMuxedID + + var intoMuxed string + var intoMuxedID uint64 + intoMuxed, intoMuxedID, err = getMuxedAccountDetails(destinationAccount) + if err != nil { + return AccountMergeDetail{}, err + } + + accountMergeDetail.IntoMuxed = intoMuxed + accountMergeDetail.IntoMuxedID = intoMuxedID + + return accountMergeDetail, nil +} diff --git a/ingest/allow_trust_details.go b/ingest/allow_trust_details.go new file mode 100644 index 0000000000..df06278738 --- /dev/null +++ b/ingest/allow_trust_details.go @@ -0,0 +1,58 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type AllowTrustDetail struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Trustor string `json:"trustor"` + Trustee string `json:"trustee"` + TrusteeMuxed string `json:"trustee_muxed"` + TrusteeMuxedID uint64 `json:"trustee_muxed_id"` + Authorize bool `json:"authorize"` + AuthorizeToMaintainLiabilities bool `json:"authorize_to_maintain_liabilities"` + ClawbackEnabled bool `json:"clawback_enabled"` +} + +func (o *LedgerOperation) AllowTrustDetails() (AllowTrustDetail, error) { + op, ok := o.Operation.Body.GetAllowTrustOp() + if !ok { + return AllowTrustDetail{}, fmt.Errorf("could not access AllowTrust info for this operation (index %d)", o.OperationIndex) + } + + allowTrustDetail := AllowTrustDetail{ + Trustor: op.Trustor.Address(), + Trustee: o.SourceAccount(), + Authorize: xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag(), + AuthorizeToMaintainLiabilities: xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag(), + ClawbackEnabled: xdr.TrustLineFlags(op.Authorize).IsClawbackEnabledFlag(), + } + + var err error + var assetCode, assetIssuer, assetType string + err = op.Asset.ToAsset(o.sourceAccountXDR().ToAccountId()).Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return AllowTrustDetail{}, err + } + + allowTrustDetail.AssetCode = assetCode + allowTrustDetail.AssetIssuer = assetIssuer + allowTrustDetail.AssetType = assetType + + var trusteeMuxed string + var trusteeMuxedID uint64 + trusteeMuxed, trusteeMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return AllowTrustDetail{}, err + } + + allowTrustDetail.TrusteeMuxed = trusteeMuxed + allowTrustDetail.TrusteeMuxedID = trusteeMuxedID + + return allowTrustDetail, nil +} diff --git a/ingest/begin_sponsoring_future_reserve_details.go b/ingest/begin_sponsoring_future_reserve_details.go new file mode 100644 index 0000000000..ea0c255468 --- /dev/null +++ b/ingest/begin_sponsoring_future_reserve_details.go @@ -0,0 +1,18 @@ +package ingest + +import "fmt" + +type BeginSponsoringFutureReservesDetail struct { + SponsoredID string `json:"sponsored_id"` +} + +func (o *LedgerOperation) BeginSponsoringFutureReservesDetails() (BeginSponsoringFutureReservesDetail, error) { + op, ok := o.Operation.Body.GetBeginSponsoringFutureReservesOp() + if !ok { + return BeginSponsoringFutureReservesDetail{}, fmt.Errorf("could not access BeginSponsoringFutureReserves info for this operation (index %d)", o.OperationIndex) + } + + return BeginSponsoringFutureReservesDetail{ + SponsoredID: op.SponsoredId.Address(), + }, nil +} diff --git a/ingest/bump_sequence_details.go b/ingest/bump_sequence_details.go new file mode 100644 index 0000000000..c3e3d2e86e --- /dev/null +++ b/ingest/bump_sequence_details.go @@ -0,0 +1,18 @@ +package ingest + +import "fmt" + +type BumpSequenceDetails struct { + BumpTo string `json:"bump_to"` +} + +func (o *LedgerOperation) BumpSequenceDetails() (BumpSequenceDetails, error) { + op, ok := o.Operation.Body.GetBumpSequenceOp() + if !ok { + return BumpSequenceDetails{}, fmt.Errorf("could not access BumpSequence info for this operation (index %d)", o.OperationIndex) + } + + return BumpSequenceDetails{ + BumpTo: fmt.Sprintf("%d", op.BumpTo), + }, nil +} diff --git a/ingest/change_trust_details.go b/ingest/change_trust_details.go new file mode 100644 index 0000000000..a3302146f7 --- /dev/null +++ b/ingest/change_trust_details.go @@ -0,0 +1,85 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type ChangeTrustDetail struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + LiquidityPoolID string `json:"liquidity_pool_id"` + Limit int64 `json:"limit"` + Trustee string `json:"trustee"` + Trustor string `json:"trustor"` + TrustorMuxed string `json:"trustor_muxed"` + TrustorMuxedID uint64 `json:"trustor_muxed_id"` +} + +func (o *LedgerOperation) ChangeTrustDetails() (ChangeTrustDetail, error) { + op, ok := o.Operation.Body.GetChangeTrustOp() + if !ok { + return ChangeTrustDetail{}, fmt.Errorf("could not access GetChangeTrust info for this operation (index %d)", o.OperationIndex) + } + + var err error + changeTrustDetail := ChangeTrustDetail{ + Trustor: o.SourceAccount(), + Limit: int64(op.Limit), + } + + if op.Line.Type == xdr.AssetTypeAssetTypePoolShare { + changeTrustDetail.AssetType, changeTrustDetail.LiquidityPoolID, err = getLiquidityPoolAssetDetails(*op.Line.LiquidityPool) + if err != nil { + return ChangeTrustDetail{}, err + } + } else { + var assetCode, assetIssuer, assetType string + err = op.Line.ToAsset().Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return ChangeTrustDetail{}, err + } + + changeTrustDetail.AssetCode = assetCode + changeTrustDetail.AssetIssuer = assetIssuer + changeTrustDetail.AssetType = assetType + changeTrustDetail.Trustee = assetIssuer + } + + var trustorMuxed string + var trustorMuxedID uint64 + trustorMuxed, trustorMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return ChangeTrustDetail{}, err + } + + changeTrustDetail.TrustorMuxed = trustorMuxed + changeTrustDetail.TrustorMuxedID = trustorMuxedID + + return changeTrustDetail, nil +} + +func getLiquidityPoolAssetDetails(lpp xdr.LiquidityPoolParameters) (string, string, error) { + if lpp.Type != xdr.LiquidityPoolTypeLiquidityPoolConstantProduct { + return "", "", fmt.Errorf("unknown liquidity pool type %d", lpp.Type) + } + + cp := lpp.ConstantProduct + + var err error + var poolID xdr.PoolId + var poolIDString string + poolID, err = xdr.NewPoolId(cp.AssetA, cp.AssetB, cp.Fee) + if err != nil { + return "", "", err + } + + poolIDString, err = PoolIDToString(poolID) + if err != nil { + return "", "", err + } + + return "liquidity_pool_shares", poolIDString, nil +} diff --git a/ingest/claim_claimable_balance_details.go b/ingest/claim_claimable_balance_details.go new file mode 100644 index 0000000000..2125d31e6b --- /dev/null +++ b/ingest/claim_claimable_balance_details.go @@ -0,0 +1,46 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type ClaimClaimableBalanceDetail struct { + BalanceID string `json:"balance_id"` + Claimant string `json:"claimant"` + ClaimantMuxed string `json:"claimant_muxed"` + ClaimantMuxedID uint64 `json:"claimant_muxed_id"` +} + +func (o *LedgerOperation) ClaimClaimableBalanceDetails() (ClaimClaimableBalanceDetail, error) { + op, ok := o.Operation.Body.GetClaimClaimableBalanceOp() + if !ok { + return ClaimClaimableBalanceDetail{}, fmt.Errorf("could not access ClaimClaimableBalance info for this operation (index %d)", o.OperationIndex) + } + + claimClaimableBalanceDetail := ClaimClaimableBalanceDetail{ + Claimant: o.SourceAccount(), + } + + var err error + var balanceID string + balanceID, err = xdr.MarshalBase64(op.BalanceId) + if err != nil { + return ClaimClaimableBalanceDetail{}, err + } + + claimClaimableBalanceDetail.BalanceID = balanceID + + var claimantMuxed string + var claimantMuxedID uint64 + claimantMuxed, claimantMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return ClaimClaimableBalanceDetail{}, err + } + + claimClaimableBalanceDetail.ClaimantMuxed = claimantMuxed + claimClaimableBalanceDetail.ClaimantMuxedID = claimantMuxedID + + return claimClaimableBalanceDetail, nil +} diff --git a/ingest/clawback_claimable_balance_details.go b/ingest/clawback_claimable_balance_details.go new file mode 100644 index 0000000000..5a8e19f796 --- /dev/null +++ b/ingest/clawback_claimable_balance_details.go @@ -0,0 +1,25 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type ClawbackClaimableBalanceDetail struct { + BalanceID string `json:"balance_id"` +} + +func (o *LedgerOperation) ClawbackClaimableBalanceDetails() (ClawbackClaimableBalanceDetail, error) { + op, ok := o.Operation.Body.GetClawbackClaimableBalanceOp() + if !ok { + return ClawbackClaimableBalanceDetail{}, fmt.Errorf("could not access ClawbackClaimableBalance info for this operation (index %d)", o.OperationIndex) + } + + balanceID, err := xdr.MarshalBase64(op.BalanceId) + if err != nil { + return ClawbackClaimableBalanceDetail{}, err + } + + return ClawbackClaimableBalanceDetail{BalanceID: balanceID}, nil +} diff --git a/ingest/clawback_details.go b/ingest/clawback_details.go new file mode 100644 index 0000000000..f9dc0f6fd4 --- /dev/null +++ b/ingest/clawback_details.go @@ -0,0 +1,47 @@ +package ingest + +import "fmt" + +type ClawbackDetail struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + From string `json:"from"` + FromMuxed string `json:"from_muxed"` + FromMuxedID uint64 `json:"from_muxed_id"` + Amount int64 `json:"amount"` +} + +func (o *LedgerOperation) ClawbackDetails() (ClawbackDetail, error) { + op, ok := o.Operation.Body.GetClawbackOp() + if !ok { + return ClawbackDetail{}, fmt.Errorf("could not access Clawback info for this operation (index %d)", o.OperationIndex) + } + + clawbackDetail := ClawbackDetail{ + Amount: int64(op.Amount), + } + + var err error + var assetCode, assetIssuer, assetType string + err = op.Asset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return ClawbackDetail{}, err + } + + clawbackDetail.AssetCode = assetCode + clawbackDetail.AssetIssuer = assetIssuer + clawbackDetail.AssetType = assetType + + var fromMuxed string + var fromMuxedID uint64 + fromMuxed, fromMuxedID, err = getMuxedAccountDetails(op.From) + if err != nil { + return ClawbackDetail{}, err + } + + clawbackDetail.FromMuxed = fromMuxed + clawbackDetail.FromMuxedID = fromMuxedID + + return clawbackDetail, nil +} diff --git a/ingest/create_account_details.go b/ingest/create_account_details.go new file mode 100644 index 0000000000..9a04de1be6 --- /dev/null +++ b/ingest/create_account_details.go @@ -0,0 +1,36 @@ +package ingest + +import ( + "fmt" +) + +type CreateAccountDetail struct { + Account string `json:"account"` + StartingBalance int64 `json:"starting_balance"` + Funder string `json:"funder"` + FunderMuxed string `json:"funder_muxed"` + FunderMuxedID uint64 `json:"funder_muxed_id"` +} + +func (o *LedgerOperation) CreateAccountDetails() (CreateAccountDetail, error) { + op, ok := o.Operation.Body.GetCreateAccountOp() + if !ok { + return CreateAccountDetail{}, fmt.Errorf("could not access CreateAccount info for this operation (index %d)", o.OperationIndex) + } + + createAccountDetail := CreateAccountDetail{ + Account: op.Destination.Address(), + StartingBalance: int64(op.StartingBalance), + Funder: o.SourceAccount(), + } + + funderMuxed, funderMuxedID, err := getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return CreateAccountDetail{}, err + } + + createAccountDetail.FunderMuxed = funderMuxed + createAccountDetail.FunderMuxedID = funderMuxedID + + return createAccountDetail, nil +} diff --git a/ingest/create_claimable_balance_details.go b/ingest/create_claimable_balance_details.go new file mode 100644 index 0000000000..795ed5edf0 --- /dev/null +++ b/ingest/create_claimable_balance_details.go @@ -0,0 +1,38 @@ +package ingest + +import ( + "fmt" +) + +type CreateClaimableBalanceDetail struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Amount int64 `json:"amount"` + Claimants []Claimant `json:"claimants"` +} + +func (o *LedgerOperation) CreateClaimableBalanceDetails() (CreateClaimableBalanceDetail, error) { + op, ok := o.Operation.Body.GetCreateClaimableBalanceOp() + if !ok { + return CreateClaimableBalanceDetail{}, fmt.Errorf("could not access CreateClaimableBalance info for this operation (index %d)", o.OperationIndex) + } + + createClaimableBalanceDetail := CreateClaimableBalanceDetail{ + Claimants: transformClaimants(op.Claimants), + Amount: int64(op.Amount), + } + + var err error + var assetCode, assetIssuer, assetType string + err = op.Asset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return CreateClaimableBalanceDetail{}, err + } + + createClaimableBalanceDetail.AssetCode = assetCode + createClaimableBalanceDetail.AssetIssuer = assetIssuer + createClaimableBalanceDetail.AssetType = assetType + + return createClaimableBalanceDetail, nil +} diff --git a/ingest/create_passive_sell_offer_details.go b/ingest/create_passive_sell_offer_details.go new file mode 100644 index 0000000000..1532cb7d07 --- /dev/null +++ b/ingest/create_passive_sell_offer_details.go @@ -0,0 +1,60 @@ +package ingest + +import ( + "fmt" + "strconv" +) + +type CreatePassiveSellOfferDetail struct { + Amount int64 `json:"amount"` + PriceN int32 `json:"price_n"` + PriceD int32 `json:"price_d"` + Price float64 `json:"price"` + BuyingAssetCode string `json:"buying_asset_code"` + BuyingAssetIssuer string `json:"buying_asset_issuer"` + BuyingAssetType string `json:"buying_asset_type"` + SellingAssetCode string `json:"selling_asset_code"` + SellingAssetIssuer string `json:"selling_asset_issuer"` + SellingAssetType string `json:"selling_asset_type"` +} + +func (o *LedgerOperation) CreatePassiveSellOfferDetails() (CreatePassiveSellOfferDetail, error) { + op, ok := o.Operation.Body.GetCreatePassiveSellOfferOp() + if !ok { + return CreatePassiveSellOfferDetail{}, fmt.Errorf("could not access CreatePassiveSellOffer info for this operation (index %d)", o.OperationIndex) + } + + createPassiveSellOffer := CreatePassiveSellOfferDetail{ + Amount: int64(op.Amount), + PriceN: int32(op.Price.N), + PriceD: int32(op.Price.D), + } + + var err error + createPassiveSellOffer.Price, err = strconv.ParseFloat(op.Price.String(), 64) + if err != nil { + return CreatePassiveSellOfferDetail{}, err + } + + var buyingAssetCode, buyingAssetIssuer, buyingAssetType string + err = op.Buying.Extract(&buyingAssetType, &buyingAssetCode, &buyingAssetIssuer) + if err != nil { + return CreatePassiveSellOfferDetail{}, err + } + + createPassiveSellOffer.BuyingAssetCode = buyingAssetCode + createPassiveSellOffer.BuyingAssetIssuer = buyingAssetIssuer + createPassiveSellOffer.BuyingAssetType = buyingAssetType + + var sellingAssetCode, sellingAssetIssuer, sellingAssetType string + err = op.Selling.Extract(&sellingAssetType, &sellingAssetCode, &sellingAssetIssuer) + if err != nil { + return CreatePassiveSellOfferDetail{}, err + } + + createPassiveSellOffer.SellingAssetCode = sellingAssetCode + createPassiveSellOffer.SellingAssetIssuer = sellingAssetIssuer + createPassiveSellOffer.SellingAssetType = sellingAssetType + + return createPassiveSellOffer, nil +} diff --git a/ingest/end_sponsoring_future_reserve_details.go b/ingest/end_sponsoring_future_reserve_details.go new file mode 100644 index 0000000000..5a9b58df9e --- /dev/null +++ b/ingest/end_sponsoring_future_reserve_details.go @@ -0,0 +1,51 @@ +package ingest + +type EndSponsoringFutureReserveDetail struct { + BeginSponsor string `json:"begin_sponsor"` + BeginSponsorMuxed string `json:"begin_sponsor_muxed"` + BeginSponsorMuxedID uint64 `json:"begin_sponsor_muxed_id"` +} + +func (o *LedgerOperation) EndSponsoringFutureReserveDetails() (EndSponsoringFutureReserveDetail, error) { + var endSponsoringFutureReserveDetail EndSponsoringFutureReserveDetail + + beginSponsorOp := o.findInitatingBeginSponsoringOp() + if beginSponsorOp != nil { + endSponsoringFutureReserveDetail.BeginSponsor = o.SourceAccount() + + var err error + var beginSponsorMuxed string + var beginSponsorMuxedID uint64 + beginSponsorMuxed, beginSponsorMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return EndSponsoringFutureReserveDetail{}, err + } + + endSponsoringFutureReserveDetail.BeginSponsorMuxed = beginSponsorMuxed + endSponsoringFutureReserveDetail.BeginSponsorMuxedID = beginSponsorMuxedID + } + + return endSponsoringFutureReserveDetail, nil +} + +func (o *LedgerOperation) findInitatingBeginSponsoringOp() *SponsorshipOutput { + if !o.Transaction.Successful() { + // Failed transactions may not have a compliant sandwich structure + // we can rely on (e.g. invalid nesting or a being operation with the wrong sponsoree ID) + // and thus we bail out since we could return incorrect information. + return nil + } + sponsoree := o.sourceAccountXDR().ToAccountId() + operations := o.Transaction.Envelope.Operations() + for i := int(o.OperationIndex) - 1; i >= 0; i-- { + if beginOp, ok := operations[i].Body.GetBeginSponsoringFutureReservesOp(); ok && + beginOp.SponsoredId.Address() == sponsoree.Address() { + result := SponsorshipOutput{ + Operation: operations[i], + OperationIndex: uint32(i), + } + return &result + } + } + return nil +} diff --git a/ingest/extend_footprint_ttl_details.go b/ingest/extend_footprint_ttl_details.go new file mode 100644 index 0000000000..8889077e03 --- /dev/null +++ b/ingest/extend_footprint_ttl_details.go @@ -0,0 +1,38 @@ +package ingest + +import "fmt" + +type ExtendFootprintTtlDetail struct { + Type string `json:"type"` + ExtendTo uint32 `json:"extend_to"` + LedgerKeyHash []string `json:"ledger_key_hash"` + ContractID string `json:"contract_id"` + ContractCodeHash string `json:"contract_code_hash"` +} + +func (o *LedgerOperation) ExtendFootprintTtlDetails() (ExtendFootprintTtlDetail, error) { + op, ok := o.Operation.Body.GetExtendFootprintTtlOp() + if !ok { + return ExtendFootprintTtlDetail{}, fmt.Errorf("could not access ExtendFootprintTtl info for this operation (index %d)", o.OperationIndex) + } + + extendFootprintTtlDetail := ExtendFootprintTtlDetail{ + Type: "extend_footprint_ttl", + ExtendTo: uint32(op.ExtendTo), + LedgerKeyHash: o.Transaction.LedgerKeyHashFromTxEnvelope(), + } + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + extendFootprintTtlDetail.ContractID = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + extendFootprintTtlDetail.ContractCodeHash = contractCodeHash + } + + return extendFootprintTtlDetail, nil +} diff --git a/ingest/inflation_details.go b/ingest/inflation_details.go new file mode 100644 index 0000000000..6eda8adea1 --- /dev/null +++ b/ingest/inflation_details.go @@ -0,0 +1,7 @@ +package ingest + +type InflationDetail struct{} + +func (o *LedgerOperation) InflationDetails() (InflationDetail, error) { + return InflationDetail{}, nil +} diff --git a/ingest/invoke_host_function_details.go b/ingest/invoke_host_function_details.go new file mode 100644 index 0000000000..8a37365602 --- /dev/null +++ b/ingest/invoke_host_function_details.go @@ -0,0 +1,144 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type InvokeHostFunctionDetail struct { + Function string `json:"function"` + Type string `json:"type"` + LedgerKeyHash []string `json:"ledger_key_hash"` + ContractID string `json:"contract_id"` + ContractCodeHash string `json:"contract_code_hash"` + Parameters []map[string]string `json:"parameters"` + ParametersDecoded []map[string]string `json:"parameters_decoded"` + AssetBalanceChanges []BalanceChangeDetail `json:"asset_balance_changes"` + From string `json:"from"` + Address string `json:"address"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` +} + +func (o *LedgerOperation) InvokeHostFunctionDetails() (InvokeHostFunctionDetail, error) { + op, ok := o.Operation.Body.GetInvokeHostFunctionOp() + if !ok { + return InvokeHostFunctionDetail{}, fmt.Errorf("could not access InvokeHostFunction info for this operation (index %d)", o.OperationIndex) + } + + invokeHostFunctionDetail := InvokeHostFunctionDetail{ + Function: op.HostFunction.Type.String(), + } + + switch op.HostFunction.Type { + case xdr.HostFunctionTypeHostFunctionTypeInvokeContract: + invokeArgs := op.HostFunction.MustInvokeContract() + args := make([]xdr.ScVal, 0, len(invokeArgs.Args)+2) + args = append(args, xdr.ScVal{Type: xdr.ScValTypeScvAddress, Address: &invokeArgs.ContractAddress}) + args = append(args, xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &invokeArgs.FunctionName}) + args = append(args, invokeArgs.Args...) + + invokeHostFunctionDetail.Type = "invoke_contract" + + contractId, err := invokeArgs.ContractAddress.String() + if err != nil { + return InvokeHostFunctionDetail{}, err + } + + invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() + invokeHostFunctionDetail.ContractID = contractId + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractCodeHash = contractCodeHash + } + + // TODO: Parameters should be processed with xdr2json + invokeHostFunctionDetail.Parameters, invokeHostFunctionDetail.ParametersDecoded = o.serializeParameters(args) + + balanceChanges, err := o.parseAssetBalanceChangesFromContractEvents() + if err != nil { + return InvokeHostFunctionDetail{}, err + } + + invokeHostFunctionDetail.AssetBalanceChanges = balanceChanges + + case xdr.HostFunctionTypeHostFunctionTypeCreateContract: + args := op.HostFunction.MustCreateContract() + + invokeHostFunctionDetail.Type = "create_contract" + invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractID = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractCodeHash = contractCodeHash + } + + preImageDetails, err := switchContractIdPreimageType(args.ContractIdPreimage) + if err != nil { + return InvokeHostFunctionDetail{}, nil + } + + invokeHostFunctionDetail.From = preImageDetails.From + invokeHostFunctionDetail.Address = preImageDetails.Address + invokeHostFunctionDetail.AssetCode = preImageDetails.AssetCode + invokeHostFunctionDetail.AssetIssuer = preImageDetails.AssetIssuer + invokeHostFunctionDetail.AssetType = preImageDetails.AssetType + case xdr.HostFunctionTypeHostFunctionTypeUploadContractWasm: + invokeHostFunctionDetail.Type = "upload_wasm" + invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractCodeHash = contractCodeHash + } + case xdr.HostFunctionTypeHostFunctionTypeCreateContractV2: + args := op.HostFunction.MustCreateContractV2() + + invokeHostFunctionDetail.Type = "create_contract_v2" + invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractID = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractCodeHash = contractCodeHash + } + + // ConstructorArgs is a list of ScVals + // This will initially be handled the same as InvokeContractParams until a different + // model is found necessary. + invokeHostFunctionDetail.Parameters, invokeHostFunctionDetail.ParametersDecoded = o.serializeParameters(args.ConstructorArgs) + + preImageDetails, err := switchContractIdPreimageType(args.ContractIdPreimage) + if err != nil { + return InvokeHostFunctionDetail{}, nil + } + + invokeHostFunctionDetail.From = preImageDetails.From + invokeHostFunctionDetail.Address = preImageDetails.Address + invokeHostFunctionDetail.AssetCode = preImageDetails.AssetCode + invokeHostFunctionDetail.AssetIssuer = preImageDetails.AssetIssuer + invokeHostFunctionDetail.AssetType = preImageDetails.AssetType + default: + return InvokeHostFunctionDetail{}, fmt.Errorf("unknown host function type: %s", op.HostFunction.Type) + } + + return invokeHostFunctionDetail, nil +} diff --git a/ingest/ledger_operation.go b/ingest/ledger_operation.go index cf7f29f799..77b831448d 100644 --- a/ingest/ledger_operation.go +++ b/ingest/ledger_operation.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "fmt" "math/big" - "strconv" "github.com/dgryski/go-farm" "github.com/stellar/go/amount" @@ -31,10 +30,7 @@ func (o *LedgerOperation) sourceAccountXDR() xdr.MuxedAccount { func (o *LedgerOperation) SourceAccount() string { muxedAccount := o.sourceAccountXDR() - providedID := muxedAccount.ToAccountId() - pointerToID := &providedID - - return pointerToID.Address() + return muxedAccount.ToAccountId().Address() } func (o *LedgerOperation) Type() int32 { @@ -90,9 +86,9 @@ func (o *LedgerOperation) OperationTraceCode() (string, error) { return operationTraceCode, nil } -func (o *LedgerOperation) OperationDetails() (map[string]interface{}, error) { +func (o *LedgerOperation) OperationDetails() (interface{}, error) { var err error - details := map[string]interface{}{} + var details interface{} switch o.Operation.Body.Type { case xdr.OperationTypeCreateAccount: @@ -201,7 +197,7 @@ func (o *LedgerOperation) OperationDetails() (map[string]interface{}, error) { return details, err } case xdr.OperationTypeSetTrustLineFlags: - details, err = o.SetTrustLineFlagsDetails() + details, err = o.SetTrustlineFlagsDetails() if err != nil { return details, err } @@ -237,705 +233,85 @@ func (o *LedgerOperation) OperationDetails() (map[string]interface{}, error) { return details, nil } -func (o *LedgerOperation) CreateAccountDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetCreateAccountOp() - if !ok { - return details, fmt.Errorf("could not access CreateAccount info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "funder"); err != nil { - return details, err - } - details["account"] = op.Destination.Address() - details["starting_balance"] = int64(op.StartingBalance) - - return details, nil -} +func getMuxedAccountDetails(a xdr.MuxedAccount) (string, uint64, error) { + var err error + var muxedAccountAddress string + var muxedAccountID uint64 -func (o *LedgerOperation) addAccountAndMuxedAccountDetails(result map[string]interface{}, a xdr.MuxedAccount, prefix string) error { - account_id := a.ToAccountId() - result[prefix] = account_id.Address() - prefix = o.FormatPrefix(prefix) if a.Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 { - muxedAccountAddress, err := a.GetAddress() + muxedAccountAddress, err = a.GetAddress() if err != nil { - return err + return "", 0, err } - result[prefix+"muxed"] = muxedAccountAddress - muxedAccountId, err := a.GetId() + muxedAccountID, err = a.GetId() if err != nil { - return err - } - result[prefix+"muxed_id"] = muxedAccountId - } - return nil -} - -func (o *LedgerOperation) PaymentDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetPaymentOp() - if !ok { - return details, fmt.Errorf("could not access Payment info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "from"); err != nil { - return details, err - } - if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { - return details, err - } - details["amount"] = int64(op.Amount) - if err := o.addAssetDetails(details, op.Asset, ""); err != nil { - return details, err - } - - return details, nil -} - -func (o *LedgerOperation) PathPaymentStrictReceiveDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetPathPaymentStrictReceiveOp() - if !ok { - return details, fmt.Errorf("could not access PathPaymentStrictReceive info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "from"); err != nil { - return details, err - } - if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { - return details, err - } - details["amount"] = int64(op.DestAmount) - details["source_amount"] = amount.String(0) - details["source_max"] = int64(op.SendMax) - if err := o.addAssetDetails(details, op.DestAsset, ""); err != nil { - return details, err - } - if err := o.addAssetDetails(details, op.SendAsset, "source"); err != nil { - return details, err - } - - if o.Transaction.Successful() { - allOperationResults, ok := o.Transaction.Result.OperationResults() - if !ok { - return details, fmt.Errorf("could not access any results for this transaction") - } - currentOperationResult := allOperationResults[o.OperationIndex] - resultBody, ok := currentOperationResult.GetTr() - if !ok { - return details, fmt.Errorf("could not access result body for this operation (index %d)", o.OperationIndex) - } - result, ok := resultBody.GetPathPaymentStrictReceiveResult() - if !ok { - return details, fmt.Errorf("could not access PathPaymentStrictReceive result info for this operation (index %d)", o.OperationIndex) - } - details["source_amount"] = int64(result.SendAmount()) - } - - details["path"] = o.TransformPath(op.Path) - return details, nil -} - -func (o *LedgerOperation) PathPaymentStrictSendDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetPathPaymentStrictSendOp() - if !ok { - return details, fmt.Errorf("could not access PathPaymentStrictSend info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "from"); err != nil { - return details, err - } - if err := o.addAccountAndMuxedAccountDetails(details, op.Destination, "to"); err != nil { - return details, err - } - details["amount"] = float64(0) - details["source_amount"] = int64(op.SendAmount) - details["destination_min"] = amount.String(op.DestMin) - if err := o.addAssetDetails(details, op.DestAsset, ""); err != nil { - return details, err - } - if err := o.addAssetDetails(details, op.SendAsset, "source"); err != nil { - return details, err - } - - if o.Transaction.Successful() { - allOperationResults, ok := o.Transaction.Result.OperationResults() - if !ok { - return details, fmt.Errorf("could not access any results for this transaction") - } - currentOperationResult := allOperationResults[o.OperationIndex] - resultBody, ok := currentOperationResult.GetTr() - if !ok { - return details, fmt.Errorf("could not access result body for this operation (index %d)", o.OperationIndex) - } - result, ok := resultBody.GetPathPaymentStrictSendResult() - if !ok { - return details, fmt.Errorf("could not access GetPathPaymentStrictSendResult result info for this operation (index %d)", o.OperationIndex) - } - details["amount"] = int64(result.DestAmount()) - } - - details["path"] = o.TransformPath(op.Path) - - return details, nil -} -func (o *LedgerOperation) ManageBuyOfferDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetManageBuyOfferOp() - if !ok { - return details, fmt.Errorf("could not access ManageBuyOffer info for this operation (index %d)", o.OperationIndex) - } - - details["offer_id"] = int64(op.OfferId) - details["amount"] = int64(op.BuyAmount) - if err := o.addPriceDetails(details, op.Price, ""); err != nil { - return details, err - } - - if err := o.addAssetDetails(details, op.Buying, "buying"); err != nil { - return details, err - } - if err := o.addAssetDetails(details, op.Selling, "selling"); err != nil { - return details, err - } - - return details, nil -} - -func (o *LedgerOperation) addPriceDetails(result map[string]interface{}, price xdr.Price, prefix string) error { - prefix = o.FormatPrefix(prefix) - parsedPrice, err := strconv.ParseFloat(price.String(), 64) - if err != nil { - return err - } - result[prefix+"price"] = parsedPrice - result[prefix+"price_r"] = Price{ - Numerator: int32(price.N), - Denominator: int32(price.D), - } - return nil -} - -func (o *LedgerOperation) ManageSellOfferDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetManageSellOfferOp() - if !ok { - return details, fmt.Errorf("could not access ManageSellOffer info for this operation (index %d)", o.OperationIndex) - } - - details["offer_id"] = int64(op.OfferId) - details["amount"] = int64(op.Amount) - if err := o.addPriceDetails(details, op.Price, ""); err != nil { - return details, err - } - - if err := o.addAssetDetails(details, op.Buying, "buying"); err != nil { - return details, err - } - if err := o.addAssetDetails(details, op.Selling, "selling"); err != nil { - return details, err - } - - return details, nil -} -func (o *LedgerOperation) CreatePassiveSellOfferDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetCreatePassiveSellOfferOp() - if !ok { - return details, fmt.Errorf("could not access CreatePassiveSellOffer info for this operation (index %d)", o.OperationIndex) - } - - details["amount"] = int64(op.Amount) - if err := o.addPriceDetails(details, op.Price, ""); err != nil { - return details, err - } - - if err := o.addAssetDetails(details, op.Buying, "buying"); err != nil { - return details, err - } - if err := o.addAssetDetails(details, op.Selling, "selling"); err != nil { - return details, err - } - - return details, nil -} -func (o *LedgerOperation) SetOptionsDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetSetOptionsOp() - if !ok { - return details, fmt.Errorf("could not access GetSetOptions info for this operation (index %d)", o.OperationIndex) - } - - if op.InflationDest != nil { - details["inflation_dest"] = op.InflationDest.Address() - } - - if op.SetFlags != nil && *op.SetFlags > 0 { - o.addOperationFlagToOperationDetails(details, uint32(*op.SetFlags), "set") - } - - if op.ClearFlags != nil && *op.ClearFlags > 0 { - o.addOperationFlagToOperationDetails(details, uint32(*op.ClearFlags), "clear") - } - - if op.MasterWeight != nil { - details["master_key_weight"] = uint32(*op.MasterWeight) - } - - if op.LowThreshold != nil { - details["low_threshold"] = uint32(*op.LowThreshold) - } - - if op.MedThreshold != nil { - details["med_threshold"] = uint32(*op.MedThreshold) - } - - if op.HighThreshold != nil { - details["high_threshold"] = uint32(*op.HighThreshold) - } - - if op.HomeDomain != nil { - details["home_domain"] = string(*op.HomeDomain) - } - - if op.Signer != nil { - details["signer_key"] = op.Signer.Key.Address() - details["signer_weight"] = uint32(op.Signer.Weight) - } - - return details, nil -} - -func (o *LedgerOperation) addOperationFlagToOperationDetails(result map[string]interface{}, flag uint32, prefix string) { - intFlags := make([]int32, 0) - stringFlags := make([]string, 0) - - if (int64(flag) & int64(xdr.AccountFlagsAuthRequiredFlag)) > 0 { - intFlags = append(intFlags, int32(xdr.AccountFlagsAuthRequiredFlag)) - stringFlags = append(stringFlags, "auth_required") - } - - if (int64(flag) & int64(xdr.AccountFlagsAuthRevocableFlag)) > 0 { - intFlags = append(intFlags, int32(xdr.AccountFlagsAuthRevocableFlag)) - stringFlags = append(stringFlags, "auth_revocable") - } - - if (int64(flag) & int64(xdr.AccountFlagsAuthImmutableFlag)) > 0 { - intFlags = append(intFlags, int32(xdr.AccountFlagsAuthImmutableFlag)) - stringFlags = append(stringFlags, "auth_immutable") - } - - if (int64(flag) & int64(xdr.AccountFlagsAuthClawbackEnabledFlag)) > 0 { - intFlags = append(intFlags, int32(xdr.AccountFlagsAuthClawbackEnabledFlag)) - stringFlags = append(stringFlags, "auth_clawback_enabled") - } - - prefix = o.FormatPrefix(prefix) - result[prefix+"flags"] = intFlags - result[prefix+"flags_s"] = stringFlags -} - -func (o *LedgerOperation) ChangeTrustDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetChangeTrustOp() - if !ok { - return details, fmt.Errorf("could not access GetChangeTrust info for this operation (index %d)", o.OperationIndex) - } - - if op.Line.Type == xdr.AssetTypeAssetTypePoolShare { - if err := o.addLiquidityPoolAssetDetails(details, *op.Line.LiquidityPool); err != nil { - return details, err - } - } else { - if err := o.addAssetDetails(details, op.Line.ToAsset(), ""); err != nil { - return details, err + return "", 0, err } - details["trustee"] = details["asset_issuer"] - } - - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "trustor"); err != nil { - return details, err - } - - details["limit"] = int64(op.Limit) - - return details, nil -} - -func (o *LedgerOperation) addLiquidityPoolAssetDetails(result map[string]interface{}, lpp xdr.LiquidityPoolParameters) error { - result["asset_type"] = "liquidity_pool_shares" - if lpp.Type != xdr.LiquidityPoolTypeLiquidityPoolConstantProduct { - return fmt.Errorf("unknown liquidity pool type %d", lpp.Type) - } - cp := lpp.ConstantProduct - poolID, err := xdr.NewPoolId(cp.AssetA, cp.AssetB, cp.Fee) - if err != nil { - return err - } - - result["liquidity_pool_id"] = o.PoolIDToString(poolID) - - return nil -} - -func (o *LedgerOperation) AllowTrustDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetAllowTrustOp() - if !ok { - return details, fmt.Errorf("could not access AllowTrust info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAssetDetails(details, op.Asset.ToAsset(o.sourceAccountXDR().ToAccountId()), ""); err != nil { - return details, err - } - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "trustee"); err != nil { - return details, err - } - details["trustor"] = op.Trustor.Address() - shouldAuth := xdr.TrustLineFlags(op.Authorize).IsAuthorized() - details["authorize"] = shouldAuth - shouldAuthLiabilities := xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag() - if shouldAuthLiabilities { - details["authorize_to_maintain_liabilities"] = shouldAuthLiabilities - } - shouldClawbackEnabled := xdr.TrustLineFlags(op.Authorize).IsClawbackEnabledFlag() - if shouldClawbackEnabled { - details["clawback_enabled"] = shouldClawbackEnabled - } - - return details, nil -} - -func (o *LedgerOperation) AccountMergeDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - destinationAccount, ok := o.Operation.Body.GetDestination() - if !ok { - return details, fmt.Errorf("could not access Destination info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "account"); err != nil { - return details, err - } - if err := o.addAccountAndMuxedAccountDetails(details, destinationAccount, "into"); err != nil { - return details, err } - - return details, nil + return muxedAccountAddress, muxedAccountID, nil } -// Inflation operations don't have information that affects the details struct -func (o *LedgerOperation) InflationDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - return details, nil +type LedgerKeyDetail struct { + AccountID string `json:"account_id"` + ClaimableBalanceID string `json:"claimable_balance_id"` + DataAccountID string `json:"data_account_id"` + DataName string `json:"data_name"` + OfferID int64 `json:"offer_id"` + TrustlineAccountID string `json:"trustline_account_id"` + TrustlineLiquidityPoolID string `json:"trustline_liquidity_pool_id"` + TrustlineAssetCode string `json:"trustline_asset_code"` + TrustlineAssetIssuer string `json:"trustline_asset_issuer"` + TrustlineAssetType string `json:"trustline_asset_type"` + LiquidityPoolID string `json:"liquidity_pool_id"` } -func (o *LedgerOperation) ManageDataDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetManageDataOp() - if !ok { - return details, fmt.Errorf("could not access GetManageData info for this operation (index %d)", o.OperationIndex) - } - - details["name"] = string(op.DataName) - if op.DataValue != nil { - details["value"] = base64.StdEncoding.EncodeToString(*op.DataValue) - } else { - details["value"] = nil - } - - return details, nil -} - -func (o *LedgerOperation) BumpSequenceDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetBumpSequenceOp() - if !ok { - return details, fmt.Errorf("could not access BumpSequence info for this operation (index %d)", o.OperationIndex) - } - - details["bump_to"] = fmt.Sprintf("%d", op.BumpTo) - - return details, nil -} -func (o *LedgerOperation) CreateClaimableBalanceDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetCreateClaimableBalanceOp() - if !ok { - return details, fmt.Errorf("could not access CreateClaimableBalance info for this operation (index %d)", o.OperationIndex) - } - - details["asset"] = op.Asset.StringCanonical() - err := o.addAssetDetails(details, op.Asset, "") - if err != nil { - return details, err - } - - details["amount"] = int64(op.Amount) - details["claimants"] = o.TransformClaimants(op.Claimants) - - return details, nil -} - -func (o *LedgerOperation) ClaimClaimableBalanceDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetClaimClaimableBalanceOp() - if !ok { - return details, fmt.Errorf("could not access ClaimClaimableBalance info for this operation (index %d)", o.OperationIndex) - } - - balanceID, err := xdr.MarshalHex(op.BalanceId) - if err != nil { - return details, fmt.Errorf("invalid balanceId in op: %d", o.OperationIndex) - } - details["balance_id"] = balanceID - if err := o.addAccountAndMuxedAccountDetails(details, o.sourceAccountXDR(), "claimant"); err != nil { - return details, err - } - - return details, nil -} - -func (o *LedgerOperation) BeginSponsoringFutureReservesDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetBeginSponsoringFutureReservesOp() - if !ok { - return details, fmt.Errorf("could not access BeginSponsoringFutureReserves info for this operation (index %d)", o.OperationIndex) - } - - details["sponsored_id"] = op.SponsoredId.Address() - - return details, nil -} - -func (o *LedgerOperation) EndSponsoringFutureReserveDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - beginSponsorOp := o.findInitatingBeginSponsoringOp() - if beginSponsorOp != nil { - beginSponsorshipSource := o.sourceAccountXDR() - if err := o.addAccountAndMuxedAccountDetails(details, beginSponsorshipSource, "begin_sponsor"); err != nil { - return details, err - } - } - - return details, nil -} - -func (o *LedgerOperation) findInitatingBeginSponsoringOp() *SponsorshipOutput { - if !o.Transaction.Successful() { - // Failed transactions may not have a compliant sandwich structure - // we can rely on (e.g. invalid nesting or a being operation with the wrong sponsoree ID) - // and thus we bail out since we could return incorrect information. - return nil - } - sponsoree := o.sourceAccountXDR().ToAccountId() - operations := o.Transaction.Envelope.Operations() - for i := int(o.OperationIndex) - 1; i >= 0; i-- { - if beginOp, ok := operations[i].Body.GetBeginSponsoringFutureReservesOp(); ok && - beginOp.SponsoredId.Address() == sponsoree.Address() { - result := SponsorshipOutput{ - Operation: operations[i], - OperationIndex: uint32(i), - } - return &result - } - } - return nil -} - -func (o *LedgerOperation) RevokeSponsorshipDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetRevokeSponsorshipOp() - if !ok { - return details, fmt.Errorf("could not access RevokeSponsorship info for this operation (index %d)", o.OperationIndex) - } - - switch op.Type { - case xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry: - if err := o.addLedgerKeyToDetails(details, *op.LedgerKey); err != nil { - return details, err - } - case xdr.RevokeSponsorshipTypeRevokeSponsorshipSigner: - details["signer_account_id"] = op.Signer.AccountId.Address() - details["signer_key"] = op.Signer.SignerKey.Address() - } - - return details, nil -} +func addLedgerKeyToDetails(ledgerKey xdr.LedgerKey) (LedgerKeyDetail, error) { + var err error + var ledgerKeyDetail LedgerKeyDetail -func (o *LedgerOperation) addLedgerKeyToDetails(result map[string]interface{}, ledgerKey xdr.LedgerKey) error { switch ledgerKey.Type { case xdr.LedgerEntryTypeAccount: - result["account_id"] = ledgerKey.Account.AccountId.Address() + ledgerKeyDetail.AccountID = ledgerKey.Account.AccountId.Address() case xdr.LedgerEntryTypeClaimableBalance: - marshalHex, err := xdr.MarshalHex(ledgerKey.ClaimableBalance.BalanceId) + var marshalHex string + marshalHex, err = xdr.MarshalHex(ledgerKey.ClaimableBalance.BalanceId) if err != nil { - return fmt.Errorf("in claimable balance: %w", err) + return LedgerKeyDetail{}, fmt.Errorf("in claimable balance: %w", err) } - result["claimable_balance_id"] = marshalHex + ledgerKeyDetail.ClaimableBalanceID = marshalHex case xdr.LedgerEntryTypeData: - result["data_account_id"] = ledgerKey.Data.AccountId.Address() - result["data_name"] = string(ledgerKey.Data.DataName) + ledgerKeyDetail.DataAccountID = ledgerKey.Data.AccountId.Address() + ledgerKeyDetail.DataName = string(ledgerKey.Data.DataName) case xdr.LedgerEntryTypeOffer: - result["offer_id"] = int64(ledgerKey.Offer.OfferId) + ledgerKeyDetail.OfferID = int64(ledgerKey.Offer.OfferId) case xdr.LedgerEntryTypeTrustline: - result["trustline_account_id"] = ledgerKey.TrustLine.AccountId.Address() + ledgerKeyDetail.TrustlineAccountID = ledgerKey.TrustLine.AccountId.Address() if ledgerKey.TrustLine.Asset.Type == xdr.AssetTypeAssetTypePoolShare { - result["trustline_liquidity_pool_id"] = o.PoolIDToString(*ledgerKey.TrustLine.Asset.LiquidityPoolId) + ledgerKeyDetail.TrustlineLiquidityPoolID, err = PoolIDToString(*ledgerKey.TrustLine.Asset.LiquidityPoolId) + if err != nil { + return LedgerKeyDetail{}, err + } } else { - result["trustline_asset"] = ledgerKey.TrustLine.Asset.ToAsset().StringCanonical() + var assetCode, assetIssuer, assetType string + err = ledgerKey.TrustLine.Asset.ToAsset().Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return LedgerKeyDetail{}, err + } + + ledgerKeyDetail.TrustlineAssetCode = assetCode + ledgerKeyDetail.TrustlineAssetIssuer = assetIssuer + ledgerKeyDetail.TrustlineAssetType = assetType } case xdr.LedgerEntryTypeLiquidityPool: - result["liquidity_pool_id"] = o.PoolIDToString(ledgerKey.LiquidityPool.LiquidityPoolId) - } - return nil -} - -func (o *LedgerOperation) ClawbackDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetClawbackOp() - if !ok { - return details, fmt.Errorf("could not access Clawback info for this operation (index %d)", o.OperationIndex) - } - - if err := o.addAssetDetails(details, op.Asset, ""); err != nil { - return details, err - } - if err := o.addAccountAndMuxedAccountDetails(details, op.From, "from"); err != nil { - return details, err - } - details["amount"] = int64(op.Amount) - - return details, nil -} -func (o *LedgerOperation) ClawbackClaimableBalanceDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetClawbackClaimableBalanceOp() - if !ok { - return details, fmt.Errorf("could not access ClawbackClaimableBalance info for this operation (index %d)", o.OperationIndex) - } - - balanceID, err := xdr.MarshalHex(op.BalanceId) - if err != nil { - return details, fmt.Errorf("invalid balanceId in op: %d", o.OperationIndex) - } - details["balance_id"] = balanceID - - return details, nil -} -func (o *LedgerOperation) SetTrustLineFlagsDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetSetTrustLineFlagsOp() - if !ok { - return details, fmt.Errorf("could not access SetTrustLineFlags info for this operation (index %d)", o.OperationIndex) - } - - details["trustor"] = op.Trustor.Address() - if err := o.addAssetDetails(details, op.Asset, ""); err != nil { - return details, err - } - if op.SetFlags > 0 { - o.addTrustLineFlagToDetails(details, xdr.TrustLineFlags(op.SetFlags), "set") - - } - if op.ClearFlags > 0 { - o.addTrustLineFlagToDetails(details, xdr.TrustLineFlags(op.ClearFlags), "clear") - } - - return details, nil -} - -func (o *LedgerOperation) addTrustLineFlagToDetails(result map[string]interface{}, f xdr.TrustLineFlags, prefix string) { - var ( - n []int32 - s []string - ) - - if f.IsAuthorized() { - n = append(n, int32(xdr.TrustLineFlagsAuthorizedFlag)) - s = append(s, "authorized") - } - - if f.IsAuthorizedToMaintainLiabilitiesFlag() { - n = append(n, int32(xdr.TrustLineFlagsAuthorizedToMaintainLiabilitiesFlag)) - s = append(s, "authorized_to_maintain_liabilities") - } - - if f.IsClawbackEnabledFlag() { - n = append(n, int32(xdr.TrustLineFlagsTrustlineClawbackEnabledFlag)) - s = append(s, "clawback_enabled") - } - - prefix = o.FormatPrefix(prefix) - result[prefix+"flags"] = n - result[prefix+"flags_s"] = s -} - -func (o *LedgerOperation) LiquidityPoolDepositDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetLiquidityPoolDepositOp() - if !ok { - return details, fmt.Errorf("could not access LiquidityPoolDeposit info for this operation (index %d)", o.OperationIndex) - } - - details["liquidity_pool_id"] = o.PoolIDToString(op.LiquidityPoolId) - var ( - assetA, assetB xdr.Asset - depositedA, depositedB xdr.Int64 - sharesReceived xdr.Int64 - ) - if o.Transaction.Successful() { - // we will use the defaults (omitted asset and 0 amounts) if the transaction failed - lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + ledgerKeyDetail.LiquidityPoolID, err = PoolIDToString(ledgerKey.LiquidityPool.LiquidityPoolId) if err != nil { - return nil, err + return LedgerKeyDetail{}, err } - params := lp.Body.ConstantProduct.Params - assetA, assetB = params.AssetA, params.AssetB - depositedA, depositedB = delta.ReserveA, delta.ReserveB - sharesReceived = delta.TotalPoolShares - } - - // Process ReserveA Details - if err := o.addAssetDetails(details, assetA, "reserve_a"); err != nil { - return details, err - } - details["reserve_a_max_amount"] = int64(op.MaxAmountA) - depositA, err := strconv.ParseFloat(amount.String(depositedA), 64) - if err != nil { - return details, err - } - details["reserve_a_deposit_amount"] = depositA - - //Process ReserveB Details - if err := o.addAssetDetails(details, assetB, "reserve_b"); err != nil { - return details, err - } - details["reserve_b_max_amount"] = int64(op.MaxAmountB) - depositB, err := strconv.ParseFloat(amount.String(depositedB), 64) - if err != nil { - return details, err - } - details["reserve_b_deposit_amount"] = depositB - - if err := o.addPriceDetails(details, op.MinPrice, "min"); err != nil { - return details, err - } - if err := o.addPriceDetails(details, op.MaxPrice, "max"); err != nil { - return details, err - } - - sharesToFloat, err := strconv.ParseFloat(amount.String(sharesReceived), 64) - if err != nil { - return details, err } - details["shares_received"] = sharesToFloat - return details, nil + return ledgerKeyDetail, nil } -// operation xdr.Operation, operationIndex int32, transaction LedgerTransaction, ledgerSeq int32 func (o *LedgerOperation) getLiquidityPoolAndProductDelta(lpID *xdr.PoolId) (*xdr.LiquidityPoolEntry, *LiquidityPoolDelta, error) { changes, err := o.Transaction.GetOperationChanges(uint32(o.OperationIndex)) if err != nil { @@ -985,218 +361,6 @@ func (o *LedgerOperation) getLiquidityPoolAndProductDelta(lpID *xdr.PoolId) (*xd return nil, nil, fmt.Errorf("liquidity pool change not found") } -func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetLiquidityPoolWithdrawOp() - if !ok { - return details, fmt.Errorf("could not access LiquidityPoolWithdraw info for this operation (index %d)", o.OperationIndex) - } - - details["liquidity_pool_id"] = o.PoolIDToString(op.LiquidityPoolId) - var ( - assetA, assetB xdr.Asset - receivedA, receivedB xdr.Int64 - ) - if o.Transaction.Successful() { - // we will use the defaults (omitted asset and 0 amounts) if the transaction failed - lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) - if err != nil { - return nil, err - } - params := lp.Body.ConstantProduct.Params - assetA, assetB = params.AssetA, params.AssetB - receivedA, receivedB = -delta.ReserveA, -delta.ReserveB - } - // Process AssetA Details - if err := o.addAssetDetails(details, assetA, "reserve_a"); err != nil { - return details, err - } - details["reserve_a_min_amount"] = int64(op.MinAmountA) - details["reserve_a_withdraw_amount"] = int64(receivedA) - - // Process AssetB Details - if err := o.addAssetDetails(details, assetB, "reserve_b"); err != nil { - return details, err - } - details["reserve_b_min_amount"] = int64(op.MinAmountB) - details["reserve_b_withdraw_amount"] = int64(receivedB) - - details["shares"] = int64(op.Amount) - - return details, nil -} - -func (o *LedgerOperation) InvokeHostFunctionDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetInvokeHostFunctionOp() - if !ok { - return details, fmt.Errorf("could not access InvokeHostFunction info for this operation (index %d)", o.OperationIndex) - } - - details["function"] = op.HostFunction.Type.String() - - switch op.HostFunction.Type { - case xdr.HostFunctionTypeHostFunctionTypeInvokeContract: - invokeArgs := op.HostFunction.MustInvokeContract() - args := make([]xdr.ScVal, 0, len(invokeArgs.Args)+2) - args = append(args, xdr.ScVal{Type: xdr.ScValTypeScvAddress, Address: &invokeArgs.ContractAddress}) - args = append(args, xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &invokeArgs.FunctionName}) - args = append(args, invokeArgs.Args...) - - details["type"] = "invoke_contract" - - contractId, err := invokeArgs.ContractAddress.String() - if err != nil { - return nil, err - } - - details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() - details["contract_id"] = contractId - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - details["contract_code_hash"] = contractCodeHash - } - - details["parameters"], details["parameters_decoded"] = o.serializeParameters(args) - - balanceChanges, err := o.parseAssetBalanceChangesFromContractEvents() - if err != nil { - return nil, err - } - - details["asset_balance_changes"] = balanceChanges - - case xdr.HostFunctionTypeHostFunctionTypeCreateContract: - args := op.HostFunction.MustCreateContract() - details["type"] = "create_contract" - - details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() - - var contractID string - contractID, ok = o.Transaction.contractIdFromTxEnvelope() - if ok { - details["contract_id"] = contractID - } - - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - details["contract_code_hash"] = contractCodeHash - } - - preimageTypeMap, err := o.switchContractIdPreimageType(args.ContractIdPreimage) - if err != nil { - return details, nil - } - - for key, val := range preimageTypeMap { - if _, ok := preimageTypeMap[key]; ok { - details[key] = val - } - } - case xdr.HostFunctionTypeHostFunctionTypeUploadContractWasm: - details["type"] = "upload_wasm" - details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() - - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - details["contract_code_hash"] = contractCodeHash - } - case xdr.HostFunctionTypeHostFunctionTypeCreateContractV2: - args := op.HostFunction.MustCreateContractV2() - details["type"] = "create_contract_v2" - - details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() - - var contractID string - contractID, ok = o.Transaction.contractIdFromTxEnvelope() - if ok { - details["contract_id"] = contractID - } - - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - details["contract_code_hash"] = contractCodeHash - } - - // ConstructorArgs is a list of ScVals - // This will initially be handled the same as InvokeContractParams until a different - // model is found necessary. - constructorArgs := args.ConstructorArgs - details["parameters"], details["parameters_decoded"] = o.serializeParameters(constructorArgs) - - preimageTypeMap, err := o.switchContractIdPreimageType(args.ContractIdPreimage) - if err != nil { - return details, nil - } - - for key, val := range preimageTypeMap { - if _, ok := preimageTypeMap[key]; ok { - details[key] = val - } - } - default: - panic(fmt.Errorf("unknown host function type: %s", op.HostFunction.Type)) - } - - return details, nil -} - -func (o *LedgerOperation) ExtendFootprintTtlDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - op, ok := o.Operation.Body.GetExtendFootprintTtlOp() - if !ok { - return details, fmt.Errorf("could not access ExtendFootprintTtl info for this operation (index %d)", o.OperationIndex) - } - - details["type"] = "extend_footprint_ttl" - details["extend_to"] = int32(op.ExtendTo) - - details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() - - var contractID string - contractID, ok = o.Transaction.contractIdFromTxEnvelope() - if ok { - details["contract_id"] = contractID - } - - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - details["contract_code_hash"] = contractCodeHash - } - - return details, nil -} -func (o *LedgerOperation) RestoreFootprintDetails() (map[string]interface{}, error) { - details := map[string]interface{}{} - _, ok := o.Operation.Body.GetRestoreFootprintOp() - if !ok { - return details, fmt.Errorf("could not access RestoreFootprint info for this operation (index %d)", o.OperationIndex) - } - - details["type"] = "restore_footprint" - - details["ledger_key_hash"] = o.Transaction.LedgerKeyHashFromTxEnvelope() - - var contractID string - contractID, ok = o.Transaction.contractIdFromTxEnvelope() - if ok { - details["contract_id"] = contractID - } - - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - details["contract_code_hash"] = contractCodeHash - } - - return details, nil -} - func (o *LedgerOperation) serializeParameters(args []xdr.ScVal) ([]map[string]string, []map[string]string) { params := make([]map[string]string, 0, len(args)) paramsDecoded := make([]map[string]string, 0, len(args)) @@ -1225,8 +389,8 @@ func (o *LedgerOperation) serializeParameters(args []xdr.ScVal) ([]map[string]st return params, paramsDecoded } -func (o *LedgerOperation) parseAssetBalanceChangesFromContractEvents() ([]map[string]interface{}, error) { - balanceChanges := []map[string]interface{}{} +func (o *LedgerOperation) parseAssetBalanceChangesFromContractEvents() ([]BalanceChangeDetail, error) { + balanceChanges := []BalanceChangeDetail{} diagnosticEvents, err := o.Transaction.GetDiagnosticEvents() if err != nil { @@ -1238,21 +402,44 @@ func (o *LedgerOperation) parseAssetBalanceChangesFromContractEvents() ([]map[st for _, contractEvent := range o.filterEvents(diagnosticEvents) { // Parse the xdr contract event to contractevents.StellarAssetContractEvent model + var err error + var balanceChangeDetail BalanceChangeDetail + var sacEvent contractevents.StellarAssetContractEvent // has some convenience like to/from attributes are expressed in strkey format for accounts(G...) and contracts(C...) - if sacEvent, err := contractevents.NewStellarAssetContractEvent(&contractEvent, o.NetworkPassphrase); err == nil { + if sacEvent, err = contractevents.NewStellarAssetContractEvent(&contractEvent, o.NetworkPassphrase); err == nil { switch sacEvent.GetType() { case contractevents.EventTypeTransfer: transferEvt := sacEvent.(*contractevents.TransferEvent) - balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry(transferEvt.From, transferEvt.To, transferEvt.Amount, transferEvt.Asset, "transfer")) + balanceChangeDetail, err = createSACBalanceChangeEntry(transferEvt.From, transferEvt.To, transferEvt.Amount, transferEvt.Asset, "transfer") + if err != nil { + return []BalanceChangeDetail{}, err + } + + balanceChanges = append(balanceChanges, balanceChangeDetail) case contractevents.EventTypeMint: mintEvt := sacEvent.(*contractevents.MintEvent) - balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry("", mintEvt.To, mintEvt.Amount, mintEvt.Asset, "mint")) + balanceChangeDetail, err = createSACBalanceChangeEntry("", mintEvt.To, mintEvt.Amount, mintEvt.Asset, "mint") + if err != nil { + return []BalanceChangeDetail{}, err + } + + balanceChanges = append(balanceChanges, balanceChangeDetail) case contractevents.EventTypeClawback: clawbackEvt := sacEvent.(*contractevents.ClawbackEvent) - balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry(clawbackEvt.From, "", clawbackEvt.Amount, clawbackEvt.Asset, "clawback")) + balanceChangeDetail, err = createSACBalanceChangeEntry(clawbackEvt.From, "", clawbackEvt.Amount, clawbackEvt.Asset, "clawback") + if err != nil { + return []BalanceChangeDetail{}, err + } + + balanceChanges = append(balanceChanges, balanceChangeDetail) case contractevents.EventTypeBurn: burnEvt := sacEvent.(*contractevents.BurnEvent) - balanceChanges = append(balanceChanges, o.createSACBalanceChangeEntry(burnEvt.From, "", burnEvt.Amount, burnEvt.Asset, "burn")) + balanceChangeDetail, err = createSACBalanceChangeEntry(burnEvt.From, "", burnEvt.Amount, burnEvt.Asset, "burn") + if err != nil { + return []BalanceChangeDetail{}, err + } + + balanceChanges = append(balanceChanges, balanceChangeDetail) } } } @@ -1271,6 +458,16 @@ func (o *LedgerOperation) filterEvents(diagnosticEvents []xdr.DiagnosticEvent) [ return filtered } +type BalanceChangeDetail struct { + From string `json:"from"` + To string `json:"to"` + Type string `json:"type"` + Amount string `json:"amount"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` +} + // fromAccount - strkey format of contract or address // toAccount - strkey format of contract or address, or nillable // amountChanged - absolute value that asset balance changed @@ -1278,74 +475,62 @@ func (o *LedgerOperation) filterEvents(diagnosticEvents []xdr.DiagnosticEvent) [ // changeType - the type of source sac event that triggered this change // // return - a balance changed record expressed as map of key/value's -func (o *LedgerOperation) createSACBalanceChangeEntry(fromAccount string, toAccount string, amountChanged xdr.Int128Parts, asset xdr.Asset, changeType string) map[string]interface{} { - balanceChange := map[string]interface{}{} +func createSACBalanceChangeEntry(fromAccount string, toAccount string, amountChanged xdr.Int128Parts, asset xdr.Asset, changeType string) (BalanceChangeDetail, error) { + balanceChangeDetail := BalanceChangeDetail{ + Type: changeType, + Amount: amount.String128(amountChanged), + } if fromAccount != "" { - balanceChange["from"] = fromAccount + balanceChangeDetail.From = fromAccount } if toAccount != "" { - balanceChange["to"] = toAccount + balanceChangeDetail.To = toAccount } - balanceChange["type"] = changeType - balanceChange["amount"] = amount.String128(amountChanged) - o.addAssetDetails(balanceChange, asset, "") - return balanceChange -} - -// addAssetDetails sets the details for `a` on `result` using keys with `prefix` -func (o *LedgerOperation) addAssetDetails(result map[string]interface{}, a xdr.Asset, prefix string) error { - var ( - assetType string - code string - issuer string - ) - err := a.Extract(&assetType, &code, &issuer) + var assetCode, assetIssuer, assetType string + err := asset.Extract(&assetType, &assetCode, &assetIssuer) if err != nil { - err = fmt.Errorf("xdr.Asset.Extract error: %w", err) - return err + return BalanceChangeDetail{}, err } - prefix = o.FormatPrefix(prefix) - result[prefix+"asset_type"] = assetType - - if a.Type == xdr.AssetTypeAssetTypeNative { - result[prefix+"asset_id"] = int64(-5706705804583548011) - return nil - } - - result[prefix+"asset_code"] = code - result[prefix+"asset_issuer"] = issuer - result[prefix+"asset_id"] = o.FarmHashAsset(code, issuer, assetType) - - return nil + return balanceChangeDetail, nil } -func (o *LedgerOperation) switchContractIdPreimageType(contractIdPreimage xdr.ContractIdPreimage) (map[string]interface{}, error) { - details := map[string]interface{}{} +type PreImageDetails struct { + From string `json:"from"` + Address string `json:"address"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` +} +func switchContractIdPreimageType(contractIdPreimage xdr.ContractIdPreimage) (PreImageDetails, error) { switch contractIdPreimage.Type { case xdr.ContractIdPreimageTypeContractIdPreimageFromAddress: fromAddress := contractIdPreimage.MustFromAddress() address, err := fromAddress.Address.String() if err != nil { - return details, err + return PreImageDetails{}, err } - details["from"] = "address" - details["address"] = address + return PreImageDetails{ + From: "address", + Address: address, + }, nil case xdr.ContractIdPreimageTypeContractIdPreimageFromAsset: - details["from"] = "asset" - details["asset"] = contractIdPreimage.MustFromAsset().StringCanonical() - err := o.addAssetDetails(details, contractIdPreimage.MustFromAsset(), "") - if err != nil { - return details, err - } + var assetCode, assetIssuer, assetType string + contractIdPreimage.MustFromAsset().Extract(&assetType, &assetCode, &assetIssuer) + + return PreImageDetails{ + From: "asset", + AssetCode: assetCode, + AssetIssuer: assetIssuer, + AssetType: assetType, + }, nil + default: - panic(fmt.Errorf("unknown contract id type: %s", contractIdPreimage.Type)) + return PreImageDetails{}, fmt.Errorf("unknown contract id type: %s", contractIdPreimage.Type) } - - return details, nil } func (o *LedgerOperation) ConvertStroopValueToReal(input int64) float64 { @@ -1400,8 +585,8 @@ type Price struct { Denominator int32 `json:"d"` } -func (o *LedgerOperation) PoolIDToString(id xdr.PoolId) string { - return xdr.Hash(id).HexString() +func PoolIDToString(id xdr.PoolId) (string, error) { + return xdr.MarshalBase64(id) } type Claimant struct { @@ -1409,7 +594,7 @@ type Claimant struct { Predicate xdr.ClaimPredicate `json:"predicate"` } -func (o *LedgerOperation) TransformClaimants(claimants []xdr.Claimant) []Claimant { +func transformClaimants(claimants []xdr.Claimant) []Claimant { var transformed []Claimant for _, c := range claimants { switch c.Type { diff --git a/ingest/liquidity_pool_deposit_details.go b/ingest/liquidity_pool_deposit_details.go new file mode 100644 index 0000000000..92f4322a98 --- /dev/null +++ b/ingest/liquidity_pool_deposit_details.go @@ -0,0 +1,110 @@ +package ingest + +import ( + "fmt" + "strconv" + + "github.com/stellar/go/xdr" +) + +type LiquidityPoolDepositDetail struct { + LiquidityPoolID string `json:"liquidity_pool_id"` + ReserveAAssetCode string `json:"reserve_a_asset_code"` + ReserveAAssetIssuer string `json:"reserve_a_asset_issuer"` + ReserveAAssetType string `json:"reserve_a_asset_type"` + ReserveAMaxAmount int64 `json:"reserve_a_max_amount"` + ReserveADepositAmount int64 `json:"reserve_a_deposit_amount"` + ReserveBAssetCode string `json:"reserve_b_asset_code"` + ReserveBAssetIssuer string `json:"reserve_b_asset_issuer"` + ReserveBAssetType string `json:"reserve_b_asset_type"` + ReserveBMaxAmount int64 `json:"reserve_b_max_amount"` + ReserveBDepositAmount int64 `json:"reserve_b_deposit_amount"` + MinPriceN int32 `json:"min_price_n"` + MinPriceD int32 `json:"min_price_d"` + MinPrice float64 `json:"min_price"` + MaxPriceN int32 `json:"max_price_n"` + MaxPriceD int32 `json:"max_price_d"` + MaxPrice float64 `json:"max_price"` + SharesReceived int64 `json:"shares_received"` +} + +func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDetail, error) { + op, ok := o.Operation.Body.GetLiquidityPoolDepositOp() + if !ok { + return LiquidityPoolDepositDetail{}, fmt.Errorf("could not access LiquidityPoolDeposit info for this operation (index %d)", o.OperationIndex) + } + + liquidityPoolDepositDetail := LiquidityPoolDepositDetail{ + ReserveAMaxAmount: int64(op.MaxAmountA), + ReserveBMaxAmount: int64(op.MaxAmountB), + MinPriceN: int32(op.MinPrice.N), + MinPriceD: int32(op.MinPrice.D), + MaxPriceN: int32(op.MaxPrice.N), + MaxPriceD: int32(op.MaxPrice.D), + } + + var err error + var liquidityPoolID string + liquidityPoolID, err = PoolIDToString(op.LiquidityPoolId) + if err != nil { + return LiquidityPoolDepositDetail{}, err + } + + liquidityPoolDepositDetail.LiquidityPoolID = liquidityPoolID + + var ( + assetA, assetB xdr.Asset + depositedA, depositedB xdr.Int64 + sharesReceived xdr.Int64 + ) + if o.Transaction.Successful() { + // we will use the defaults (omitted asset and 0 amounts) if the transaction failed + lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + if err != nil { + return LiquidityPoolDepositDetail{}, err + } + + params := lp.Body.ConstantProduct.Params + assetA, assetB = params.AssetA, params.AssetB + depositedA, depositedB = delta.ReserveA, delta.ReserveB + sharesReceived = delta.TotalPoolShares + } + + // Process ReserveA Details + var assetACode, assetAIssuer, assetAType string + err = assetA.Extract(&assetAType, &assetACode, &assetAIssuer) + if err != nil { + return LiquidityPoolDepositDetail{}, err + } + + liquidityPoolDepositDetail.ReserveAAssetCode = assetACode + liquidityPoolDepositDetail.ReserveAAssetIssuer = assetAIssuer + liquidityPoolDepositDetail.ReserveAAssetType = assetAType + liquidityPoolDepositDetail.ReserveADepositAmount = int64(depositedA) + + //Process ReserveB Details + var assetBCode, assetBIssuer, assetBType string + err = assetB.Extract(&assetBType, &assetBCode, &assetBIssuer) + if err != nil { + return LiquidityPoolDepositDetail{}, err + } + + liquidityPoolDepositDetail.ReserveBAssetCode = assetBCode + liquidityPoolDepositDetail.ReserveBAssetIssuer = assetBIssuer + liquidityPoolDepositDetail.ReserveBAssetType = assetBType + liquidityPoolDepositDetail.ReserveBDepositAmount = int64(depositedB) + + liquidityPoolDepositDetail.MinPrice, err = strconv.ParseFloat(op.MinPrice.String(), 64) + if err != nil { + return LiquidityPoolDepositDetail{}, err + } + + liquidityPoolDepositDetail.MaxPrice, err = strconv.ParseFloat(op.MaxPrice.String(), 64) + if err != nil { + return LiquidityPoolDepositDetail{}, err + } + + liquidityPoolDepositDetail.SharesReceived = int64(sharesReceived) + + return liquidityPoolDepositDetail, nil +} diff --git a/ingest/liquidity_pool_withdraw_details.go b/ingest/liquidity_pool_withdraw_details.go new file mode 100644 index 0000000000..c250494e3d --- /dev/null +++ b/ingest/liquidity_pool_withdraw_details.go @@ -0,0 +1,85 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type LiquidityPoolWithdrawDetail struct { + LiquidityPoolID string `json:"liquidity_pool_id"` + ReserveAAssetCode string `json:"reserve_a_asset_code"` + ReserveAAssetIssuer string `json:"reserve_a_asset_issuer"` + ReserveAAssetType string `json:"reserve_a_asset_type"` + ReserveAMinAmount int64 `json:"reserve_a_min_amount"` + ReserveAWithdrawAmount int64 `json:"reserve_a_withdraw_amount"` + ReserveBAssetCode string `json:"reserve_b_asset_code"` + ReserveBAssetIssuer string `json:"reserve_b_asset_issuer"` + ReserveBAssetType string `json:"reserve_b_asset_type"` + ReserveBMinAmount int64 `json:"reserve_b_min_amount"` + ReserveBWithdrawAmount int64 `json:"reserve_b_withdraw_amount"` + Shares int64 `json:"shares"` +} + +func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawDetail, error) { + op, ok := o.Operation.Body.GetLiquidityPoolWithdrawOp() + if !ok { + return LiquidityPoolWithdrawDetail{}, fmt.Errorf("could not access LiquidityPoolWithdraw info for this operation (index %d)", o.OperationIndex) + } + + liquidityPoolWithdrawDetail := LiquidityPoolWithdrawDetail{ + ReserveAMinAmount: int64(op.MinAmountA), + ReserveBMinAmount: int64(op.MinAmountB), + Shares: int64(op.Amount), + } + + var err error + var liquidityPoolID string + liquidityPoolID, err = PoolIDToString(op.LiquidityPoolId) + if err != nil { + return LiquidityPoolWithdrawDetail{}, err + } + + liquidityPoolWithdrawDetail.LiquidityPoolID = liquidityPoolID + + var ( + assetA, assetB xdr.Asset + receivedA, receivedB xdr.Int64 + ) + if o.Transaction.Successful() { + // we will use the defaults (omitted asset and 0 amounts) if the transaction failed + lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + if err != nil { + return LiquidityPoolWithdrawDetail{}, err + } + params := lp.Body.ConstantProduct.Params + assetA, assetB = params.AssetA, params.AssetB + receivedA, receivedB = -delta.ReserveA, -delta.ReserveB + } + + // Process AssetA Details + var assetACode, assetAIssuer, assetAType string + err = assetA.Extract(&assetAType, &assetACode, &assetAIssuer) + if err != nil { + return LiquidityPoolWithdrawDetail{}, err + } + + liquidityPoolWithdrawDetail.ReserveAAssetCode = assetACode + liquidityPoolWithdrawDetail.ReserveAAssetIssuer = assetAIssuer + liquidityPoolWithdrawDetail.ReserveAAssetType = assetAType + liquidityPoolWithdrawDetail.ReserveAWithdrawAmount = int64(receivedA) + + // Process AssetB Details + var assetBCode, assetBIssuer, assetBType string + err = assetB.Extract(&assetBType, &assetBCode, &assetBIssuer) + if err != nil { + return LiquidityPoolWithdrawDetail{}, err + } + + liquidityPoolWithdrawDetail.ReserveBAssetCode = assetBCode + liquidityPoolWithdrawDetail.ReserveBAssetIssuer = assetBIssuer + liquidityPoolWithdrawDetail.ReserveBAssetType = assetBType + liquidityPoolWithdrawDetail.ReserveAWithdrawAmount = int64(receivedB) + + return liquidityPoolWithdrawDetail, nil +} diff --git a/ingest/manage_buy_offer_details.go b/ingest/manage_buy_offer_details.go new file mode 100644 index 0000000000..21c9ac35ab --- /dev/null +++ b/ingest/manage_buy_offer_details.go @@ -0,0 +1,62 @@ +package ingest + +import ( + "fmt" + "strconv" +) + +type ManageBuyOffer struct { + OfferID int64 `json:"offer_id"` + Amount int64 `json:"amount"` + PriceN int32 `json:"price_n"` + PriceD int32 `json:"price_d"` + Price float64 `json:"price"` + BuyingAssetCode string `json:"buying_asset_code"` + BuyingAssetIssuer string `json:"buying_asset_issuer"` + BuyingAssetType string `json:"buying_asset_type"` + SellingAssetCode string `json:"selling_asset_code"` + SellingAssetIssuer string `json:"selling_asset_issuer"` + SellingAssetType string `json:"selling_asset_type"` +} + +func (o *LedgerOperation) ManageBuyOfferDetails() (ManageBuyOffer, error) { + op, ok := o.Operation.Body.GetManageBuyOfferOp() + if !ok { + return ManageBuyOffer{}, fmt.Errorf("could not access ManageBuyOffer info for this operation (index %d)", o.OperationIndex) + } + + manageBuyOffer := ManageBuyOffer{ + OfferID: int64(op.OfferId), + Amount: int64(op.BuyAmount), + PriceN: int32(op.Price.N), + PriceD: int32(op.Price.D), + } + + var err error + manageBuyOffer.Price, err = strconv.ParseFloat(op.Price.String(), 64) + if err != nil { + return ManageBuyOffer{}, err + } + + var buyingAssetCode, buyingAssetIssuer, buyingAssetType string + err = op.Buying.Extract(&buyingAssetType, &buyingAssetCode, &buyingAssetIssuer) + if err != nil { + return ManageBuyOffer{}, err + } + + manageBuyOffer.BuyingAssetCode = buyingAssetCode + manageBuyOffer.BuyingAssetIssuer = buyingAssetIssuer + manageBuyOffer.BuyingAssetType = buyingAssetType + + var sellingAssetCode, sellingAssetIssuer, sellingAssetType string + err = op.Selling.Extract(&sellingAssetType, &sellingAssetCode, &sellingAssetIssuer) + if err != nil { + return ManageBuyOffer{}, err + } + + manageBuyOffer.SellingAssetCode = sellingAssetCode + manageBuyOffer.SellingAssetIssuer = sellingAssetIssuer + manageBuyOffer.SellingAssetType = sellingAssetType + + return manageBuyOffer, nil +} diff --git a/ingest/manage_data_details.go b/ingest/manage_data_details.go new file mode 100644 index 0000000000..abf523fd6a --- /dev/null +++ b/ingest/manage_data_details.go @@ -0,0 +1,28 @@ +package ingest + +import ( + "encoding/base64" + "fmt" +) + +type ManageDataDetail struct { + Name string `json:"name"` + Value string `json:value"` +} + +func (o *LedgerOperation) ManageDataDetails() (ManageDataDetail, error) { + op, ok := o.Operation.Body.GetManageDataOp() + if !ok { + return ManageDataDetail{}, fmt.Errorf("could not access GetManageData info for this operation (index %d)", o.OperationIndex) + } + + manageDataDetail := ManageDataDetail{ + Name: string(op.DataName), + } + + if op.DataValue != nil { + manageDataDetail.Value = base64.StdEncoding.EncodeToString(*op.DataValue) + } + + return manageDataDetail, nil +} diff --git a/ingest/manage_sell_offer_details.go b/ingest/manage_sell_offer_details.go new file mode 100644 index 0000000000..9b33fd4b03 --- /dev/null +++ b/ingest/manage_sell_offer_details.go @@ -0,0 +1,63 @@ +package ingest + +import ( + "fmt" + "strconv" +) + +type ManageSellOffer struct { + OfferID int64 `json:"offer_id"` + Amount int64 `json:"amount"` + PriceN int32 `json:"price_n"` + PriceD int32 `json:"price_d"` + Price float64 `json:"price"` + BuyingAssetCode string `json:"buying_asset_code"` + BuyingAssetIssuer string `json:"buying_asset_issuer"` + BuyingAssetType string `json:"buying_asset_type"` + SellingAssetCode string `json:"selling_asset_code"` + SellingAssetIssuer string `json:"selling_asset_issuer"` + SellingAssetType string `json:"selling_asset_type"` +} + +func (o *LedgerOperation) ManageSellOfferDetails() (ManageSellOffer, error) { + op, ok := o.Operation.Body.GetManageSellOfferOp() + if !ok { + return ManageSellOffer{}, fmt.Errorf("could not access ManageSellOffer info for this operation (index %d)", o.OperationIndex) + } + + manageSellOffer := ManageSellOffer{ + OfferID: int64(op.OfferId), + Amount: int64(op.Amount), + PriceN: int32(op.Price.N), + PriceD: int32(op.Price.D), + } + + var err error + manageSellOffer.Price, err = strconv.ParseFloat(op.Price.String(), 64) + if err != nil { + return ManageSellOffer{}, err + } + + var buyingAssetCode, buyingAssetIssuer, buyingAssetType string + err = op.Buying.Extract(&buyingAssetType, &buyingAssetCode, &buyingAssetIssuer) + if err != nil { + return ManageSellOffer{}, err + } + + manageSellOffer.BuyingAssetCode = buyingAssetCode + manageSellOffer.BuyingAssetIssuer = buyingAssetIssuer + manageSellOffer.BuyingAssetType = buyingAssetType + + var sellingAssetCode, sellingAssetIssuer, sellingAssetType string + err = op.Selling.Extract(&sellingAssetType, &sellingAssetCode, &sellingAssetIssuer) + if err != nil { + return ManageSellOffer{}, err + } + + manageSellOffer.SellingAssetCode = sellingAssetCode + manageSellOffer.SellingAssetIssuer = sellingAssetIssuer + manageSellOffer.SellingAssetType = sellingAssetType + + return manageSellOffer, nil + +} diff --git a/ingest/path_payment_strict_receive_details.go b/ingest/path_payment_strict_receive_details.go new file mode 100644 index 0000000000..3a9eab83d4 --- /dev/null +++ b/ingest/path_payment_strict_receive_details.go @@ -0,0 +1,100 @@ +package ingest + +import ( + "fmt" +) + +type PathPaymentStrictReceiveDetail struct { + From string `json:"from"` + FromMuxed string `json:"from_muxed"` + FromMuxedID uint64 `json:"from_muxed_id"` + To string `json:"to"` + ToMuxed string `json:"to_muxed"` + ToMuxedID uint64 `json:"to_muxed_id"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Amount int64 `json:"amount"` + SourceAssetCode string `json:"source_asset_code"` + SourceAssetIssuer string `json:"source_asset_issuer"` + SourceAssetType string `json:"source_asset_type"` + SourceAmount int64 `json:"source_amount"` + SourceMax int64 `json:"source_max"` + Path []Path `json:"path"` +} + +func (o *LedgerOperation) PathPaymentStrictReceiveDetails() (PathPaymentStrictReceiveDetail, error) { + op, ok := o.Operation.Body.GetPathPaymentStrictReceiveOp() + if !ok { + return PathPaymentStrictReceiveDetail{}, fmt.Errorf("could not access PathPaymentStrictReceive info for this operation (index %d)", o.OperationIndex) + } + + pathPaymentStrictReceiveDetail := PathPaymentStrictReceiveDetail{ + From: o.SourceAccount(), + To: op.Destination.Address(), + Amount: int64(op.DestAmount), + SourceMax: int64(op.SendMax), + } + + var err error + var fromMuxed string + var fromMuxedID uint64 + fromMuxed, fromMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return PathPaymentStrictReceiveDetail{}, err + } + + pathPaymentStrictReceiveDetail.FromMuxed = fromMuxed + pathPaymentStrictReceiveDetail.FromMuxedID = fromMuxedID + + var toMuxed string + var toMuxedID uint64 + toMuxed, toMuxedID, err = getMuxedAccountDetails(op.Destination) + if err != nil { + return PathPaymentStrictReceiveDetail{}, err + } + + pathPaymentStrictReceiveDetail.ToMuxed = toMuxed + pathPaymentStrictReceiveDetail.ToMuxedID = toMuxedID + + var assetCode, assetIssuer, assetType string + err = op.DestAsset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return PathPaymentStrictReceiveDetail{}, err + } + + pathPaymentStrictReceiveDetail.AssetCode = assetCode + pathPaymentStrictReceiveDetail.AssetIssuer = assetIssuer + pathPaymentStrictReceiveDetail.AssetType = assetType + + var sourceAssetCode, sourceAssetIssuer, sourceAssetType string + err = op.SendAsset.Extract(&sourceAssetType, &sourceAssetCode, &sourceAssetIssuer) + if err != nil { + return PathPaymentStrictReceiveDetail{}, err + } + + pathPaymentStrictReceiveDetail.SourceAssetCode = sourceAssetCode + pathPaymentStrictReceiveDetail.SourceAssetIssuer = sourceAssetIssuer + pathPaymentStrictReceiveDetail.SourceAssetType = sourceAssetType + + if o.Transaction.Successful() { + allOperationResults, ok := o.Transaction.Result.OperationResults() + if !ok { + return PathPaymentStrictReceiveDetail{}, fmt.Errorf("could not access any results for this transaction") + } + currentOperationResult := allOperationResults[o.OperationIndex] + resultBody, ok := currentOperationResult.GetTr() + if !ok { + return PathPaymentStrictReceiveDetail{}, fmt.Errorf("could not access result body for this operation (index %d)", o.OperationIndex) + } + result, ok := resultBody.GetPathPaymentStrictReceiveResult() + if !ok { + return PathPaymentStrictReceiveDetail{}, fmt.Errorf("could not access PathPaymentStrictReceive result info for this operation (index %d)", o.OperationIndex) + } + pathPaymentStrictReceiveDetail.SourceAmount = int64(result.SendAmount()) + } + + pathPaymentStrictReceiveDetail.Path = o.TransformPath(op.Path) + + return pathPaymentStrictReceiveDetail, nil +} diff --git a/ingest/path_payment_strict_send_details.go b/ingest/path_payment_strict_send_details.go new file mode 100644 index 0000000000..6b0c28e69f --- /dev/null +++ b/ingest/path_payment_strict_send_details.go @@ -0,0 +1,100 @@ +package ingest + +import ( + "fmt" +) + +type PathPaymentStrictSendDetail struct { + From string `json:"from"` + FromMuxed string `json:"from_muxed"` + FromMuxedID uint64 `json:"from_muxed_id"` + To string `json:"to"` + ToMuxed string `json:"to_muxed"` + ToMuxedID uint64 `json:"to_muxed_id"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Amount int64 `json:"amount"` + SourceAssetCode string `json:"source_asset_code"` + SourceAssetIssuer string `json:"source_asset_issuer"` + SourceAssetType string `json:"source_asset_type"` + SourceAmount int64 `json:"source_amount"` + DestinationMin int64 `json:"destination_min"` + Path []Path `json:"path"` +} + +func (o *LedgerOperation) PathPaymentStrictSendDetails() (PathPaymentStrictSendDetail, error) { + op, ok := o.Operation.Body.GetPathPaymentStrictSendOp() + if !ok { + return PathPaymentStrictSendDetail{}, fmt.Errorf("could not access PathPaymentStrictSend info for this operation (index %d)", o.OperationIndex) + } + + pathPaymentStrictSendDetail := PathPaymentStrictSendDetail{ + From: o.SourceAccount(), + To: op.Destination.Address(), + SourceAmount: int64(op.SendAmount), + DestinationMin: int64(op.DestMin), + } + + var err error + var fromMuxed string + var fromMuxedID uint64 + fromMuxed, fromMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return PathPaymentStrictSendDetail{}, err + } + + pathPaymentStrictSendDetail.FromMuxed = fromMuxed + pathPaymentStrictSendDetail.FromMuxedID = fromMuxedID + + var toMuxed string + var toMuxedID uint64 + toMuxed, toMuxedID, err = getMuxedAccountDetails(op.Destination) + if err != nil { + return PathPaymentStrictSendDetail{}, err + } + + pathPaymentStrictSendDetail.ToMuxed = toMuxed + pathPaymentStrictSendDetail.ToMuxedID = toMuxedID + + var assetCode, assetIssuer, assetType string + err = op.DestAsset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return PathPaymentStrictSendDetail{}, err + } + + pathPaymentStrictSendDetail.AssetCode = assetCode + pathPaymentStrictSendDetail.AssetIssuer = assetIssuer + pathPaymentStrictSendDetail.AssetType = assetType + + var sourceAssetCode, sourceAssetIssuer, sourceAssetType string + err = op.SendAsset.Extract(&sourceAssetType, &sourceAssetCode, &sourceAssetIssuer) + if err != nil { + return PathPaymentStrictSendDetail{}, err + } + + pathPaymentStrictSendDetail.SourceAssetCode = sourceAssetCode + pathPaymentStrictSendDetail.SourceAssetIssuer = sourceAssetIssuer + pathPaymentStrictSendDetail.SourceAssetType = sourceAssetType + + if o.Transaction.Successful() { + allOperationResults, ok := o.Transaction.Result.OperationResults() + if !ok { + return PathPaymentStrictSendDetail{}, fmt.Errorf("could not access any results for this transaction") + } + currentOperationResult := allOperationResults[o.OperationIndex] + resultBody, ok := currentOperationResult.GetTr() + if !ok { + return PathPaymentStrictSendDetail{}, fmt.Errorf("could not access result body for this operation (index %d)", o.OperationIndex) + } + result, ok := resultBody.GetPathPaymentStrictSendResult() + if !ok { + return PathPaymentStrictSendDetail{}, fmt.Errorf("could not access GetPathPaymentStrictSendResult result info for this operation (index %d)", o.OperationIndex) + } + pathPaymentStrictSendDetail.Amount = int64(result.DestAmount()) + } + + pathPaymentStrictSendDetail.Path = o.TransformPath(op.Path) + + return pathPaymentStrictSendDetail, nil +} diff --git a/ingest/payment_details.go b/ingest/payment_details.go new file mode 100644 index 0000000000..c7845ed503 --- /dev/null +++ b/ingest/payment_details.go @@ -0,0 +1,60 @@ +package ingest + +import ( + "fmt" +) + +type PaymentDetail struct { + From string `json:"from"` + FromMuxed string `json:"from_muxed"` + FromMuxedID uint64 `json:"from_muxed_id"` + To string `json:"to"` + ToMuxed string `json:"to_muxed"` + ToMuxedID uint64 `json:"to_muxed_id"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Amount int64 `json:"amount"` +} + +func (o *LedgerOperation) PaymentDetails() (PaymentDetail, error) { + op, ok := o.Operation.Body.GetPaymentOp() + if !ok { + return PaymentDetail{}, fmt.Errorf("could not access Payment info for this operation (index %d)", o.OperationIndex) + } + + paymentDetail := PaymentDetail{ + From: o.SourceAccount(), + To: op.Destination.Address(), + Amount: int64(op.Amount), + } + + var err error + var fromMuxed string + var fromMuxedID uint64 + fromMuxed, fromMuxedID, err = getMuxedAccountDetails(o.sourceAccountXDR()) + if err != nil { + return PaymentDetail{}, err + } + + paymentDetail.FromMuxed = fromMuxed + paymentDetail.FromMuxedID = fromMuxedID + + var toMuxed string + var toMuxedID uint64 + toMuxed, toMuxedID, err = getMuxedAccountDetails(op.Destination) + if err != nil { + return PaymentDetail{}, err + } + + paymentDetail.ToMuxed = toMuxed + paymentDetail.ToMuxedID = toMuxedID + + var assetCode, assetIssuer, assetType string + err = op.Asset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return PaymentDetail{}, err + } + + return paymentDetail, nil +} diff --git a/ingest/restore_footprint_details.go b/ingest/restore_footprint_details.go new file mode 100644 index 0000000000..496308ffad --- /dev/null +++ b/ingest/restore_footprint_details.go @@ -0,0 +1,36 @@ +package ingest + +import "fmt" + +type RestoreFootprintDetail struct { + Type string `json:"type"` + LedgerKeyHash []string `json:"ledger_key_hash"` + ContractID string `json:"contract_id"` + ContractCodeHash string `json:"contract_code_hash"` +} + +func (o *LedgerOperation) RestoreFootprintDetails() (RestoreFootprintDetail, error) { + _, ok := o.Operation.Body.GetRestoreFootprintOp() + if !ok { + return RestoreFootprintDetail{}, fmt.Errorf("could not access RestoreFootprint info for this operation (index %d)", o.OperationIndex) + } + + restoreFootprintDetail := RestoreFootprintDetail{ + Type: "restore_footprint", + LedgerKeyHash: o.Transaction.LedgerKeyHashFromTxEnvelope(), + } + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + restoreFootprintDetail.ContractID = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + restoreFootprintDetail.ContractCodeHash = contractCodeHash + } + + return restoreFootprintDetail, nil +} diff --git a/ingest/revoke_sponsorship_details.go b/ingest/revoke_sponsorship_details.go new file mode 100644 index 0000000000..9ca5a1aefb --- /dev/null +++ b/ingest/revoke_sponsorship_details.go @@ -0,0 +1,37 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type RevokeSponsorshipDetail struct { + SignerAccountID string `json:"signer_account_id"` + SignerKey string `json:"signer_key"` + LedgerKeyDetails LedgerKeyDetail `json:"ledger_key_detail` +} + +func (o *LedgerOperation) RevokeSponsorshipDetails() (RevokeSponsorshipDetail, error) { + op, ok := o.Operation.Body.GetRevokeSponsorshipOp() + if !ok { + return RevokeSponsorshipDetail{}, fmt.Errorf("could not access RevokeSponsorship info for this operation (index %d)", o.OperationIndex) + } + + var revokeSponsorshipDetail RevokeSponsorshipDetail + + switch op.Type { + case xdr.RevokeSponsorshipTypeRevokeSponsorshipLedgerEntry: + ledgerKeyDetail, err := addLedgerKeyToDetails(*op.LedgerKey) + if err != nil { + return RevokeSponsorshipDetail{}, err + } + + revokeSponsorshipDetail.LedgerKeyDetails = ledgerKeyDetail + case xdr.RevokeSponsorshipTypeRevokeSponsorshipSigner: + revokeSponsorshipDetail.SignerAccountID = op.Signer.AccountId.Address() + revokeSponsorshipDetail.SignerKey = op.Signer.SignerKey.Address() + } + + return revokeSponsorshipDetail, nil +} diff --git a/ingest/set_options_details.go b/ingest/set_options_details.go new file mode 100644 index 0000000000..2216e4b9b0 --- /dev/null +++ b/ingest/set_options_details.go @@ -0,0 +1,97 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type SetOptionsDetails struct { + InflationDestination string `json:"inflation_destination"` + MasterKeyWeight uint32 `json:"master_key_weight"` + LowThreshold uint32 `json:"low_threshold"` + MediumThreshold uint32 `json:"medium_threshold"` + HighThreshold uint32 `json:"high_threshold"` + HomeDomain string `json:"home_domain"` + SignerKey string `json:"signer_key"` + SignerWeight uint32 `json:"signer_weight"` + SetFlags []int32 `json:"set_flags"` + SetFlagsString []string `json:"set_flags_string"` + ClearFlags []int32 `json:"clear_flags"` + ClearFlagsString []string `json:"clear_flags_string"` +} + +func (o *LedgerOperation) SetOptionsDetails() (SetOptionsDetails, error) { + op, ok := o.Operation.Body.GetSetOptionsOp() + if !ok { + return SetOptionsDetails{}, fmt.Errorf("could not access GetSetOptions info for this operation (index %d)", o.OperationIndex) + } + + var setOptionsDetail SetOptionsDetails + + if op.InflationDest != nil { + setOptionsDetail.InflationDestination = op.InflationDest.Address() + } + + if op.SetFlags != nil && *op.SetFlags > 0 { + setOptionsDetail.SetFlags, setOptionsDetail.SetFlagsString = addOperationFlagToOperationDetails(uint32(*op.SetFlags)) + } + + if op.ClearFlags != nil && *op.ClearFlags > 0 { + setOptionsDetail.ClearFlags, setOptionsDetail.ClearFlagsString = addOperationFlagToOperationDetails(uint32(*op.ClearFlags)) + } + + if op.MasterWeight != nil { + setOptionsDetail.MasterKeyWeight = uint32(*op.MasterWeight) + } + + if op.LowThreshold != nil { + setOptionsDetail.LowThreshold = uint32(*op.LowThreshold) + } + + if op.MedThreshold != nil { + setOptionsDetail.MediumThreshold = uint32(*op.MedThreshold) + } + + if op.HighThreshold != nil { + setOptionsDetail.HighThreshold = uint32(*op.HighThreshold) + } + + if op.HomeDomain != nil { + setOptionsDetail.HomeDomain = string(*op.HomeDomain) + } + + if op.Signer != nil { + setOptionsDetail.SignerKey = op.Signer.Key.Address() + setOptionsDetail.SignerWeight = uint32(op.Signer.Weight) + } + + return setOptionsDetail, nil +} + +func addOperationFlagToOperationDetails(flag uint32) ([]int32, []string) { + intFlags := make([]int32, 0) + stringFlags := make([]string, 0) + + if (int64(flag) & int64(xdr.AccountFlagsAuthRequiredFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthRequiredFlag)) + stringFlags = append(stringFlags, "auth_required") + } + + if (int64(flag) & int64(xdr.AccountFlagsAuthRevocableFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthRevocableFlag)) + stringFlags = append(stringFlags, "auth_revocable") + } + + if (int64(flag) & int64(xdr.AccountFlagsAuthImmutableFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthImmutableFlag)) + stringFlags = append(stringFlags, "auth_immutable") + } + + if (int64(flag) & int64(xdr.AccountFlagsAuthClawbackEnabledFlag)) > 0 { + intFlags = append(intFlags, int32(xdr.AccountFlagsAuthClawbackEnabledFlag)) + stringFlags = append(stringFlags, "auth_clawback_enabled") + } + + return intFlags, stringFlags +} diff --git a/ingest/set_trustline_flags_details.go b/ingest/set_trustline_flags_details.go new file mode 100644 index 0000000000..6a71c4c22a --- /dev/null +++ b/ingest/set_trustline_flags_details.go @@ -0,0 +1,73 @@ +package ingest + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type SetTrustlineFlagsDetail struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Trustor string `json:"trustor"` + SetFlags []int32 `json:"set_flags"` + SetFlagsString []string `json:"set_flags_string"` + ClearFlags []int32 `json:"clear_flags"` + ClearFlagsString []string `json:"clear_flags_string"` +} + +func (o *LedgerOperation) SetTrustlineFlagsDetails() (SetTrustlineFlagsDetail, error) { + op, ok := o.Operation.Body.GetSetTrustLineFlagsOp() + if !ok { + return SetTrustlineFlagsDetail{}, fmt.Errorf("could not access SetTrustLineFlags info for this operation (index %d)", o.OperationIndex) + } + + setTrustLineFlagsDetail := SetTrustlineFlagsDetail{ + Trustor: op.Trustor.Address(), + } + + var err error + var assetCode, assetIssuer, assetType string + err = op.Asset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return SetTrustlineFlagsDetail{}, err + } + + setTrustLineFlagsDetail.AssetCode = assetCode + setTrustLineFlagsDetail.AssetIssuer = assetIssuer + setTrustLineFlagsDetail.AssetType = assetType + + if op.SetFlags > 0 { + setTrustLineFlagsDetail.SetFlags, setTrustLineFlagsDetail.SetFlagsString = getTrustLineFlagToDetails(xdr.TrustLineFlags(op.SetFlags)) + } + if op.ClearFlags > 0 { + setTrustLineFlagsDetail.ClearFlags, setTrustLineFlagsDetail.ClearFlagsString = getTrustLineFlagToDetails(xdr.TrustLineFlags(op.ClearFlags)) + } + + return setTrustLineFlagsDetail, nil +} + +func getTrustLineFlagToDetails(f xdr.TrustLineFlags) ([]int32, []string) { + var ( + n []int32 + s []string + ) + + if f.IsAuthorized() { + n = append(n, int32(xdr.TrustLineFlagsAuthorizedFlag)) + s = append(s, "authorized") + } + + if f.IsAuthorizedToMaintainLiabilitiesFlag() { + n = append(n, int32(xdr.TrustLineFlagsAuthorizedToMaintainLiabilitiesFlag)) + s = append(s, "authorized_to_maintain_liabilities") + } + + if f.IsClawbackEnabledFlag() { + n = append(n, int32(xdr.TrustLineFlagsTrustlineClawbackEnabledFlag)) + s = append(s, "clawback_enabled") + } + + return n, s +} From b7c0737e0b1af37f0cf5694609837c76eb362207 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Mon, 3 Feb 2025 17:55:32 -0500 Subject: [PATCH 05/12] Update marshalbase64 --- ingest/ledger_transaction.go | 6 +++--- xdr/hash.go | 12 ------------ 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ingest/ledger_transaction.go b/ingest/ledger_transaction.go index d32b403743..bbfa86da22 100644 --- a/ingest/ledger_transaction.go +++ b/ingest/ledger_transaction.go @@ -632,7 +632,7 @@ func (t *LedgerTransaction) LedgerKeyHashFromTxEnvelope() []string { } for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadOnly { - ledgerKeyBase64, err := ledgerKey.MarshalBinaryBase64() + ledgerKeyBase64, err := xdr.MarshalBase64(ledgerKey) if err != nil { panic(err) } @@ -642,7 +642,7 @@ func (t *LedgerTransaction) LedgerKeyHashFromTxEnvelope() []string { } for _, ledgerKey := range v1Envelope.Tx.Ext.SorobanData.Resources.Footprint.ReadWrite { - ledgerKeyBase64, err := ledgerKey.MarshalBinaryBase64() + ledgerKeyBase64, err := xdr.MarshalBase64(ledgerKey) if err != nil { panic(err) } @@ -688,7 +688,7 @@ func (t *LedgerTransaction) contractCodeFromContractData(ledgerKey xdr.LedgerKey return "", false } - codeHash, err := contractCode.Hash.MarshalBinaryBase64() + codeHash, err := xdr.MarshalBase64(contractCode.Hash) if err != nil { panic(err) } diff --git a/xdr/hash.go b/xdr/hash.go index c0107163b6..084520d082 100644 --- a/xdr/hash.go +++ b/xdr/hash.go @@ -1,7 +1,6 @@ package xdr import ( - "encoding/base64" "encoding/hex" ) @@ -20,14 +19,3 @@ func (s Hash) Equals(o Hash) bool { } return true } - -// MarshalBinaryBase64 marshals XDR into a binary form and then encodes it -// using base64. -func (h Hash) MarshalBinaryBase64() (string, error) { - b, err := h.MarshalBinary() - if err != nil { - return "", err - } - - return base64.StdEncoding.EncodeToString(b), nil -} From a3846f2c97a1851f98b66fd8f141baf4ed487896 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Mon, 3 Feb 2025 18:33:05 -0500 Subject: [PATCH 06/12] Fix typos --- ingest/manage_data_details.go | 2 +- ingest/revoke_sponsorship_details.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ingest/manage_data_details.go b/ingest/manage_data_details.go index abf523fd6a..ce52817130 100644 --- a/ingest/manage_data_details.go +++ b/ingest/manage_data_details.go @@ -7,7 +7,7 @@ import ( type ManageDataDetail struct { Name string `json:"name"` - Value string `json:value"` + Value string `json:"value"` } func (o *LedgerOperation) ManageDataDetails() (ManageDataDetail, error) { diff --git a/ingest/revoke_sponsorship_details.go b/ingest/revoke_sponsorship_details.go index 9ca5a1aefb..654d5f128e 100644 --- a/ingest/revoke_sponsorship_details.go +++ b/ingest/revoke_sponsorship_details.go @@ -9,7 +9,7 @@ import ( type RevokeSponsorshipDetail struct { SignerAccountID string `json:"signer_account_id"` SignerKey string `json:"signer_key"` - LedgerKeyDetails LedgerKeyDetail `json:"ledger_key_detail` + LedgerKeyDetails LedgerKeyDetail `json:"ledger_key_detail"` } func (o *LedgerOperation) RevokeSponsorshipDetails() (RevokeSponsorshipDetail, error) { From fc6b854ce66fe08c04a75b59158ac0ce7003a8c6 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Mon, 3 Feb 2025 18:49:55 -0500 Subject: [PATCH 07/12] fix ci/cd --- ingest/ledger_operation.go | 3 ++- ingest/liquidity_pool_deposit_details.go | 4 +++- ingest/liquidity_pool_withdraw_details.go | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ingest/ledger_operation.go b/ingest/ledger_operation.go index 77b831448d..3b6d4e49f1 100644 --- a/ingest/ledger_operation.go +++ b/ingest/ledger_operation.go @@ -69,13 +69,14 @@ func (o *LedgerOperation) OperationTraceCode() (string, error) { var operationTraceCode string var operationResults []xdr.OperationResult var ok bool + var err error operationResults, ok = o.Transaction.Result.Result.OperationResults() if ok { var operationResultTr xdr.OperationResultTr operationResultTr, ok = operationResults[o.OperationIndex].GetTr() if ok { - operationTraceCode, err := operationResultTr.MapOperationResultTr() + operationTraceCode, err = operationResultTr.MapOperationResultTr() if err != nil { return "", err } diff --git a/ingest/liquidity_pool_deposit_details.go b/ingest/liquidity_pool_deposit_details.go index 92f4322a98..39e65288c5 100644 --- a/ingest/liquidity_pool_deposit_details.go +++ b/ingest/liquidity_pool_deposit_details.go @@ -56,10 +56,12 @@ func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDet assetA, assetB xdr.Asset depositedA, depositedB xdr.Int64 sharesReceived xdr.Int64 + lp *xdr.LiquidityPoolEntry + delta *LiquidityPoolDelta ) if o.Transaction.Successful() { // we will use the defaults (omitted asset and 0 amounts) if the transaction failed - lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + lp, delta, err = o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) if err != nil { return LiquidityPoolDepositDetail{}, err } diff --git a/ingest/liquidity_pool_withdraw_details.go b/ingest/liquidity_pool_withdraw_details.go index c250494e3d..9c4de93b3b 100644 --- a/ingest/liquidity_pool_withdraw_details.go +++ b/ingest/liquidity_pool_withdraw_details.go @@ -45,10 +45,12 @@ func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawD var ( assetA, assetB xdr.Asset receivedA, receivedB xdr.Int64 + lp *xdr.LiquidityPoolEntry + delta *LiquidityPoolDelta ) if o.Transaction.Successful() { // we will use the defaults (omitted asset and 0 amounts) if the transaction failed - lp, delta, err := o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) + lp, delta, err = o.getLiquidityPoolAndProductDelta(&op.LiquidityPoolId) if err != nil { return LiquidityPoolWithdrawDetail{}, err } From 875c995d4cedd78b0607beacc6d1e25fb4e0a469 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Mon, 3 Feb 2025 20:30:24 -0500 Subject: [PATCH 08/12] fix tests --- ingest/allow_trust_details.go | 2 +- ingest/bump_sequence_details.go | 4 +- ingest/clawback_details.go | 1 + ingest/ledger_operation_test.go | 538 +++++++++++----------- ingest/liquidity_pool_withdraw_details.go | 2 +- ingest/payment_details.go | 4 + 6 files changed, 269 insertions(+), 282 deletions(-) diff --git a/ingest/allow_trust_details.go b/ingest/allow_trust_details.go index df06278738..f2cbed5cb8 100644 --- a/ingest/allow_trust_details.go +++ b/ingest/allow_trust_details.go @@ -28,7 +28,7 @@ func (o *LedgerOperation) AllowTrustDetails() (AllowTrustDetail, error) { allowTrustDetail := AllowTrustDetail{ Trustor: op.Trustor.Address(), Trustee: o.SourceAccount(), - Authorize: xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag(), + Authorize: xdr.TrustLineFlags(op.Authorize).IsAuthorized(), AuthorizeToMaintainLiabilities: xdr.TrustLineFlags(op.Authorize).IsAuthorizedToMaintainLiabilitiesFlag(), ClawbackEnabled: xdr.TrustLineFlags(op.Authorize).IsClawbackEnabledFlag(), } diff --git a/ingest/bump_sequence_details.go b/ingest/bump_sequence_details.go index c3e3d2e86e..b2e16a895c 100644 --- a/ingest/bump_sequence_details.go +++ b/ingest/bump_sequence_details.go @@ -3,7 +3,7 @@ package ingest import "fmt" type BumpSequenceDetails struct { - BumpTo string `json:"bump_to"` + BumpTo int64 `json:"bump_to"` } func (o *LedgerOperation) BumpSequenceDetails() (BumpSequenceDetails, error) { @@ -13,6 +13,6 @@ func (o *LedgerOperation) BumpSequenceDetails() (BumpSequenceDetails, error) { } return BumpSequenceDetails{ - BumpTo: fmt.Sprintf("%d", op.BumpTo), + BumpTo: int64(op.BumpTo), }, nil } diff --git a/ingest/clawback_details.go b/ingest/clawback_details.go index f9dc0f6fd4..f91dda8487 100644 --- a/ingest/clawback_details.go +++ b/ingest/clawback_details.go @@ -20,6 +20,7 @@ func (o *LedgerOperation) ClawbackDetails() (ClawbackDetail, error) { clawbackDetail := ClawbackDetail{ Amount: int64(op.Amount), + From: op.From.Address(), } var err error diff --git a/ingest/ledger_operation_test.go b/ingest/ledger_operation_test.go index d7127104dc..f70a8534c7 100644 --- a/ingest/ledger_operation_test.go +++ b/ingest/ledger_operation_test.go @@ -488,7 +488,7 @@ func transactionTestInput() *LedgerTransaction { type testOutput struct { err error - result map[string]interface{} + result interface{} } func operationTestInput() []xdr.Operation { @@ -1086,218 +1086,194 @@ func resultTestOutput() []testOutput { output := []testOutput{ { err: nil, - result: map[string]interface{}{ - "account": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "funder": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "funder_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "funder_muxed_id": uint64(123), - "starting_balance": int64(25000000)}, + result: CreateAccountDetail{ + Account: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + Funder: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + FunderMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + FunderMuxedID: uint64(123), + StartingBalance: int64(25000000)}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(350000000), - "asset_code": "USDT", - "asset_id": int64(-8205667356306085451), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "from_muxed_id": uint64(123), - "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + result: PaymentDetail{ + Amount: int64(350000000), + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + From: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + FromMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + FromMuxedID: uint64(123), + To: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(350000000), - "asset_id": int64(-5706705804583548011), - "asset_type": "native", - "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "from_muxed_id": uint64(123), - "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + result: PaymentDetail{ + Amount: int64(350000000), + AssetType: "native", + From: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + FromMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + FromMuxedID: uint64(123), + To: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(8951495900), - "asset_id": int64(-5706705804583548011), - "asset_type": "native", - "from": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", - "path": []Path{ + result: PathPaymentStrictReceiveDetail{ + Amount: int64(8951495900), + AssetType: "native", + From: "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", + Path: []Path{ { AssetCode: "USDT", AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", AssetType: "credit_alphanum4", }, }, - "source_amount": int64(0), - "source_asset_id": int64(-5706705804583548011), - "source_asset_type": "native", - "source_max": int64(8951495900), - "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + SourceAmount: int64(0), + SourceAssetType: "native", + SourceMax: int64(8951495900), + To: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(765860000), - "buying_asset_id": int64(-5706705804583548011), - "buying_asset_type": "native", - "offer_id": int64(0), - "price": 0.514092, - "price_r": Price{ - Numerator: 128523, - Denominator: 250000, - }, - "selling_asset_code": "USDT", - "selling_asset_id": int64(-8205667356306085451), - "selling_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "selling_asset_type": "credit_alphanum4"}, + result: ManageSellOffer{ + Amount: int64(765860000), + BuyingAssetType: "native", + OfferID: int64(0), + Price: 0.514092, + PriceN: 128523, + PriceD: 250000, + SellingAssetCode: "USDT", + SellingAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + SellingAssetType: "credit_alphanum4"}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(631595000), - "buying_asset_code": "USDT", - "buying_asset_id": int64(-8205667356306085451), - "buying_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "buying_asset_type": "credit_alphanum4", - "price": 0.0791606, - "price_r": Price{ - Numerator: 99583200, - Denominator: 1257990000, - }, - "selling_asset_id": int64(-5706705804583548011), - "selling_asset_type": "native"}, + result: CreatePassiveSellOfferDetail{ + Amount: int64(631595000), + BuyingAssetCode: "USDT", + BuyingAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + BuyingAssetType: "credit_alphanum4", + Price: 0.0791606, + PriceN: 99583200, + PriceD: 1257990000, + SellingAssetType: "native"}, }, { err: nil, - result: map[string]interface{}{ - "clear_flags": []int32{1, - 2}, - "clear_flags_s": []string{"auth_required", - "auth_revocable"}, - "high_threshold": uint32(5), - "home_domain": "2019=DRA;n-test", - "inflation_dest": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "low_threshold": uint32(1), - "master_key_weight": uint32(3), - "med_threshold": uint32(3), - "set_flags": []int32{4}, - "set_flags_s": []string{"auth_immutable"}, - "signer_key": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", - "signer_weight": uint32(1)}, + result: SetOptionsDetails{ + ClearFlags: []int32{1, 2}, + ClearFlagsString: []string{"auth_required", "auth_revocable"}, + HighThreshold: uint32(5), + HomeDomain: "2019=DRA;n-test", + InflationDestination: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + LowThreshold: uint32(1), + MasterKeyWeight: uint32(3), + MediumThreshold: uint32(3), + SetFlags: []int32{4}, + SetFlagsString: []string{"auth_immutable"}, + SignerKey: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + SignerWeight: uint32(1)}, }, { err: nil, - result: map[string]interface{}{ - "asset_code": "USSD", - "asset_id": int64(6690054458235693884), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "limit": int64(500000000000000000), - "trustee": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "trustor": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "trustor_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "trustor_muxed_id": uint64(123)}, + result: ChangeTrustDetail{ + AssetCode: "USSD", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + Limit: int64(500000000000000000), + Trustee: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + Trustor: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + TrustorMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + TrustorMuxedID: uint64(123)}, }, { err: nil, - result: map[string]interface{}{ - "asset_type": "liquidity_pool_shares", - "limit": int64(500000000000000000), - "liquidity_pool_id": "1c261d6c75930204a73b480c3020ab525e9be48ce93de6194cf69fb06f07452d", - "trustor": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "trustor_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "trustor_muxed_id": uint64(123)}, + result: ChangeTrustDetail{ + AssetType: "liquidity_pool_shares", + Limit: int64(500000000000000000), + LiquidityPoolID: "HCYdbHWTAgSnO0gMMCCrUl6b5IzpPeYZTPafsG8HRS0=", + Trustor: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + TrustorMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + TrustorMuxedID: uint64(123)}, }, { err: nil, - result: map[string]interface{}{ - "asset_code": "USDT", - "asset_id": int64(8181787832768848499), - "asset_issuer": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "asset_type": "credit_alphanum4", - "authorize": true, - "trustee": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "trustee_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "trustee_muxed_id": uint64(123), - "trustor": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + result: AllowTrustDetail{ + AssetCode: "USDT", + AssetIssuer: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + AssetType: "credit_alphanum4", + Authorize: true, + Trustee: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + TrusteeMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + TrusteeMuxedID: uint64(123), + Trustor: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, - result: map[string]interface{}{ - "account": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "account_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "account_muxed_id": uint64(123), - "into": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + result: AccountMergeDetail{ + Account: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + AccountMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + AccountMuxedID: uint64(123), + Into: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, - result: map[string]interface{}{}, + result: InflationDetail{}, }, { err: nil, - result: map[string]interface{}{ - "name": "test", - "value": "dmFsdWU=", + result: ManageDataDetail{ + Name: "test", + Value: "dmFsdWU=", }, }, { err: nil, - result: map[string]interface{}{ - "bump_to": "100", + result: BumpSequenceDetails{ + BumpTo: int64(100), }, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(7654501001), - "buying_asset_id": int64(-5706705804583548011), - "buying_asset_type": "native", - "offer_id": int64(100), - "price": 0.3496823, - "price_r": Price{ - Numerator: 635863285, - Denominator: 1818402817, - }, - "selling_asset_code": "USDT", - "selling_asset_id": int64(-8205667356306085451), - "selling_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "selling_asset_type": "credit_alphanum4"}, + result: ManageBuyOffer{ + Amount: int64(7654501001), + BuyingAssetType: "native", + OfferID: int64(100), + Price: 0.3496823, + PriceN: 635863285, + PriceD: 1818402817, + SellingAssetCode: "USDT", + SellingAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + SellingAssetType: "credit_alphanum4"}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(640000000), - "asset_id": int64(-5706705804583548011), - "asset_type": "native", - "destination_min": "428.0460538", - "from": "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", - "from_muxed": "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", - "from_muxed_id": uint64(123), - "path": []Path{ + result: PathPaymentStrictSendDetail{ + Amount: int64(640000000), + AssetType: "native", + DestinationMin: 4280460538, + From: "GAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAK", + FromMuxed: "MAISEMYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMJ2I", + FromMuxedID: uint64(123), + Path: []Path{ { AssetCode: "USDT", AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", AssetType: "credit_alphanum4", }, }, - "source_amount": int64(1598182), - "source_asset_id": int64(-5706705804583548011), - "source_asset_type": "native", - "to": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, + SourceAmount: int64(1598182), + SourceAssetType: "native", + To: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA"}, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(1234567890000), - "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_code": "USDT", - "asset_id": int64(-8205667356306085451), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "claimants": []Claimant{ + result: CreateClaimableBalanceDetail{ + Amount: int64(1234567890000), + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + Claimants: []Claimant{ { Destination: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", Predicate: xdr.ClaimPredicate{ @@ -1314,143 +1290,153 @@ func resultTestOutput() []testOutput { }, { err: nil, - result: map[string]interface{}{ - "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", - "claimant": "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", + result: ClaimClaimableBalanceDetail{ + BalanceID: "AAAAAAECAwQFBgcICQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + Claimant: "GBT4YAEGJQ5YSFUMNKX6BPBUOCPNAIOFAVZOF6MIME2CECBMEIUXFZZN", }, }, { err: nil, - result: map[string]interface{}{ - "sponsored_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + result: BeginSponsoringFutureReservesDetail{ + SponsoredID: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", }, }, { err: nil, - result: map[string]interface{}{ - "signer_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "signer_key": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + result: RevokeSponsorshipDetail{ + SignerAccountID: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + SignerKey: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", }, }, { err: nil, - result: map[string]interface{}{ - "account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + result: RevokeSponsorshipDetail{ + LedgerKeyDetails: LedgerKeyDetail{ + AccountID: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + }, }, }, { err: nil, - result: map[string]interface{}{ - "claimable_balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", + result: RevokeSponsorshipDetail{ + LedgerKeyDetails: LedgerKeyDetail{ + ClaimableBalanceID: "000000000102030405060708090000000000000000000000000000000000000000000000", + }, }, }, { err: nil, - result: map[string]interface{}{ - "data_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "data_name": "test", + result: RevokeSponsorshipDetail{ + LedgerKeyDetails: LedgerKeyDetail{ + DataAccountID: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + DataName: "test", + }, }, }, { err: nil, - result: map[string]interface{}{ - "offer_id": int64(100), + result: RevokeSponsorshipDetail{ + LedgerKeyDetails: LedgerKeyDetail{ + OfferID: int64(100), + }, }, }, { err: nil, - result: map[string]interface{}{ - "trustline_account_id": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "trustline_asset": "USTT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + result: RevokeSponsorshipDetail{ + LedgerKeyDetails: LedgerKeyDetail{ + TrustlineAccountID: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + TrustlineAssetCode: "USTT", + TrustlineAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + TrustlineAssetType: "credit_alphanum4", + }, }, }, { err: nil, - result: map[string]interface{}{ - "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000", + result: RevokeSponsorshipDetail{ + LedgerKeyDetails: LedgerKeyDetail{ + LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + }, }, }, { err: nil, - result: map[string]interface{}{ - "amount": int64(1598182), - "asset_code": "USDT", - "asset_id": int64(-8205667356306085451), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "from": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + result: ClawbackDetail{ + Amount: int64(1598182), + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + From: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", }, }, { err: nil, - result: map[string]interface{}{ - "balance_id": "000000000102030405060708090000000000000000000000000000000000000000000000", + result: ClawbackClaimableBalanceDetail{ + BalanceID: "AAAAAAECAwQFBgcICQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", }, }, { err: nil, - result: map[string]interface{}{ - "asset_code": "USDT", - "asset_id": int64(-8205667356306085451), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "clear_flags": []int32{1, 2}, - "clear_flags_s": []string{"authorized", "authorized_to_maintain_liabilities"}, - "set_flags": []int32{4}, - "set_flags_s": []string{"clawback_enabled"}, - "trustor": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + result: SetTrustlineFlagsDetail{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + ClearFlags: []int32{1, 2}, + ClearFlagsString: []string{"authorized", "authorized_to_maintain_liabilities"}, + SetFlags: []int32{4}, + SetFlagsString: []string{"clawback_enabled"}, + Trustor: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", }, }, { err: nil, - result: map[string]interface{}{ - "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000", - "max_price": 1e+06, - "max_price_r": Price{Numerator: 1000000, Denominator: 1}, - "min_price": 1e-06, - "min_price_r": Price{Numerator: 1, Denominator: 1000000}, - "reserve_a_asset_code": "USDT", - "reserve_a_asset_id": int64(-8205667356306085451), - "reserve_a_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "reserve_a_asset_type": "credit_alphanum4", - "reserve_a_deposit_amount": 1e-07, - "reserve_a_max_amount": int64(1000), - "reserve_b_asset_code": "USDT", - "reserve_b_asset_id": int64(-8205667356306085451), - "reserve_b_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "reserve_b_asset_type": "credit_alphanum4", - "reserve_b_deposit_amount": 1e-07, - "reserve_b_max_amount": int64(100), - "shares_received": 1e-07, + result: LiquidityPoolDepositDetail{ + LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + MaxPrice: 1e+06, + MaxPriceN: 1000000, + MaxPriceD: 1, + MinPrice: 1e-06, + MinPriceN: 1, + MinPriceD: 1000000, + ReserveAAssetCode: "USDT", + ReserveAAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + ReserveAAssetType: "credit_alphanum4", + ReserveADepositAmount: int64(1), + ReserveAMaxAmount: int64(1000), + ReserveBAssetCode: "USDT", + ReserveBAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + ReserveBAssetType: "credit_alphanum4", + ReserveBDepositAmount: 1, + ReserveBMaxAmount: int64(100), + SharesReceived: 1, }, }, { err: nil, - result: map[string]interface{}{ - "liquidity_pool_id": "0102030405060708090000000000000000000000000000000000000000000000", - "reserve_a_asset_code": "USDT", - "reserve_a_asset_id": int64(-8205667356306085451), - "reserve_a_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "reserve_a_asset_type": "credit_alphanum4", - "reserve_a_min_amount": int64(1), - "reserve_a_withdraw_amount": int64(-1), - "reserve_b_asset_code": "USDT", - "reserve_b_asset_id": int64(-8205667356306085451), - "reserve_b_asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "reserve_b_asset_type": "credit_alphanum4", - "reserve_b_min_amount": int64(1), - "reserve_b_withdraw_amount": int64(-1), - "shares": int64(4), + result: LiquidityPoolWithdrawDetail{ + LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ReserveAAssetCode: "USDT", + ReserveAAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + ReserveAAssetType: "credit_alphanum4", + ReserveAMinAmount: int64(1), + ReserveAWithdrawAmount: int64(-1), + ReserveBAssetCode: "USDT", + ReserveBAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + ReserveBAssetType: "credit_alphanum4", + ReserveBMinAmount: int64(1), + ReserveBWithdrawAmount: int64(-1), + Shares: int64(4), }, }, { err: nil, - result: map[string]interface{}{ - "asset_balance_changes": []map[string]interface{}{}, - "contract_id": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", - "function": "HostFunctionTypeHostFunctionTypeInvokeContract", - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "parameters": []map[string]string{ + result: InvokeHostFunctionDetail{ + AssetBalanceChanges: []BalanceChangeDetail{}, + ContractID: "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", + Function: "HostFunctionTypeHostFunctionTypeInvokeContract", + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + Parameters: []map[string]string{ { "type": "Address", "value": "AAAAEgAAAAESNFZ4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", @@ -1460,7 +1446,7 @@ func resultTestOutput() []testOutput { "value": "AAAADwAAAAR0ZXN0", }, }, - "parameters_decoded": []map[string]string{ + ParametersDecoded: []map[string]string{ { "type": "Address", "value": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", @@ -1470,85 +1456,81 @@ func resultTestOutput() []testOutput { "value": "test", }, }, - "type": "invoke_contract", + Type: "invoke_contract", }, }, { err: nil, - result: map[string]interface{}{ - "address": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", - "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", - "from": "address", - "function": "HostFunctionTypeHostFunctionTypeCreateContract", - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "type": "create_contract", + result: InvokeHostFunctionDetail{ + Address: "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", + ContractID: "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + From: "address", + Function: "HostFunctionTypeHostFunctionTypeCreateContract", + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + Type: "create_contract", }, }, { err: nil, - result: map[string]interface{}{ - "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_code": "USDT", - "asset_id": int64(-8205667356306085451), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", - "from": "asset", - "function": "HostFunctionTypeHostFunctionTypeCreateContract", - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "type": "create_contract", + result: InvokeHostFunctionDetail{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + ContractID: "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + From: "asset", + Function: "HostFunctionTypeHostFunctionTypeCreateContract", + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + Type: "create_contract", }, }, { err: nil, - result: map[string]interface{}{ - "asset": "USDT:GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_code": "USDT", - "asset_id": int64(-8205667356306085451), - "asset_issuer": "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - "asset_type": "credit_alphanum4", - "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", - "from": "asset", - "function": "HostFunctionTypeHostFunctionTypeCreateContractV2", - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "parameters": []map[string]string{ + result: InvokeHostFunctionDetail{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + ContractID: "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + From: "asset", + Function: "HostFunctionTypeHostFunctionTypeCreateContractV2", + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + Parameters: []map[string]string{ { "type": "B", "value": "AAAAAAAAAAE=", }, }, - "parameters_decoded": []map[string]string{ + ParametersDecoded: []map[string]string{ { "type": "B", "value": "true", }, }, - "type": "create_contract_v2", + Type: "create_contract_v2", }, }, { err: nil, - result: map[string]interface{}{ - "function": "HostFunctionTypeHostFunctionTypeUploadContractWasm", - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "type": "upload_wasm", + result: InvokeHostFunctionDetail{ + Function: "HostFunctionTypeHostFunctionTypeUploadContractWasm", + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + Type: "upload_wasm", }, }, { err: nil, - result: map[string]interface{}{ - "extend_to": int32(1234), - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", - "type": "extend_footprint_ttl", + result: ExtendFootprintTtlDetail{ + ExtendTo: uint32(1234), + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + ContractID: "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + Type: "extend_footprint_ttl", }, }, { err: nil, - result: map[string]interface{}{ - "ledger_key_hash": []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - "contract_id": "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", - "type": "restore_footprint", + result: RestoreFootprintDetail{ + LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, + ContractID: "CAJDIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABT4W", + Type: "restore_footprint", }, }, } diff --git a/ingest/liquidity_pool_withdraw_details.go b/ingest/liquidity_pool_withdraw_details.go index 9c4de93b3b..a8e5249d57 100644 --- a/ingest/liquidity_pool_withdraw_details.go +++ b/ingest/liquidity_pool_withdraw_details.go @@ -81,7 +81,7 @@ func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawD liquidityPoolWithdrawDetail.ReserveBAssetCode = assetBCode liquidityPoolWithdrawDetail.ReserveBAssetIssuer = assetBIssuer liquidityPoolWithdrawDetail.ReserveBAssetType = assetBType - liquidityPoolWithdrawDetail.ReserveAWithdrawAmount = int64(receivedB) + liquidityPoolWithdrawDetail.ReserveBWithdrawAmount = int64(receivedB) return liquidityPoolWithdrawDetail, nil } diff --git a/ingest/payment_details.go b/ingest/payment_details.go index c7845ed503..0266ec9f58 100644 --- a/ingest/payment_details.go +++ b/ingest/payment_details.go @@ -56,5 +56,9 @@ func (o *LedgerOperation) PaymentDetails() (PaymentDetail, error) { return PaymentDetail{}, err } + paymentDetail.AssetCode = assetCode + paymentDetail.AssetIssuer = assetIssuer + paymentDetail.AssetType = assetType + return paymentDetail, nil } From 5c68c65ea0178d17f12438c2660a746ca8cdc220 Mon Sep 17 00:00:00 2001 From: chowbao <simon.chow765@gmail.com> Date: Tue, 4 Feb 2025 11:18:42 -0500 Subject: [PATCH 09/12] Update hash.go --- xdr/hash.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xdr/hash.go b/xdr/hash.go index 084520d082..2a15c18c9c 100644 --- a/xdr/hash.go +++ b/xdr/hash.go @@ -1,8 +1,6 @@ package xdr -import ( - "encoding/hex" -) +import "encoding/hex" func (h Hash) HexString() string { return hex.EncodeToString(h[:]) From cc6d49df2de93fbffceb5557a714a3166d38de82 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Thu, 6 Feb 2025 10:58:38 -0500 Subject: [PATCH 10/12] add string for js --- ingest/account_merge_details.go | 2 +- ingest/allow_trust_details.go | 2 +- ingest/bump_sequence_details.go | 2 +- ingest/change_trust_details.go | 4 ++-- ingest/claim_claimable_balance_details.go | 2 +- ingest/clawback_details.go | 4 ++-- ingest/create_account_details.go | 4 ++-- ingest/create_claimable_balance_details.go | 2 +- ingest/create_passive_sell_offer_details.go | 2 +- ingest/end_sponsoring_future_reserve_details.go | 2 +- ingest/ledger_operation.go | 2 +- ingest/liquidity_pool_deposit_details.go | 10 +++++----- ingest/liquidity_pool_withdraw_details.go | 10 +++++----- ingest/manage_buy_offer_details.go | 4 ++-- ingest/manage_sell_offer_details.go | 4 ++-- ingest/path_payment_strict_receive_details.go | 10 +++++----- ingest/path_payment_strict_send_details.go | 10 +++++----- ingest/payment_details.go | 6 +++--- 18 files changed, 41 insertions(+), 41 deletions(-) diff --git a/ingest/account_merge_details.go b/ingest/account_merge_details.go index fc70291188..b439a49ac5 100644 --- a/ingest/account_merge_details.go +++ b/ingest/account_merge_details.go @@ -5,7 +5,7 @@ import "fmt" type AccountMergeDetail struct { Account string `json:"account"` AccountMuxed string `json:"account_muxed"` - AccountMuxedID uint64 `json:"account_muxed_id"` + AccountMuxedID uint64 `json:"account_muxed_id,string"` Into string `json:"into"` IntoMuxed string `json:"into_muxed"` IntoMuxedID uint64 `json:"into_muxed_id"` diff --git a/ingest/allow_trust_details.go b/ingest/allow_trust_details.go index f2cbed5cb8..3c3785c88b 100644 --- a/ingest/allow_trust_details.go +++ b/ingest/allow_trust_details.go @@ -13,7 +13,7 @@ type AllowTrustDetail struct { Trustor string `json:"trustor"` Trustee string `json:"trustee"` TrusteeMuxed string `json:"trustee_muxed"` - TrusteeMuxedID uint64 `json:"trustee_muxed_id"` + TrusteeMuxedID uint64 `json:"trustee_muxed_id,string"` Authorize bool `json:"authorize"` AuthorizeToMaintainLiabilities bool `json:"authorize_to_maintain_liabilities"` ClawbackEnabled bool `json:"clawback_enabled"` diff --git a/ingest/bump_sequence_details.go b/ingest/bump_sequence_details.go index b2e16a895c..abeb2a4e52 100644 --- a/ingest/bump_sequence_details.go +++ b/ingest/bump_sequence_details.go @@ -3,7 +3,7 @@ package ingest import "fmt" type BumpSequenceDetails struct { - BumpTo int64 `json:"bump_to"` + BumpTo int64 `json:"bump_to,string"` } func (o *LedgerOperation) BumpSequenceDetails() (BumpSequenceDetails, error) { diff --git a/ingest/change_trust_details.go b/ingest/change_trust_details.go index a3302146f7..ca3040bd2d 100644 --- a/ingest/change_trust_details.go +++ b/ingest/change_trust_details.go @@ -11,11 +11,11 @@ type ChangeTrustDetail struct { AssetIssuer string `json:"asset_issuer"` AssetType string `json:"asset_type"` LiquidityPoolID string `json:"liquidity_pool_id"` - Limit int64 `json:"limit"` + Limit int64 `json:"limit,string"` Trustee string `json:"trustee"` Trustor string `json:"trustor"` TrustorMuxed string `json:"trustor_muxed"` - TrustorMuxedID uint64 `json:"trustor_muxed_id"` + TrustorMuxedID uint64 `json:"trustor_muxed_id,string"` } func (o *LedgerOperation) ChangeTrustDetails() (ChangeTrustDetail, error) { diff --git a/ingest/claim_claimable_balance_details.go b/ingest/claim_claimable_balance_details.go index 2125d31e6b..69d85950ad 100644 --- a/ingest/claim_claimable_balance_details.go +++ b/ingest/claim_claimable_balance_details.go @@ -10,7 +10,7 @@ type ClaimClaimableBalanceDetail struct { BalanceID string `json:"balance_id"` Claimant string `json:"claimant"` ClaimantMuxed string `json:"claimant_muxed"` - ClaimantMuxedID uint64 `json:"claimant_muxed_id"` + ClaimantMuxedID uint64 `json:"claimant_muxed_id,string"` } func (o *LedgerOperation) ClaimClaimableBalanceDetails() (ClaimClaimableBalanceDetail, error) { diff --git a/ingest/clawback_details.go b/ingest/clawback_details.go index f91dda8487..ff84f41705 100644 --- a/ingest/clawback_details.go +++ b/ingest/clawback_details.go @@ -8,8 +8,8 @@ type ClawbackDetail struct { AssetType string `json:"asset_type"` From string `json:"from"` FromMuxed string `json:"from_muxed"` - FromMuxedID uint64 `json:"from_muxed_id"` - Amount int64 `json:"amount"` + FromMuxedID uint64 `json:"from_muxed_id,string"` + Amount int64 `json:"amount,string"` } func (o *LedgerOperation) ClawbackDetails() (ClawbackDetail, error) { diff --git a/ingest/create_account_details.go b/ingest/create_account_details.go index 9a04de1be6..9eed63cc2d 100644 --- a/ingest/create_account_details.go +++ b/ingest/create_account_details.go @@ -6,10 +6,10 @@ import ( type CreateAccountDetail struct { Account string `json:"account"` - StartingBalance int64 `json:"starting_balance"` + StartingBalance int64 `json:"starting_balance,string"` Funder string `json:"funder"` FunderMuxed string `json:"funder_muxed"` - FunderMuxedID uint64 `json:"funder_muxed_id"` + FunderMuxedID uint64 `json:"funder_muxed_id,string"` } func (o *LedgerOperation) CreateAccountDetails() (CreateAccountDetail, error) { diff --git a/ingest/create_claimable_balance_details.go b/ingest/create_claimable_balance_details.go index 795ed5edf0..0aefcf1ffb 100644 --- a/ingest/create_claimable_balance_details.go +++ b/ingest/create_claimable_balance_details.go @@ -8,7 +8,7 @@ type CreateClaimableBalanceDetail struct { AssetCode string `json:"asset_code"` AssetIssuer string `json:"asset_issuer"` AssetType string `json:"asset_type"` - Amount int64 `json:"amount"` + Amount int64 `json:"amount,string"` Claimants []Claimant `json:"claimants"` } diff --git a/ingest/create_passive_sell_offer_details.go b/ingest/create_passive_sell_offer_details.go index 1532cb7d07..2ee8791b8c 100644 --- a/ingest/create_passive_sell_offer_details.go +++ b/ingest/create_passive_sell_offer_details.go @@ -6,7 +6,7 @@ import ( ) type CreatePassiveSellOfferDetail struct { - Amount int64 `json:"amount"` + Amount int64 `json:"amount,string"` PriceN int32 `json:"price_n"` PriceD int32 `json:"price_d"` Price float64 `json:"price"` diff --git a/ingest/end_sponsoring_future_reserve_details.go b/ingest/end_sponsoring_future_reserve_details.go index 5a9b58df9e..c2d6f609e3 100644 --- a/ingest/end_sponsoring_future_reserve_details.go +++ b/ingest/end_sponsoring_future_reserve_details.go @@ -3,7 +3,7 @@ package ingest type EndSponsoringFutureReserveDetail struct { BeginSponsor string `json:"begin_sponsor"` BeginSponsorMuxed string `json:"begin_sponsor_muxed"` - BeginSponsorMuxedID uint64 `json:"begin_sponsor_muxed_id"` + BeginSponsorMuxedID uint64 `json:"begin_sponsor_muxed_id,string"` } func (o *LedgerOperation) EndSponsoringFutureReserveDetails() (EndSponsoringFutureReserveDetail, error) { diff --git a/ingest/ledger_operation.go b/ingest/ledger_operation.go index 3b6d4e49f1..62f39c8271 100644 --- a/ingest/ledger_operation.go +++ b/ingest/ledger_operation.go @@ -257,7 +257,7 @@ type LedgerKeyDetail struct { ClaimableBalanceID string `json:"claimable_balance_id"` DataAccountID string `json:"data_account_id"` DataName string `json:"data_name"` - OfferID int64 `json:"offer_id"` + OfferID int64 `json:"offer_id,string"` TrustlineAccountID string `json:"trustline_account_id"` TrustlineLiquidityPoolID string `json:"trustline_liquidity_pool_id"` TrustlineAssetCode string `json:"trustline_asset_code"` diff --git a/ingest/liquidity_pool_deposit_details.go b/ingest/liquidity_pool_deposit_details.go index 39e65288c5..8d7dce32d6 100644 --- a/ingest/liquidity_pool_deposit_details.go +++ b/ingest/liquidity_pool_deposit_details.go @@ -12,20 +12,20 @@ type LiquidityPoolDepositDetail struct { ReserveAAssetCode string `json:"reserve_a_asset_code"` ReserveAAssetIssuer string `json:"reserve_a_asset_issuer"` ReserveAAssetType string `json:"reserve_a_asset_type"` - ReserveAMaxAmount int64 `json:"reserve_a_max_amount"` - ReserveADepositAmount int64 `json:"reserve_a_deposit_amount"` + ReserveAMaxAmount int64 `json:"reserve_a_max_amount,string"` + ReserveADepositAmount int64 `json:"reserve_a_deposit_amount,string"` ReserveBAssetCode string `json:"reserve_b_asset_code"` ReserveBAssetIssuer string `json:"reserve_b_asset_issuer"` ReserveBAssetType string `json:"reserve_b_asset_type"` - ReserveBMaxAmount int64 `json:"reserve_b_max_amount"` - ReserveBDepositAmount int64 `json:"reserve_b_deposit_amount"` + ReserveBMaxAmount int64 `json:"reserve_b_max_amount,string"` + ReserveBDepositAmount int64 `json:"reserve_b_deposit_amount,string"` MinPriceN int32 `json:"min_price_n"` MinPriceD int32 `json:"min_price_d"` MinPrice float64 `json:"min_price"` MaxPriceN int32 `json:"max_price_n"` MaxPriceD int32 `json:"max_price_d"` MaxPrice float64 `json:"max_price"` - SharesReceived int64 `json:"shares_received"` + SharesReceived int64 `json:"shares_received,string"` } func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDetail, error) { diff --git a/ingest/liquidity_pool_withdraw_details.go b/ingest/liquidity_pool_withdraw_details.go index a8e5249d57..94d4a93ac7 100644 --- a/ingest/liquidity_pool_withdraw_details.go +++ b/ingest/liquidity_pool_withdraw_details.go @@ -11,14 +11,14 @@ type LiquidityPoolWithdrawDetail struct { ReserveAAssetCode string `json:"reserve_a_asset_code"` ReserveAAssetIssuer string `json:"reserve_a_asset_issuer"` ReserveAAssetType string `json:"reserve_a_asset_type"` - ReserveAMinAmount int64 `json:"reserve_a_min_amount"` - ReserveAWithdrawAmount int64 `json:"reserve_a_withdraw_amount"` + ReserveAMinAmount int64 `json:"reserve_a_min_amount,string"` + ReserveAWithdrawAmount int64 `json:"reserve_a_withdraw_amount,string"` ReserveBAssetCode string `json:"reserve_b_asset_code"` ReserveBAssetIssuer string `json:"reserve_b_asset_issuer"` ReserveBAssetType string `json:"reserve_b_asset_type"` - ReserveBMinAmount int64 `json:"reserve_b_min_amount"` - ReserveBWithdrawAmount int64 `json:"reserve_b_withdraw_amount"` - Shares int64 `json:"shares"` + ReserveBMinAmount int64 `json:"reserve_b_min_amount,string"` + ReserveBWithdrawAmount int64 `json:"reserve_b_withdraw_amount,string"` + Shares int64 `json:"shares,string"` } func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawDetail, error) { diff --git a/ingest/manage_buy_offer_details.go b/ingest/manage_buy_offer_details.go index 21c9ac35ab..81e39982a8 100644 --- a/ingest/manage_buy_offer_details.go +++ b/ingest/manage_buy_offer_details.go @@ -6,8 +6,8 @@ import ( ) type ManageBuyOffer struct { - OfferID int64 `json:"offer_id"` - Amount int64 `json:"amount"` + OfferID int64 `json:"offer_id,string"` + Amount int64 `json:"amount,string"` PriceN int32 `json:"price_n"` PriceD int32 `json:"price_d"` Price float64 `json:"price"` diff --git a/ingest/manage_sell_offer_details.go b/ingest/manage_sell_offer_details.go index 9b33fd4b03..5fc7811ce1 100644 --- a/ingest/manage_sell_offer_details.go +++ b/ingest/manage_sell_offer_details.go @@ -6,8 +6,8 @@ import ( ) type ManageSellOffer struct { - OfferID int64 `json:"offer_id"` - Amount int64 `json:"amount"` + OfferID int64 `json:"offer_id,string"` + Amount int64 `json:"amount,string"` PriceN int32 `json:"price_n"` PriceD int32 `json:"price_d"` Price float64 `json:"price"` diff --git a/ingest/path_payment_strict_receive_details.go b/ingest/path_payment_strict_receive_details.go index 3a9eab83d4..31dd64d514 100644 --- a/ingest/path_payment_strict_receive_details.go +++ b/ingest/path_payment_strict_receive_details.go @@ -7,19 +7,19 @@ import ( type PathPaymentStrictReceiveDetail struct { From string `json:"from"` FromMuxed string `json:"from_muxed"` - FromMuxedID uint64 `json:"from_muxed_id"` + FromMuxedID uint64 `json:"from_muxed_id,string"` To string `json:"to"` ToMuxed string `json:"to_muxed"` - ToMuxedID uint64 `json:"to_muxed_id"` + ToMuxedID uint64 `json:"to_muxed_id,string"` AssetCode string `json:"asset_code"` AssetIssuer string `json:"asset_issuer"` AssetType string `json:"asset_type"` - Amount int64 `json:"amount"` + Amount int64 `json:"amount,string"` SourceAssetCode string `json:"source_asset_code"` SourceAssetIssuer string `json:"source_asset_issuer"` SourceAssetType string `json:"source_asset_type"` - SourceAmount int64 `json:"source_amount"` - SourceMax int64 `json:"source_max"` + SourceAmount int64 `json:"source_amount,string"` + SourceMax int64 `json:"source_max,string"` Path []Path `json:"path"` } diff --git a/ingest/path_payment_strict_send_details.go b/ingest/path_payment_strict_send_details.go index 6b0c28e69f..1a28b8d173 100644 --- a/ingest/path_payment_strict_send_details.go +++ b/ingest/path_payment_strict_send_details.go @@ -7,19 +7,19 @@ import ( type PathPaymentStrictSendDetail struct { From string `json:"from"` FromMuxed string `json:"from_muxed"` - FromMuxedID uint64 `json:"from_muxed_id"` + FromMuxedID uint64 `json:"from_muxed_id,string"` To string `json:"to"` ToMuxed string `json:"to_muxed"` - ToMuxedID uint64 `json:"to_muxed_id"` + ToMuxedID uint64 `json:"to_muxed_id,string"` AssetCode string `json:"asset_code"` AssetIssuer string `json:"asset_issuer"` AssetType string `json:"asset_type"` - Amount int64 `json:"amount"` + Amount int64 `json:"amount,string"` SourceAssetCode string `json:"source_asset_code"` SourceAssetIssuer string `json:"source_asset_issuer"` SourceAssetType string `json:"source_asset_type"` - SourceAmount int64 `json:"source_amount"` - DestinationMin int64 `json:"destination_min"` + SourceAmount int64 `json:"source_amount,string"` + DestinationMin int64 `json:"destination_min,string"` Path []Path `json:"path"` } diff --git a/ingest/payment_details.go b/ingest/payment_details.go index 0266ec9f58..c209db38ee 100644 --- a/ingest/payment_details.go +++ b/ingest/payment_details.go @@ -7,14 +7,14 @@ import ( type PaymentDetail struct { From string `json:"from"` FromMuxed string `json:"from_muxed"` - FromMuxedID uint64 `json:"from_muxed_id"` + FromMuxedID uint64 `json:"from_muxed_id,string"` To string `json:"to"` ToMuxed string `json:"to_muxed"` - ToMuxedID uint64 `json:"to_muxed_id"` + ToMuxedID uint64 `json:"to_muxed_id,string"` AssetCode string `json:"asset_code"` AssetIssuer string `json:"asset_issuer"` AssetType string `json:"asset_type"` - Amount int64 `json:"amount"` + Amount int64 `json:"amount,string"` } func (o *LedgerOperation) PaymentDetails() (PaymentDetail, error) { From 0258c833933253b1b6e6f5a0c9f6bf9ac8a0567f Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Thu, 6 Feb 2025 11:07:08 -0500 Subject: [PATCH 11/12] make createcontract better --- ingest/invoke_host_function_details.go | 67 +++++++++++--------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/ingest/invoke_host_function_details.go b/ingest/invoke_host_function_details.go index 8a37365602..58a2720c46 100644 --- a/ingest/invoke_host_function_details.go +++ b/ingest/invoke_host_function_details.go @@ -70,30 +70,13 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (InvokeHostFunctionDetail, args := op.HostFunction.MustCreateContract() invokeHostFunctionDetail.Type = "create_contract" - invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() - - var contractID string - contractID, ok = o.Transaction.contractIdFromTxEnvelope() - if ok { - invokeHostFunctionDetail.ContractID = contractID - } - - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - invokeHostFunctionDetail.ContractCodeHash = contractCodeHash - } preImageDetails, err := switchContractIdPreimageType(args.ContractIdPreimage) if err != nil { return InvokeHostFunctionDetail{}, nil } - invokeHostFunctionDetail.From = preImageDetails.From - invokeHostFunctionDetail.Address = preImageDetails.Address - invokeHostFunctionDetail.AssetCode = preImageDetails.AssetCode - invokeHostFunctionDetail.AssetIssuer = preImageDetails.AssetIssuer - invokeHostFunctionDetail.AssetType = preImageDetails.AssetType + o.getCreateContractDetails(&invokeHostFunctionDetail, preImageDetails) case xdr.HostFunctionTypeHostFunctionTypeUploadContractWasm: invokeHostFunctionDetail.Type = "upload_wasm" invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() @@ -107,38 +90,44 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (InvokeHostFunctionDetail, args := op.HostFunction.MustCreateContractV2() invokeHostFunctionDetail.Type = "create_contract_v2" - invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() - var contractID string - contractID, ok = o.Transaction.contractIdFromTxEnvelope() - if ok { - invokeHostFunctionDetail.ContractID = contractID + preImageDetails, err := switchContractIdPreimageType(args.ContractIdPreimage) + if err != nil { + return InvokeHostFunctionDetail{}, err } - var contractCodeHash string - contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() - if ok { - invokeHostFunctionDetail.ContractCodeHash = contractCodeHash - } + o.getCreateContractDetails(&invokeHostFunctionDetail, preImageDetails) // ConstructorArgs is a list of ScVals // This will initially be handled the same as InvokeContractParams until a different // model is found necessary. invokeHostFunctionDetail.Parameters, invokeHostFunctionDetail.ParametersDecoded = o.serializeParameters(args.ConstructorArgs) - - preImageDetails, err := switchContractIdPreimageType(args.ContractIdPreimage) - if err != nil { - return InvokeHostFunctionDetail{}, nil - } - - invokeHostFunctionDetail.From = preImageDetails.From - invokeHostFunctionDetail.Address = preImageDetails.Address - invokeHostFunctionDetail.AssetCode = preImageDetails.AssetCode - invokeHostFunctionDetail.AssetIssuer = preImageDetails.AssetIssuer - invokeHostFunctionDetail.AssetType = preImageDetails.AssetType default: return InvokeHostFunctionDetail{}, fmt.Errorf("unknown host function type: %s", op.HostFunction.Type) } return invokeHostFunctionDetail, nil } + +func (o *LedgerOperation) getCreateContractDetails(invokeHostFunctionDetail *InvokeHostFunctionDetail, preImageDetails PreImageDetails) { + var ok bool + invokeHostFunctionDetail.LedgerKeyHash = o.Transaction.LedgerKeyHashFromTxEnvelope() + + var contractID string + contractID, ok = o.Transaction.contractIdFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractID = contractID + } + + var contractCodeHash string + contractCodeHash, ok = o.Transaction.ContractCodeHashFromTxEnvelope() + if ok { + invokeHostFunctionDetail.ContractCodeHash = contractCodeHash + } + + invokeHostFunctionDetail.From = preImageDetails.From + invokeHostFunctionDetail.Address = preImageDetails.Address + invokeHostFunctionDetail.AssetCode = preImageDetails.AssetCode + invokeHostFunctionDetail.AssetIssuer = preImageDetails.AssetIssuer + invokeHostFunctionDetail.AssetType = preImageDetails.AssetType +} From 52353e1b974c222560f56fc53cc60760d5187b57 Mon Sep 17 00:00:00 2001 From: Simon Chow <simon.chow@stellar.org> Date: Thu, 13 Feb 2025 19:53:23 -0500 Subject: [PATCH 12/12] address comments --- go.mod | 1 + go.sum | 2 + ingest/account_merge_details.go | 2 +- ingest/invoke_host_function_details.go | 14 ++- ingest/ledger_operation.go | 40 ++++---- ingest/ledger_operation_test.go | 112 ++++++++++------------ ingest/liquidity_pool_deposit_details.go | 70 +++++++------- ingest/liquidity_pool_withdraw_details.go | 42 ++++---- 8 files changed, 142 insertions(+), 141 deletions(-) diff --git a/go.mod b/go.mod index 6d7e5fab5d..1a5c1e73ed 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,7 @@ require ( github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 github.com/fsouza/fake-gcs-server v1.49.2 + github.com/stellar/go-stellar-xdr-json v0.0.0-20250203230501-c6fa0788fe99 ) require ( diff --git a/go.sum b/go.sum index b071601459..8ecbbccca2 100644 --- a/go.sum +++ b/go.sum @@ -469,6 +469,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= +github.com/stellar/go-stellar-xdr-json v0.0.0-20250203230501-c6fa0788fe99 h1:m6P+35IPHoxYjAnEgZOgSiDXwEnFCIOpXdwNt8tZ/cw= +github.com/stellar/go-stellar-xdr-json v0.0.0-20250203230501-c6fa0788fe99/go.mod h1:BQ9LWqVJ14qa7ctsyLmjEHSx3Pi7r/8A0mbKzib4cgs= github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2 h1:OzCVd0SV5qE3ZcDeSFCmOWLZfEWZ3Oe8KtmSOYKEVWE= github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stellar/throttled v2.2.3-0.20190823235211-89d75816f59d+incompatible h1:jMXXAcz6xTarGDQ4VtVbtERogcmDQw4RaE85Cr9CgoQ= diff --git a/ingest/account_merge_details.go b/ingest/account_merge_details.go index b439a49ac5..f75ed00d1a 100644 --- a/ingest/account_merge_details.go +++ b/ingest/account_merge_details.go @@ -8,7 +8,7 @@ type AccountMergeDetail struct { AccountMuxedID uint64 `json:"account_muxed_id,string"` Into string `json:"into"` IntoMuxed string `json:"into_muxed"` - IntoMuxedID uint64 `json:"into_muxed_id"` + IntoMuxedID uint64 `json:"into_muxed_id,string"` } func (o *LedgerOperation) AccountMergeDetails() (AccountMergeDetail, error) { diff --git a/ingest/invoke_host_function_details.go b/ingest/invoke_host_function_details.go index 58a2720c46..69a74f8d16 100644 --- a/ingest/invoke_host_function_details.go +++ b/ingest/invoke_host_function_details.go @@ -12,8 +12,7 @@ type InvokeHostFunctionDetail struct { LedgerKeyHash []string `json:"ledger_key_hash"` ContractID string `json:"contract_id"` ContractCodeHash string `json:"contract_code_hash"` - Parameters []map[string]string `json:"parameters"` - ParametersDecoded []map[string]string `json:"parameters_decoded"` + Parameters []interface{} `json:"parameters"` AssetBalanceChanges []BalanceChangeDetail `json:"asset_balance_changes"` From string `json:"from"` Address string `json:"address"` @@ -56,8 +55,10 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (InvokeHostFunctionDetail, invokeHostFunctionDetail.ContractCodeHash = contractCodeHash } - // TODO: Parameters should be processed with xdr2json - invokeHostFunctionDetail.Parameters, invokeHostFunctionDetail.ParametersDecoded = o.serializeParameters(args) + invokeHostFunctionDetail.Parameters, err = o.serializeParameters(args) + if err != nil { + return InvokeHostFunctionDetail{}, err + } balanceChanges, err := o.parseAssetBalanceChangesFromContractEvents() if err != nil { @@ -101,7 +102,10 @@ func (o *LedgerOperation) InvokeHostFunctionDetails() (InvokeHostFunctionDetail, // ConstructorArgs is a list of ScVals // This will initially be handled the same as InvokeContractParams until a different // model is found necessary. - invokeHostFunctionDetail.Parameters, invokeHostFunctionDetail.ParametersDecoded = o.serializeParameters(args.ConstructorArgs) + invokeHostFunctionDetail.Parameters, err = o.serializeParameters(args.ConstructorArgs) + if err != nil { + return InvokeHostFunctionDetail{}, err + } default: return InvokeHostFunctionDetail{}, fmt.Errorf("unknown host function type: %s", op.HostFunction.Type) } diff --git a/ingest/ledger_operation.go b/ingest/ledger_operation.go index 62f39c8271..981ac8bfb2 100644 --- a/ingest/ledger_operation.go +++ b/ingest/ledger_operation.go @@ -1,11 +1,12 @@ package ingest import ( - "encoding/base64" + "encoding/json" "fmt" "math/big" "github.com/dgryski/go-farm" + "github.com/stellar/go-stellar-xdr-json/xdr2json" "github.com/stellar/go/amount" "github.com/stellar/go/support/contractevents" "github.com/stellar/go/toid" @@ -362,32 +363,29 @@ func (o *LedgerOperation) getLiquidityPoolAndProductDelta(lpID *xdr.PoolId) (*xd return nil, nil, fmt.Errorf("liquidity pool change not found") } -func (o *LedgerOperation) serializeParameters(args []xdr.ScVal) ([]map[string]string, []map[string]string) { - params := make([]map[string]string, 0, len(args)) - paramsDecoded := make([]map[string]string, 0, len(args)) +func (o *LedgerOperation) serializeParameters(args []xdr.ScVal) ([]interface{}, error) { + var params []interface{} for _, param := range args { - serializedParam := map[string]string{} - serializedParam["value"] = "n/a" - serializedParam["type"] = "n/a" - - serializedParamDecoded := map[string]string{} - serializedParamDecoded["value"] = "n/a" - serializedParamDecoded["type"] = "n/a" - - if scValTypeName, ok := param.ArmForSwitch(int32(param.Type)); ok { - serializedParam["type"] = scValTypeName - serializedParamDecoded["type"] = scValTypeName - if raw, err := param.MarshalBinary(); err == nil { - serializedParam["value"] = base64.StdEncoding.EncodeToString(raw) - serializedParamDecoded["value"] = param.String() + if _, ok := param.ArmForSwitch(int32(param.Type)); ok { + var err error + var raw []byte + var jsonMessage json.RawMessage + raw, err = param.MarshalBinary() + if err != nil { + return nil, err } + + jsonMessage, err = xdr2json.ConvertBytes(xdr.ScVal{}, raw) + if err != nil { + return nil, err + } + + params = append(params, jsonMessage) } - params = append(params, serializedParam) - paramsDecoded = append(paramsDecoded, serializedParamDecoded) } - return params, paramsDecoded + return params, nil } func (o *LedgerOperation) parseAssetBalanceChangesFromContractEvents() ([]BalanceChangeDetail, error) { diff --git a/ingest/ledger_operation_test.go b/ingest/ledger_operation_test.go index f70a8534c7..74c6cb6d37 100644 --- a/ingest/ledger_operation_test.go +++ b/ingest/ledger_operation_test.go @@ -1,12 +1,16 @@ package ingest import ( + "encoding/json" "testing" "github.com/stellar/go/xdr" "github.com/stretchr/testify/assert" ) +var testScSymbol xdr.ScSymbol = "test" +var testScBool bool = true + func TestOperation(t *testing.T) { o := LedgerOperation{ OperationIndex: int32(0), @@ -1392,41 +1396,50 @@ func resultTestOutput() []testOutput { { err: nil, result: LiquidityPoolDepositDetail{ - LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - MaxPrice: 1e+06, - MaxPriceN: 1000000, - MaxPriceD: 1, - MinPrice: 1e-06, - MinPriceN: 1, - MinPriceD: 1000000, - ReserveAAssetCode: "USDT", - ReserveAAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - ReserveAAssetType: "credit_alphanum4", - ReserveADepositAmount: int64(1), - ReserveAMaxAmount: int64(1000), - ReserveBAssetCode: "USDT", - ReserveBAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - ReserveBAssetType: "credit_alphanum4", - ReserveBDepositAmount: 1, - ReserveBMaxAmount: int64(100), - SharesReceived: 1, + LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + MaxPrice: 1e+06, + MaxPriceN: 1000000, + MaxPriceD: 1, + MinPrice: 1e-06, + MinPriceN: 1, + MinPriceD: 1000000, + ReserveAssetA: ReserveAsset{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + DepositAmount: int64(1), + MaxAmount: int64(1000), + }, + ReserveAssetB: ReserveAsset{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + DepositAmount: 1, + MaxAmount: int64(100), + }, + SharesReceived: 1, }, }, { err: nil, result: LiquidityPoolWithdrawDetail{ - LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - ReserveAAssetCode: "USDT", - ReserveAAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - ReserveAAssetType: "credit_alphanum4", - ReserveAMinAmount: int64(1), - ReserveAWithdrawAmount: int64(-1), - ReserveBAssetCode: "USDT", - ReserveBAssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", - ReserveBAssetType: "credit_alphanum4", - ReserveBMinAmount: int64(1), - ReserveBWithdrawAmount: int64(-1), - Shares: int64(4), + LiquidityPoolID: "AQIDBAUGBwgJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ReserveAssetA: ReserveAsset{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + MinAmount: int64(1), + WithdrawAmount: int64(-1), + }, + + ReserveAssetB: ReserveAsset{ + AssetCode: "USDT", + AssetIssuer: "GBVVRXLMNCJQW3IDDXC3X6XCH35B5Q7QXNMMFPENSOGUPQO7WO7HGZPA", + AssetType: "credit_alphanum4", + MinAmount: int64(1), + WithdrawAmount: int64(-1), + }, + Shares: int64(4), }, }, { @@ -1436,25 +1449,15 @@ func resultTestOutput() []testOutput { ContractID: "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", Function: "HostFunctionTypeHostFunctionTypeInvokeContract", LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - Parameters: []map[string]string{ - { - "type": "Address", - "value": "AAAAEgAAAAESNFZ4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - }, - { - "type": "Sym", - "value": "AAAADwAAAAR0ZXN0", - }, - }, - ParametersDecoded: []map[string]string{ - { - "type": "Address", - "value": "CAJDIVTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABR37", - }, - { - "type": "Sym", - "value": "test", + Parameters: []interface{}{ + json.RawMessage{ + 0x7b, 0x22, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x3a, 0x22, 0x43, 0x41, 0x4a, 0x44, + 0x49, 0x56, 0x54, 0x59, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x42, 0x52, 0x33, 0x37, 0x22, 0x7d, }, + json.RawMessage{0x7b, 0x22, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x22, 0x3a, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x7d}, }, Type: "invoke_contract", }, @@ -1493,17 +1496,8 @@ func resultTestOutput() []testOutput { From: "asset", Function: "HostFunctionTypeHostFunctionTypeCreateContractV2", LedgerKeyHash: []string{"AAAABgAAAAESNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=="}, - Parameters: []map[string]string{ - { - "type": "B", - "value": "AAAAAAAAAAE=", - }, - }, - ParametersDecoded: []map[string]string{ - { - "type": "B", - "value": "true", - }, + Parameters: []interface{}{ + json.RawMessage{0x7b, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x74, 0x72, 0x75, 0x65, 0x7d}, }, Type: "create_contract_v2", }, diff --git a/ingest/liquidity_pool_deposit_details.go b/ingest/liquidity_pool_deposit_details.go index 8d7dce32d6..d4d38635f8 100644 --- a/ingest/liquidity_pool_deposit_details.go +++ b/ingest/liquidity_pool_deposit_details.go @@ -8,24 +8,26 @@ import ( ) type LiquidityPoolDepositDetail struct { - LiquidityPoolID string `json:"liquidity_pool_id"` - ReserveAAssetCode string `json:"reserve_a_asset_code"` - ReserveAAssetIssuer string `json:"reserve_a_asset_issuer"` - ReserveAAssetType string `json:"reserve_a_asset_type"` - ReserveAMaxAmount int64 `json:"reserve_a_max_amount,string"` - ReserveADepositAmount int64 `json:"reserve_a_deposit_amount,string"` - ReserveBAssetCode string `json:"reserve_b_asset_code"` - ReserveBAssetIssuer string `json:"reserve_b_asset_issuer"` - ReserveBAssetType string `json:"reserve_b_asset_type"` - ReserveBMaxAmount int64 `json:"reserve_b_max_amount,string"` - ReserveBDepositAmount int64 `json:"reserve_b_deposit_amount,string"` - MinPriceN int32 `json:"min_price_n"` - MinPriceD int32 `json:"min_price_d"` - MinPrice float64 `json:"min_price"` - MaxPriceN int32 `json:"max_price_n"` - MaxPriceD int32 `json:"max_price_d"` - MaxPrice float64 `json:"max_price"` - SharesReceived int64 `json:"shares_received,string"` + LiquidityPoolID string `json:"liquidity_pool_id"` + ReserveAssetA ReserveAsset `json:"reserve_asset_a"` + ReserveAssetB ReserveAsset `json:"reserve_asset_b"` + MinPriceN int32 `json:"min_price_n"` + MinPriceD int32 `json:"min_price_d"` + MinPrice float64 `json:"min_price"` + MaxPriceN int32 `json:"max_price_n"` + MaxPriceD int32 `json:"max_price_d"` + MaxPrice float64 `json:"max_price"` + SharesReceived int64 `json:"shares_received,string"` +} + +type ReserveAsset struct { + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + MinAmount int64 `json:"min_amount,string"` + MaxAmount int64 `json:"max_amount,string"` + DepositAmount int64 `json:"deposit_amount,string"` + WithdrawAmount int64 `json:"withdraw_amount,string"` } func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDetail, error) { @@ -35,12 +37,16 @@ func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDet } liquidityPoolDepositDetail := LiquidityPoolDepositDetail{ - ReserveAMaxAmount: int64(op.MaxAmountA), - ReserveBMaxAmount: int64(op.MaxAmountB), - MinPriceN: int32(op.MinPrice.N), - MinPriceD: int32(op.MinPrice.D), - MaxPriceN: int32(op.MaxPrice.N), - MaxPriceD: int32(op.MaxPrice.D), + ReserveAssetA: ReserveAsset{ + MaxAmount: int64(op.MaxAmountA), + }, + ReserveAssetB: ReserveAsset{ + MaxAmount: int64(op.MaxAmountB), + }, + MinPriceN: int32(op.MinPrice.N), + MinPriceD: int32(op.MinPrice.D), + MaxPriceN: int32(op.MaxPrice.N), + MaxPriceD: int32(op.MaxPrice.D), } var err error @@ -79,10 +85,10 @@ func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDet return LiquidityPoolDepositDetail{}, err } - liquidityPoolDepositDetail.ReserveAAssetCode = assetACode - liquidityPoolDepositDetail.ReserveAAssetIssuer = assetAIssuer - liquidityPoolDepositDetail.ReserveAAssetType = assetAType - liquidityPoolDepositDetail.ReserveADepositAmount = int64(depositedA) + liquidityPoolDepositDetail.ReserveAssetA.AssetCode = assetACode + liquidityPoolDepositDetail.ReserveAssetA.AssetIssuer = assetAIssuer + liquidityPoolDepositDetail.ReserveAssetA.AssetType = assetAType + liquidityPoolDepositDetail.ReserveAssetA.DepositAmount = int64(depositedA) //Process ReserveB Details var assetBCode, assetBIssuer, assetBType string @@ -91,10 +97,10 @@ func (o *LedgerOperation) LiquidityPoolDepositDetails() (LiquidityPoolDepositDet return LiquidityPoolDepositDetail{}, err } - liquidityPoolDepositDetail.ReserveBAssetCode = assetBCode - liquidityPoolDepositDetail.ReserveBAssetIssuer = assetBIssuer - liquidityPoolDepositDetail.ReserveBAssetType = assetBType - liquidityPoolDepositDetail.ReserveBDepositAmount = int64(depositedB) + liquidityPoolDepositDetail.ReserveAssetB.AssetCode = assetBCode + liquidityPoolDepositDetail.ReserveAssetB.AssetIssuer = assetBIssuer + liquidityPoolDepositDetail.ReserveAssetB.AssetType = assetBType + liquidityPoolDepositDetail.ReserveAssetB.DepositAmount = int64(depositedB) liquidityPoolDepositDetail.MinPrice, err = strconv.ParseFloat(op.MinPrice.String(), 64) if err != nil { diff --git a/ingest/liquidity_pool_withdraw_details.go b/ingest/liquidity_pool_withdraw_details.go index 94d4a93ac7..44d750fbab 100644 --- a/ingest/liquidity_pool_withdraw_details.go +++ b/ingest/liquidity_pool_withdraw_details.go @@ -7,18 +7,10 @@ import ( ) type LiquidityPoolWithdrawDetail struct { - LiquidityPoolID string `json:"liquidity_pool_id"` - ReserveAAssetCode string `json:"reserve_a_asset_code"` - ReserveAAssetIssuer string `json:"reserve_a_asset_issuer"` - ReserveAAssetType string `json:"reserve_a_asset_type"` - ReserveAMinAmount int64 `json:"reserve_a_min_amount,string"` - ReserveAWithdrawAmount int64 `json:"reserve_a_withdraw_amount,string"` - ReserveBAssetCode string `json:"reserve_b_asset_code"` - ReserveBAssetIssuer string `json:"reserve_b_asset_issuer"` - ReserveBAssetType string `json:"reserve_b_asset_type"` - ReserveBMinAmount int64 `json:"reserve_b_min_amount,string"` - ReserveBWithdrawAmount int64 `json:"reserve_b_withdraw_amount,string"` - Shares int64 `json:"shares,string"` + LiquidityPoolID string `json:"liquidity_pool_id"` + ReserveAssetA ReserveAsset `json:"reserve_asset_a"` + ReserveAssetB ReserveAsset `json:"reserve_asset_b"` + Shares int64 `json:"shares,string"` } func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawDetail, error) { @@ -28,9 +20,13 @@ func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawD } liquidityPoolWithdrawDetail := LiquidityPoolWithdrawDetail{ - ReserveAMinAmount: int64(op.MinAmountA), - ReserveBMinAmount: int64(op.MinAmountB), - Shares: int64(op.Amount), + ReserveAssetA: ReserveAsset{ + MinAmount: int64(op.MinAmountA), + }, + ReserveAssetB: ReserveAsset{ + MinAmount: int64(op.MinAmountB), + }, + Shares: int64(op.Amount), } var err error @@ -66,10 +62,10 @@ func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawD return LiquidityPoolWithdrawDetail{}, err } - liquidityPoolWithdrawDetail.ReserveAAssetCode = assetACode - liquidityPoolWithdrawDetail.ReserveAAssetIssuer = assetAIssuer - liquidityPoolWithdrawDetail.ReserveAAssetType = assetAType - liquidityPoolWithdrawDetail.ReserveAWithdrawAmount = int64(receivedA) + liquidityPoolWithdrawDetail.ReserveAssetA.AssetCode = assetACode + liquidityPoolWithdrawDetail.ReserveAssetA.AssetIssuer = assetAIssuer + liquidityPoolWithdrawDetail.ReserveAssetA.AssetType = assetAType + liquidityPoolWithdrawDetail.ReserveAssetA.WithdrawAmount = int64(receivedA) // Process AssetB Details var assetBCode, assetBIssuer, assetBType string @@ -78,10 +74,10 @@ func (o *LedgerOperation) LiquidityPoolWithdrawDetails() (LiquidityPoolWithdrawD return LiquidityPoolWithdrawDetail{}, err } - liquidityPoolWithdrawDetail.ReserveBAssetCode = assetBCode - liquidityPoolWithdrawDetail.ReserveBAssetIssuer = assetBIssuer - liquidityPoolWithdrawDetail.ReserveBAssetType = assetBType - liquidityPoolWithdrawDetail.ReserveBWithdrawAmount = int64(receivedB) + liquidityPoolWithdrawDetail.ReserveAssetB.AssetCode = assetBCode + liquidityPoolWithdrawDetail.ReserveAssetB.AssetIssuer = assetBIssuer + liquidityPoolWithdrawDetail.ReserveAssetB.AssetType = assetBType + liquidityPoolWithdrawDetail.ReserveAssetB.WithdrawAmount = int64(receivedB) return liquidityPoolWithdrawDetail, nil }