diff --git a/ingest/change.go b/ingest/change.go index 6680faa057..e60bef5a98 100644 --- a/ingest/change.go +++ b/ingest/change.go @@ -4,7 +4,10 @@ import ( "bytes" "fmt" "sort" + "time" + "github.com/stellar/go/ingest/ledger" + "github.com/stellar/go/ingest/ledgerentry" "github.com/stellar/go/support/errors" "github.com/stellar/go/xdr" ) @@ -327,3 +330,128 @@ func (c Change) AccountChangedExceptSigners() (bool, error) { return !bytes.Equal(preBinary, postBinary), nil } + +func (c Change) ExtractEntry() (xdr.LedgerEntry, xdr.LedgerEntryChangeType, bool, error) { + switch changeType := c.LedgerEntryChangeType(); changeType { + case xdr.LedgerEntryChangeTypeLedgerEntryCreated, xdr.LedgerEntryChangeTypeLedgerEntryUpdated: + return *c.Post, changeType, false, nil + case xdr.LedgerEntryChangeTypeLedgerEntryRemoved: + return *c.Pre, changeType, true, nil + default: + return xdr.LedgerEntry{}, changeType, false, fmt.Errorf("unable to extract ledger entry type from change") + } +} + +func (c Change) Deleted() bool { + return c.LedgerEntryChangeType() == xdr.LedgerEntryChangeTypeLedgerEntryRemoved +} + +func (c Change) ClosedAt() time.Time { + if c.Ledger != nil { + return ledger.ClosedAt(*c.Ledger) + } + + return ledger.ClosedAt(c.Transaction.Ledger) +} + +func (c Change) Sequence() uint32 { + if c.Ledger != nil { + return ledger.Sequence(*c.Ledger) + } + + return ledger.Sequence(c.Transaction.Ledger) +} + +func (c Change) LastModifiedLedger() (uint32, error) { + ledgerEntry, _, _, err := c.ExtractEntry() + if err != nil { + return 0, err + } + + return uint32(ledgerEntry.LastModifiedLedgerSeq), nil +} + +func (c Change) Sponsor() (string, error) { + ledgerEntry, _, _, err := c.ExtractEntry() + if err != nil { + return "", err + } + + if ledgerEntry.SponsoringID() == nil { + return "", nil + } + + return ledgerEntry.SponsoringID().Address(), nil +} + +func (c Change) LedgerKeyHash() (string, error) { + ledgerKey, err := c.LedgerKey() + if err != nil { + return "", err + } + + return ledgerKey.MarshalBinaryBase64() +} + +func (c Change) EntryDetails(passphrase string) (interface{}, error) { + var err error + var ledgerEntry xdr.LedgerEntry + var details interface{} + + ledgerEntry, _, _, err = c.ExtractEntry() + if err != nil { + return nil, err + } + + switch ledgerEntry.Data.Type { + case xdr.LedgerEntryTypeAccount: + details, err = ledgerentry.AccountDetails(ledgerEntry.Data.Account) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeTrustline: + details, err = ledgerentry.TrustlineDetails(ledgerEntry.Data.TrustLine) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeOffer: + details, err = ledgerentry.OfferDetails(ledgerEntry.Data.Offer) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeData: + details, err = ledgerentry.DataDetails(ledgerEntry.Data.Data) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeClaimableBalance: + details, err = ledgerentry.ClaimableBalanceDetails(ledgerEntry.Data.ClaimableBalance) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeLiquidityPool: + details, err = ledgerentry.LiquidityPoolDetails(ledgerEntry.Data.LiquidityPool) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeContractData: + details, err = ledgerentry.ContractDataDetails(passphrase, ledgerEntry.Data.ContractData) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeContractCode: + details, err = ledgerentry.ContractCodeDetails(ledgerEntry.Data.ContractCode) + if err != nil { + return details, err + } + case xdr.LedgerEntryTypeTtl: + details, err = ledgerentry.TtlDetails(ledgerEntry.Data.Ttl) + if err != nil { + return details, err + } + default: + return details, fmt.Errorf("unknown LedgerEntry data type") + } + + return details, nil +} diff --git a/ingest/ledgerentry/account.go b/ingest/ledgerentry/account.go new file mode 100644 index 0000000000..33437f52d9 --- /dev/null +++ b/ingest/ledgerentry/account.go @@ -0,0 +1,76 @@ +package ledgerentry + +import ( + "github.com/stellar/go/xdr" +) + +type Account struct { + AccountID string `json:"account_id"` + Balance int64 `json:"balance"` + SequenceNumber int64 `json:"sequence_number"` + SequenceLedger uint32 `json:"sequence_ledger"` + SequenceTime int64 `json:"sequence_time"` + NumSubentries uint32 `json:"num_subentries"` + Flags uint32 `json:"flags"` + HomeDomain string `json:"home_domain"` + MasterWeight int32 `json:"master_weight"` + ThresholdLow int32 `json:"threshold_low"` + ThresholdMedium int32 `json:"threshold_medium"` + ThresholdHigh int32 `json:"threshold_high"` + NumSponsored uint32 `json:"num_sponsored"` + NumSponsoring uint32 `json:"num_sponsoring"` + BuyingLiabilities int64 `json:"buying_liabilities"` + SellingLiabilities int64 `json:"selling_liabilities"` + InflationDestination string `json:"inflation_destination"` + Signers []Signers `json:"signers"` +} + +type Signers struct { + Address string + Weight int32 + Sponsor string +} + +func AccountDetails(accountEntry *xdr.AccountEntry) (Account, error) { + account := Account{ + AccountID: accountEntry.AccountId.Address(), + SequenceNumber: int64(accountEntry.SeqNum), + SequenceLedger: uint32(accountEntry.SeqLedger()), + SequenceTime: int64(accountEntry.SeqTime()), + NumSubentries: uint32(accountEntry.NumSubEntries), + Flags: uint32(accountEntry.Flags), + HomeDomain: string(accountEntry.HomeDomain), + MasterWeight: int32(accountEntry.MasterKeyWeight()), + ThresholdLow: int32(accountEntry.ThresholdLow()), + ThresholdMedium: int32(accountEntry.ThresholdMedium()), + ThresholdHigh: int32(accountEntry.ThresholdHigh()), + NumSponsored: uint32(accountEntry.NumSponsored()), + NumSponsoring: uint32(accountEntry.NumSponsoring()), + } + + if accountEntry.InflationDest != nil { + account.InflationDestination = accountEntry.InflationDest.Address() + } + + accountExtensionInfo, ok := accountEntry.Ext.GetV1() + if ok { + account.BuyingLiabilities = int64(accountExtensionInfo.Liabilities.Buying) + account.SellingLiabilities = int64(accountExtensionInfo.Liabilities.Selling) + } + + signers := []Signers{} + sponsors := accountEntry.SponsorPerSigner() + for signer, weight := range accountEntry.SignerSummary() { + sponsorDesc := sponsors[signer] + + signers = append(signers, Signers{ + Address: signer, + Weight: weight, + Sponsor: sponsorDesc.Address(), + }) + } + + account.Signers = signers + + return account, nil +} diff --git a/ingest/ledgerentry/claimable_balance.go b/ingest/ledgerentry/claimable_balance.go new file mode 100644 index 0000000000..ca64713f21 --- /dev/null +++ b/ingest/ledgerentry/claimable_balance.go @@ -0,0 +1,62 @@ +package ledgerentry + +import ( + "github.com/stellar/go/xdr" +) + +type ClaimableBalance struct { + BalanceID string `json:"balance_id"` + Claimants []Claimant `json:"claimants"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + AssetID int64 `json:"asset_id"` + Amount int64 `json:"amount"` + Flags uint32 `json:"flags"` +} + +type Claimant struct { + Destination string `json:"destination"` + Predicate xdr.ClaimPredicate `json:"predicate"` +} + +func ClaimableBalanceDetails(claimableBalanceEntry *xdr.ClaimableBalanceEntry) (ClaimableBalance, error) { + claimableBalance := ClaimableBalance{ + Amount: int64(claimableBalanceEntry.Amount), + Flags: uint32(claimableBalanceEntry.Flags()), + } + + var err error + var balanceID string + balanceID, err = xdr.MarshalBase64(claimableBalanceEntry.BalanceId) + if err != nil { + return ClaimableBalance{}, err + } + + claimableBalance.BalanceID = balanceID + + var assetType, assetCode, assetIssuer string + err = claimableBalanceEntry.Asset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return ClaimableBalance{}, err + } + + claimableBalance.AssetCode = assetCode + claimableBalance.AssetIssuer = assetIssuer + claimableBalance.AssetType = assetType + + var claimants []Claimant + for _, c := range claimableBalanceEntry.Claimants { + switch c.Type { + case 0: + claimants = append(claimants, Claimant{ + Destination: c.V0.Destination.Address(), + Predicate: c.V0.Predicate, + }) + } + } + + claimableBalance.Claimants = claimants + + return claimableBalance, nil +} diff --git a/ingest/ledgerentry/contract_code.go b/ingest/ledgerentry/contract_code.go new file mode 100644 index 0000000000..0376a79dcc --- /dev/null +++ b/ingest/ledgerentry/contract_code.go @@ -0,0 +1,52 @@ +package ledgerentry + +import ( + "fmt" + + "github.com/stellar/go/xdr" +) + +type ContractCode struct { + ContractCodeHash string `json:"contract_code_hash"` + NInstructions uint32 `json:"n_instructions"` + NFunctions uint32 `json:"n_functions"` + NGlobals uint32 `json:"n_globals"` + NTableEntries uint32 `json:"n_table_entries"` + NTypes uint32 `json:"n_types"` + NDataSegments uint32 `json:"n_data_segments"` + NElemSegments uint32 `json:"n_elem_segments"` + NImports uint32 `json:"n_imports"` + NExports uint32 `json:"n_exports"` + NDataSegmentBytes uint32 `json:"n_data_segment_bytes"` +} + +func ContractCodeDetails(contractCodeEntry *xdr.ContractCodeEntry) (ContractCode, error) { + var contractCode ContractCode + + switch contractCodeEntry.Ext.V { + case 1: + contractCode.NInstructions = uint32(contractCodeEntry.Ext.V1.CostInputs.NInstructions) + contractCode.NFunctions = uint32(contractCodeEntry.Ext.V1.CostInputs.NFunctions) + contractCode.NGlobals = uint32(contractCodeEntry.Ext.V1.CostInputs.NGlobals) + contractCode.NTableEntries = uint32(contractCodeEntry.Ext.V1.CostInputs.NTableEntries) + contractCode.NTypes = uint32(contractCodeEntry.Ext.V1.CostInputs.NTypes) + contractCode.NDataSegments = uint32(contractCodeEntry.Ext.V1.CostInputs.NDataSegments) + contractCode.NElemSegments = uint32(contractCodeEntry.Ext.V1.CostInputs.NElemSegments) + contractCode.NImports = uint32(contractCodeEntry.Ext.V1.CostInputs.NImports) + contractCode.NExports = uint32(contractCodeEntry.Ext.V1.CostInputs.NExports) + contractCode.NDataSegmentBytes = uint32(contractCodeEntry.Ext.V1.CostInputs.NDataSegmentBytes) + default: + return ContractCode{}, fmt.Errorf("unknown ContractCodeEntry.Ext.V") + } + + var err error + var contractCodeHash string + contractCodeHash, err = contractCodeEntry.Hash.MarshalBinaryBase64() + if err != nil { + return ContractCode{}, err + } + + contractCode.ContractCodeHash = contractCodeHash + + return contractCode, nil +} diff --git a/ingest/ledgerentry/contract_data.go b/ingest/ledgerentry/contract_data.go new file mode 100644 index 0000000000..a8275a6751 --- /dev/null +++ b/ingest/ledgerentry/contract_data.go @@ -0,0 +1,272 @@ +package ledgerentry + +import ( + "math/big" + + "github.com/stellar/go/strkey" + "github.com/stellar/go/xdr" +) + +type ContractData struct { + ContractId string `json:"contract_id"` + ContractKeyType string `json:"contract_key_type"` + Durability string `json:"durability"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + BalanceHolder string `json:"balance_holder"` + Balance string `json:"balance"` + KeyDecoded interface{} `json:"key_decoded"` + ValDecoded interface{} `json:"val_decoded"` +} + +func ContractDataDetails(passphrase string, contractDataEntry *xdr.ContractDataEntry) (ContractData, error) { + contractData := ContractData{ + Durability: contractDataEntry.Durability.String(), + KeyDecoded: contractDataEntry.Key, + ValDecoded: contractDataEntry.Val, + } + + contractAsset := AssetFromContractData(passphrase, *contractDataEntry) + + var err error + var ok bool + var assetType, assetCode, assetIssuer string + err = contractAsset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return ContractData{}, err + } + + contractData.AssetCode = assetCode + contractData.AssetIssuer = assetIssuer + contractData.AssetType = assetType + + var contractDataBalanceHolder string + dataBalanceHolder, dataBalance, ok := ContractBalanceFromContractData(passphrase, *contractDataEntry) + if ok { + holderHashByte, _ := xdr.Hash(dataBalanceHolder).MarshalBinary() + contractDataBalanceHolder, _ = strkey.Encode(strkey.VersionByteContract, holderHashByte) + contractData.BalanceHolder = contractDataBalanceHolder + contractData.Balance = dataBalance.String() + } + + var contractDataContractId xdr.Hash + contractDataContractId, ok = contractDataEntry.Contract.GetContractId() + if ok { + var contractDataContractIdByte []byte + contractDataContractIdByte, err = contractDataContractId.MarshalBinary() + if err != nil { + return ContractData{}, err + } + + var contractDataContractIdString string + contractDataContractIdString, err = strkey.Encode(strkey.VersionByteContract, contractDataContractIdByte) + if err != nil { + return ContractData{}, err + } + + contractData.ContractId = contractDataContractIdString + } + + return contractData, nil +} + +var ( + // these are storage DataKey enum + // https://github.com/stellar/rs-soroban-env/blob/v0.0.16/soroban-env-host/src/native_contract/token/storage_types.rs#L23 + balanceMetadataSym = xdr.ScSymbol("Balance") + issuerSym = xdr.ScSymbol("issuer") + assetCodeSym = xdr.ScSymbol("asset_code") + assetInfoSym = xdr.ScSymbol("AssetInfo") + assetInfoVec = &xdr.ScVec{ + xdr.ScVal{ + Type: xdr.ScValTypeScvSymbol, + Sym: &assetInfoSym, + }, + } + assetInfoKey = xdr.ScVal{ + Type: xdr.ScValTypeScvVec, + Vec: &assetInfoVec, + } +) + +func AssetFromContractData(passphrase string, contractData xdr.ContractDataEntry) *xdr.Asset { + if contractData.Key.Type != xdr.ScValTypeScvLedgerKeyContractInstance { + return nil + } + contractInstanceData, ok := contractData.Val.GetInstance() + if !ok || contractInstanceData.Storage == nil { + return nil + } + + nativeAssetContractID, err := xdr.MustNewNativeAsset().ContractID(passphrase) + if err != nil { + return nil + } + + var assetInfo *xdr.ScVal + for _, mapEntry := range *contractInstanceData.Storage { + if mapEntry.Key.Equals(assetInfoKey) { + // clone the map entry to avoid reference to loop iterator + mapValXdr, cloneErr := mapEntry.Val.MarshalBinary() + if cloneErr != nil { + return nil + } + assetInfo = &xdr.ScVal{} + cloneErr = assetInfo.UnmarshalBinary(mapValXdr) + if cloneErr != nil { + return nil + } + break + } + } + + if assetInfo == nil { + return nil + } + + vecPtr, ok := assetInfo.GetVec() + if !ok || vecPtr == nil || len(*vecPtr) != 2 { + return nil + } + vec := *vecPtr + + sym, ok := vec[0].GetSym() + if !ok { + return nil + } + switch sym { + case "AlphaNum4": + case "AlphaNum12": + case "Native": + if contractData.Contract.ContractId != nil && (*contractData.Contract.ContractId) == nativeAssetContractID { + asset := xdr.MustNewNativeAsset() + return &asset + } + default: + return nil + } + + var assetCode, assetIssuer string + assetMapPtr, ok := vec[1].GetMap() + if !ok || assetMapPtr == nil || len(*assetMapPtr) != 2 { + return nil + } + assetMap := *assetMapPtr + + assetCodeEntry, assetIssuerEntry := assetMap[0], assetMap[1] + if sym, ok = assetCodeEntry.Key.GetSym(); !ok || sym != assetCodeSym { + return nil + } + assetCodeSc, ok := assetCodeEntry.Val.GetStr() + if !ok { + return nil + } + if assetCode = string(assetCodeSc); assetCode == "" { + return nil + } + + if sym, ok = assetIssuerEntry.Key.GetSym(); !ok || sym != issuerSym { + return nil + } + assetIssuerSc, ok := assetIssuerEntry.Val.GetBytes() + if !ok { + return nil + } + assetIssuer, err = strkey.Encode(strkey.VersionByteAccountID, assetIssuerSc) + if err != nil { + return nil + } + + asset, err := xdr.NewCreditAsset(assetCode, assetIssuer) + if err != nil { + return nil + } + + expectedID, err := asset.ContractID(passphrase) + if err != nil { + return nil + } + if contractData.Contract.ContractId == nil || expectedID != *(contractData.Contract.ContractId) { + return nil + } + + return &asset +} + +// ContractBalanceFromContractData takes a ledger entry and verifies that the +// ledger entry corresponds to the balance entry written to contract storage by +// the Stellar Asset Contract. +// +// Reference: +// +// https://github.com/stellar/rs-soroban-env/blob/da325551829d31dcbfa71427d51c18e71a121c5f/soroban-env-host/src/native_contract/token/storage_types.rs#L11-L24 +func ContractBalanceFromContractData(passphrase string, contractData xdr.ContractDataEntry) ([32]byte, *big.Int, bool) { + _, err := xdr.MustNewNativeAsset().ContractID(passphrase) + if err != nil { + return [32]byte{}, nil, false + } + + if contractData.Contract.ContractId == nil { + return [32]byte{}, nil, false + } + + keyEnumVecPtr, ok := contractData.Key.GetVec() + if !ok || keyEnumVecPtr == nil { + return [32]byte{}, nil, false + } + keyEnumVec := *keyEnumVecPtr + if len(keyEnumVec) != 2 || !keyEnumVec[0].Equals( + xdr.ScVal{ + Type: xdr.ScValTypeScvSymbol, + Sym: &balanceMetadataSym, + }, + ) { + return [32]byte{}, nil, false + } + + scAddress, ok := keyEnumVec[1].GetAddress() + if !ok { + return [32]byte{}, nil, false + } + + holder, ok := scAddress.GetContractId() + if !ok { + return [32]byte{}, nil, false + } + + balanceMapPtr, ok := contractData.Val.GetMap() + if !ok || balanceMapPtr == nil { + return [32]byte{}, nil, false + } + balanceMap := *balanceMapPtr + if !ok || len(balanceMap) != 3 { + return [32]byte{}, nil, false + } + + var keySym xdr.ScSymbol + if keySym, ok = balanceMap[0].Key.GetSym(); !ok || keySym != "amount" { + return [32]byte{}, nil, false + } + if keySym, ok = balanceMap[1].Key.GetSym(); !ok || keySym != "authorized" || + !balanceMap[1].Val.IsBool() { + return [32]byte{}, nil, false + } + if keySym, ok = balanceMap[2].Key.GetSym(); !ok || keySym != "clawback" || + !balanceMap[2].Val.IsBool() { + return [32]byte{}, nil, false + } + amount, ok := balanceMap[0].Val.GetI128() + if !ok { + return [32]byte{}, nil, false + } + + // amount cannot be negative + // https://github.com/stellar/rs-soroban-env/blob/a66f0815ba06a2f5328ac420950690fd1642f887/soroban-env-host/src/native_contract/token/balance.rs#L92-L93 + if int64(amount.Hi) < 0 { + return [32]byte{}, nil, false + } + amt := new(big.Int).Lsh(new(big.Int).SetInt64(int64(amount.Hi)), 64) + amt.Add(amt, new(big.Int).SetUint64(uint64(amount.Lo))) + return holder, amt, true +} diff --git a/ingest/ledgerentry/data.go b/ingest/ledgerentry/data.go new file mode 100644 index 0000000000..47292bc73f --- /dev/null +++ b/ingest/ledgerentry/data.go @@ -0,0 +1,25 @@ +package ledgerentry + +import "github.com/stellar/go/xdr" + +type Data struct { + AccountID string `json:"account_id"` + DataName string `json:"data_name"` + DataValue string `json:"data_value"` +} + +func DataDetails(dataEntry *xdr.DataEntry) (Data, error) { + data := Data{ + AccountID: dataEntry.AccountId.Address(), + DataName: string(dataEntry.DataName), + } + + dataValue, err := xdr.MarshalBase64(dataEntry.DataValue) + if err != nil { + return Data{}, err + } + + data.DataValue = dataValue + + return data, nil +} diff --git a/ingest/ledgerentry/liquidity_pool.go b/ingest/ledgerentry/liquidity_pool.go new file mode 100644 index 0000000000..79255d6272 --- /dev/null +++ b/ingest/ledgerentry/liquidity_pool.go @@ -0,0 +1,71 @@ +package ledgerentry + +import ( + "github.com/stellar/go/xdr" +) + +type LiquidityPool struct { + LiquidityPoolID string `json:"liquidity_pool_id"` + Type int32 `json:"type"` + Fee int32 `json:"fee"` + TrustlineCount int64 `json:"trustline_count"` + PoolShareCount int64 `json:"pool_share_count"` + AssetAType string `json:"asset_a_type"` + AssetACode string `json:"asset_a_code"` + AssetAIssuer string `json:"asset_a_issuer"` + AssetAReserve int64 `json:"asset_a_amount"` + AssetBType string `json:"asset_b_type"` + AssetBCode string `json:"asset_b_code"` + AssetBIssuer string `json:"asset_b_issuer"` + AssetBReserve int64 `json:"asset_b_amount"` +} + +func LiquidityPoolDetails(liquidityPoolEntry *xdr.LiquidityPoolEntry) (LiquidityPool, error) { + lp := LiquidityPool{ + Type: int32(liquidityPoolEntry.Body.Type), + } + + var err error + var liquidtiyPoolID string + liquidtiyPoolID, err = xdr.MarshalBase64(liquidityPoolEntry.LiquidityPoolId) + if err != nil { + return LiquidityPool{}, err + } + + lp.LiquidityPoolID = liquidtiyPoolID + + var ok bool + var constantProduct xdr.LiquidityPoolEntryConstantProduct + constantProduct, ok = liquidityPoolEntry.Body.GetConstantProduct() + if !ok { + return lp, nil + } + + lp.Fee = int32(constantProduct.Params.Fee) + lp.TrustlineCount = int64(constantProduct.PoolSharesTrustLineCount) + lp.PoolShareCount = int64(constantProduct.TotalPoolShares) + + var assetAType, assetACode, assetAIssuer string + err = constantProduct.Params.AssetA.Extract(&assetAType, &assetACode, &assetAIssuer) + if err != nil { + return LiquidityPool{}, err + } + + lp.AssetACode = assetACode + lp.AssetAIssuer = assetAIssuer + lp.AssetAType = assetAType + lp.AssetAReserve = int64(constantProduct.ReserveA) + + var assetBType, assetBCode, assetBIssuer string + err = constantProduct.Params.AssetB.Extract(&assetBType, &assetBCode, &assetBIssuer) + if err != nil { + return LiquidityPool{}, err + } + + lp.AssetBCode = assetBCode + lp.AssetBIssuer = assetBIssuer + lp.AssetBType = assetBType + lp.AssetBReserve = int64(constantProduct.ReserveB) + + return lp, nil +} diff --git a/ingest/ledgerentry/offer.go b/ingest/ledgerentry/offer.go new file mode 100644 index 0000000000..041eef8a03 --- /dev/null +++ b/ingest/ledgerentry/offer.go @@ -0,0 +1,52 @@ +package ledgerentry + +import "github.com/stellar/go/xdr" + +type Offer struct { + SellerID string `json:"seller_id"` + OfferID int64 `json:"offer_id"` + SellingAssetType string `json:"selling_asset_type"` + SellingAssetCode string `json:"selling_asset_code"` + SellingAssetIssuer string `json:"selling_asset_issuer"` + BuyingAssetType string `json:"buying_asset_type"` + BuyingAssetCode string `json:"buying_asset_code"` + BuyingAssetIssuer string `json:"buying_asset_issuer"` + Amount int64 `json:"amount"` + PriceN int32 `json:"pricen"` + PriceD int32 `json:"priced"` + Flags uint32 `json:"flags"` +} + +func OfferDetails(offerEntry *xdr.OfferEntry) (Offer, error) { + offer := Offer{ + SellerID: offerEntry.SellerId.Address(), + OfferID: int64(offerEntry.OfferId), + Amount: int64(offerEntry.Amount), + PriceN: int32(offerEntry.Price.N), + PriceD: int32(offerEntry.Price.D), + Flags: uint32(offerEntry.Flags), + } + + var err error + var sellingAssetType, sellingAssetCode, sellingAssetIssuer string + err = offerEntry.Selling.Extract(&sellingAssetType, &sellingAssetCode, &sellingAssetIssuer) + if err != nil { + return Offer{}, err + } + + offer.SellingAssetCode = sellingAssetCode + offer.SellingAssetIssuer = sellingAssetIssuer + offer.SellingAssetType = sellingAssetType + + var buyingAssetType, buyingAssetCode, buyingAssetIssuer string + err = offerEntry.Buying.Extract(&buyingAssetType, &buyingAssetCode, &buyingAssetIssuer) + if err != nil { + return Offer{}, err + } + + offer.BuyingAssetCode = buyingAssetCode + offer.BuyingAssetIssuer = buyingAssetIssuer + offer.BuyingAssetType = buyingAssetType + + return offer, nil +} diff --git a/ingest/ledgerentry/trustline.go b/ingest/ledgerentry/trustline.go new file mode 100644 index 0000000000..ae7239b230 --- /dev/null +++ b/ingest/ledgerentry/trustline.go @@ -0,0 +1,48 @@ +package ledgerentry + +import "github.com/stellar/go/xdr" + +type Trustline struct { + AccountID string `json:"account_id"` + AssetCode string `json:"asset_code"` + AssetIssuer string `json:"asset_issuer"` + AssetType string `json:"asset_type"` + Balance int64 `json:"balance"` + TrustlineLimit int64 `json:"trustline_limit"` + LiquidityPoolID string `json:"liquidity_pool_id"` + BuyingLiabilities int64 `json:"buying_liabilities"` + SellingLiabilities int64 `json:"selling_liabilities"` + Flags uint32 `json:"flags"` +} + +func TrustlineDetails(trustlineEntry *xdr.TrustLineEntry) (Trustline, error) { + trustline := Trustline{ + AccountID: trustlineEntry.AccountId.Address(), + Balance: int64(trustlineEntry.Balance), + TrustlineLimit: int64(trustlineEntry.Limit), + BuyingLiabilities: int64(trustlineEntry.Liabilities().Buying), + SellingLiabilities: int64(trustlineEntry.Liabilities().Selling), + Flags: uint32(trustlineEntry.Flags), + } + + var err error + var assetType, assetCode, assetIssuer string + err = trustlineEntry.Asset.Extract(&assetType, &assetCode, &assetIssuer) + if err != nil { + return Trustline{}, err + } + + trustline.AssetCode = assetCode + trustline.AssetIssuer = assetIssuer + trustline.AssetType = assetType + + var poolID string + poolID, err = xdr.MarshalBase64(trustlineEntry.Asset.LiquidityPoolId) + if err != nil { + return Trustline{}, err + } + + trustline.LiquidityPoolID = poolID + + return trustline, nil +} diff --git a/ingest/ledgerentry/ttl.go b/ingest/ledgerentry/ttl.go new file mode 100644 index 0000000000..17b871dbb5 --- /dev/null +++ b/ingest/ledgerentry/ttl.go @@ -0,0 +1,13 @@ +package ledgerentry + +import "github.com/stellar/go/xdr" + +type Ttl struct { + LiveUntilLedgerSeq uint32 +} + +func TtlDetails(ttlEntry *xdr.TtlEntry) (Ttl, error) { + return Ttl{ + LiveUntilLedgerSeq: uint32(ttlEntry.LiveUntilLedgerSeq), + }, nil +}