From 94645eb7908985aab71c8d1db43907c46d8d34c7 Mon Sep 17 00:00:00 2001 From: pharr117 <24580777+pharr117@users.noreply.github.com> Date: Sun, 28 Jan 2024 14:00:08 -0500 Subject: [PATCH] Patch/csv parsers various message types (#527) * Fix csv parsing group interface definition: 1. Remove hack for parserfunc that was using specific subwrapper parsing func signature 2. Redefine parsing group struct definitions in each csv parser subtype 3. Add parsing group in accointing for concentrated liquidity position messages * Add cl position group handling for cointracker * Finish implementation of all parsing groups for cl position handling messages --- csv/parsers/accointing/accointing.go | 4 +- csv/parsers/accointing/osmosis.go | 113 +++++++++++++---- csv/parsers/accointing/rows.go | 38 ++++++ csv/parsers/cointracker/cointracker.go | 4 +- csv/parsers/cointracker/osmosis.go | 111 ++++++++++++++++- csv/parsers/cointracker/rows.go | 58 +++++++++ .../cryptotaxcalculator.go | 6 +- csv/parsers/cryptotaxcalculator/osmosis.go | 115 +++++++++++++++++- csv/parsers/cryptotaxcalculator/rows.go | 60 ++++++++- csv/parsers/koinly/koinly.go | 4 +- csv/parsers/koinly/osmosis.go | 115 +++++++++++++++++- csv/parsers/koinly/rows.go | 58 +++++++++ csv/parsers/osmosis.go | 59 ++------- csv/parsers/taxbit/osmosis.go | 115 +++++++++++++++++- csv/parsers/taxbit/rows.go | 58 +++++++++ csv/parsers/taxbit/taxbit.go | 4 +- csv/parsers/types.go | 2 +- csv/parsers/utils.go | 29 +++++ db/search.go | 1 + 19 files changed, 863 insertions(+), 91 deletions(-) diff --git a/csv/parsers/accointing/accointing.go b/csv/parsers/accointing/accointing.go index 298fcfb2..88ed2446 100644 --- a/csv/parsers/accointing/accointing.go +++ b/csv/parsers/accointing/accointing.go @@ -48,7 +48,7 @@ func (p *Parser) ProcessTaxableTx(address string, taxableTxs []db.TaxableTransac // Parse all the TXs found in the Parsing Groups for _, txParsingGroup := range p.ParsingGroups { - err := txParsingGroup.ParseGroup(ParseGroup) + err := txParsingGroup.ParseGroup() if err != nil { return err } @@ -82,7 +82,7 @@ func (p *Parser) ProcessTaxableEvent(taxableEvents []db.TaxableEvent) error { } func (p *Parser) InitializeParsingGroups() { - p.ParsingGroups = append(p.ParsingGroups, parsers.GetOsmosisTxParsingGroups()...) + p.ParsingGroups = append(p.ParsingGroups, &OsmosisLpTxGroup{}, &OsmosisConcentratedLiquidityTxGroup{}) } func (p *Parser) GetRows(address string, startDate, endDate *time.Time) ([]parsers.CsvRow, error) { diff --git a/csv/parsers/accointing/osmosis.go b/csv/parsers/accointing/osmosis.go index 067f7e43..c0ab500a 100644 --- a/csv/parsers/accointing/osmosis.go +++ b/csv/parsers/accointing/osmosis.go @@ -7,12 +7,42 @@ import ( "github.com/DefiantLabs/cosmos-tax-cli/config" "github.com/DefiantLabs/cosmos-tax-cli/csv/parsers" "github.com/DefiantLabs/cosmos-tax-cli/db" - "github.com/DefiantLabs/cosmos-tax-cli/util" + "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/concentratedliquidity" "github.com/preichenberger/go-coinbasepro/v2" ) -func ParseGroup(sf *parsers.WrapperLpTxGroup) error { +type OsmosisLpTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisLpTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisLpTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisLpTxGroup[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisLpTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisLpTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisLpTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + // Add tx to group using the TX ID as key and appending to array + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +func (sf *OsmosisLpTxGroup) ParseGroup() error { cbClient := coinbasepro.NewClient() for _, txMessages := range sf.GroupedTxes { for _, message := range txMessages { @@ -21,27 +51,8 @@ func ParseGroup(sf *parsers.WrapperLpTxGroup) error { row.OperationID = message.Message.Tx.Hash row.Date = message.Message.Tx.Block.TimeStamp.Format(TimeLayout) - denomRecieved := message.DenominationReceived - valueRecieved := message.AmountReceived - conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(valueRecieved), denomRecieved) - if err != nil { - row.InBuyAmount = util.NumericToString(valueRecieved) - row.InBuyAsset = denomRecieved.Base - } else { - row.InBuyAmount = conversionAmount.Text('f', -1) - row.InBuyAsset = conversionSymbol - } - - denomSent := message.DenominationSent - valueSent := message.AmountSent - conversionAmount, conversionSymbol, err = db.ConvertUnits(util.FromNumeric(valueSent), denomSent) - if err != nil { - row.OutSellAmount = util.NumericToString(valueSent) - row.OutSellAsset = denomSent.Base - } else { - row.OutSellAmount = conversionAmount.Text('f', -1) - row.OutSellAsset = conversionSymbol - } + parseAndAddReceivedAmountWithDefault(&row, message) + parseAndAddSentAmountWithDefault(&row, message) // We deliberately exclude the GAMM tokens from OutSell/InBuy for Exits/Joins respectively // Accointing has no way of using the GAMM token to determine LP cost basis etc... @@ -77,3 +88,59 @@ func ParseGroup(sf *parsers.WrapperLpTxGroup) error { } return nil } + +type OsmosisConcentratedLiquidityTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisConcentratedLiquidity[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + // Add tx to group using the TX ID as key and appending to array + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +// Concentrated liquidit txs are grouped to be parsed together. Complex analysis may be require later, so group them now for later extension. +func (sf *OsmosisConcentratedLiquidityTxGroup) ParseGroup() error { + for _, txMessages := range sf.GroupedTxes { + for _, message := range txMessages { + row := Row{} + row.OperationID = message.Message.Tx.Hash + row.Date = message.Message.Tx.Block.TimeStamp.Format(TimeLayout) + + switch message.Message.MessageType.MessageType { + case concentratedliquidity.MsgCreatePosition: + parseAndAddSentAmountWithDefault(&row, message) + case concentratedliquidity.MsgWithdrawPosition, concentratedliquidity.MsgTransferPositions: + parseAndAddReceivedAmountWithDefault(&row, message) + case concentratedliquidity.MsgAddToPosition: + if message.DenominationReceivedID != nil { + parseAndAddReceivedAmountWithDefault(&row, message) + } else { + parseAndAddSentAmountWithDefault(&row, message) + } + } + sf.Rows = append(sf.Rows, row) + } + } + return nil +} diff --git a/csv/parsers/accointing/rows.go b/csv/parsers/accointing/rows.go index 369fc31f..8497b423 100644 --- a/csv/parsers/accointing/rows.go +++ b/csv/parsers/accointing/rows.go @@ -110,3 +110,41 @@ func (row *Row) ParseFee(tx db.Tx, fee db.Fee) error { return nil } + +func parseAndAddSentAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountSent), event.DenominationSent) + if err != nil { + return fmt.Errorf("cannot parse denom units for TX %s (classification: withdrawal)", row.OperationID) + } + row.OutSellAmount = conversionAmount.Text('f', -1) + row.OutSellAsset = conversionSymbol + + return nil +} + +func parseAndAddSentAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddSentAmount(row, event) + if err != nil { + row.OutSellAmount = util.NumericToString(event.AmountSent) + row.OutSellAsset = event.DenominationSent.Base + } +} + +func parseAndAddReceivedAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountReceived), event.DenominationReceived) + if err != nil { + return fmt.Errorf("cannot parse denom units for TX %s (classification: deposit)", row.OperationID) + } + row.InBuyAmount = conversionAmount.Text('f', -1) + row.InBuyAsset = conversionSymbol + + return nil +} + +func parseAndAddReceivedAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddReceivedAmount(row, event) + if err != nil { + row.InBuyAmount = util.NumericToString(event.AmountReceived) + row.InBuyAsset = event.DenominationReceived.Base + } +} diff --git a/csv/parsers/cointracker/cointracker.go b/csv/parsers/cointracker/cointracker.go index 60f60f6c..53f52202 100644 --- a/csv/parsers/cointracker/cointracker.go +++ b/csv/parsers/cointracker/cointracker.go @@ -60,7 +60,7 @@ func (p *Parser) ProcessTaxableTx(address string, taxableTxs []db.TaxableTransac // Parse all the TXs found in the Parsing Groups for _, txParsingGroup := range p.ParsingGroups { - err := txParsingGroup.ParseGroup(ParseGroup) + err := txParsingGroup.ParseGroup() if err != nil { return err } @@ -94,7 +94,7 @@ func (p *Parser) ProcessTaxableEvent(taxableEvents []db.TaxableEvent) error { } func (p *Parser) InitializeParsingGroups() { - p.ParsingGroups = append(p.ParsingGroups, parsers.GetOsmosisTxParsingGroups()...) + p.ParsingGroups = append(p.ParsingGroups, &OsmosisLpTxGroup{}, &OsmosisConcentratedLiquidityTxGroup{}) } func (p *Parser) GetRows(address string, startDate, endDate *time.Time) ([]parsers.CsvRow, error) { diff --git a/csv/parsers/cointracker/osmosis.go b/csv/parsers/cointracker/osmosis.go index a5614daf..da520a37 100644 --- a/csv/parsers/cointracker/osmosis.go +++ b/csv/parsers/cointracker/osmosis.go @@ -3,10 +3,40 @@ package cointracker import ( "github.com/DefiantLabs/cosmos-tax-cli/csv/parsers" "github.com/DefiantLabs/cosmos-tax-cli/db" + "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/concentratedliquidity" "github.com/DefiantLabs/cosmos-tax-cli/util" ) -func ParseGroup(sf *parsers.WrapperLpTxGroup) error { +type OsmosisLpTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisLpTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisLpTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisLpTxGroup[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisLpTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisLpTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisLpTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +func (sf *OsmosisLpTxGroup) ParseGroup() error { // cbClient := coinbasepro.NewClient() for _, txMessages := range sf.GroupedTxes { for _, message := range txMessages { @@ -74,3 +104,82 @@ func ParseGroup(sf *parsers.WrapperLpTxGroup) error { } return nil } + +type OsmosisConcentratedLiquidityTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisConcentratedLiquidity[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + // Add tx to group using the TX ID as key and appending to array + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +// Concentrated liquidit txs are grouped to be parsed together. Complex analysis may be require later, so group them now for later extension. +func (sf *OsmosisConcentratedLiquidityTxGroup) ParseGroup() error { + txsToFees := parsers.GetTxToFeesMap(sf.GroupedTxes) + for _, txMessages := range sf.GroupedTxes { + for _, message := range txMessages { + + row := Row{} + row.Date = message.Message.Tx.Block.TimeStamp.Format(TimeLayout) + + switch message.Message.MessageType.MessageType { + case concentratedliquidity.MsgCreatePosition: + parseAndAddSentAmountWithDefault(&row, message) + case concentratedliquidity.MsgWithdrawPosition, concentratedliquidity.MsgTransferPositions: + parseAndAddReceivedAmountWithDefault(&row, message) + case concentratedliquidity.MsgAddToPosition: + if message.DenominationReceivedID != nil { + parseAndAddReceivedAmountWithDefault(&row, message) + } else { + parseAndAddSentAmountWithDefault(&row, message) + } + } + + messageFee := txsToFees[message.Message.Tx.ID] + if len(messageFee) > 0 { + fee := messageFee[0] + parseAndAddFeeWithDefault(&row, fee) + + // This fee has been processed, pop it off the stack + txsToFees[message.Message.Tx.ID] = txsToFees[message.Message.Tx.ID][1:] + + } + sf.Rows = append(sf.Rows, row) + } + } + + // If there are any fees left over, add them to the CSV + for _, fees := range txsToFees { + for _, fee := range fees { + row := Row{} + err := row.ParseFee(fee.Tx, fee) + if err != nil { + return err + } + sf.Rows = append(sf.Rows, row) + } + } + return nil +} diff --git a/csv/parsers/cointracker/rows.go b/csv/parsers/cointracker/rows.go index 9d872069..258a3e1d 100644 --- a/csv/parsers/cointracker/rows.go +++ b/csv/parsers/cointracker/rows.go @@ -1,6 +1,7 @@ package cointracker import ( + "errors" "fmt" "github.com/DefiantLabs/cosmos-tax-cli/db" @@ -103,3 +104,60 @@ func (row *Row) ParseFee(tx db.Tx, fee db.Fee) error { return nil } + +func parseAndAddSentAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountSent), event.DenominationSent) + if err != nil { + return errors.New("cannot parse denom units") + } + row.SentAmount = conversionAmount.Text('f', -1) + row.SentCurrency = conversionSymbol + + return nil +} + +func parseAndAddSentAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddSentAmount(row, event) + if err != nil { + row.SentAmount = util.NumericToString(event.AmountSent) + row.SentCurrency = event.DenominationSent.Base + } +} + +func parseAndAddReceivedAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountReceived), event.DenominationReceived) + if err != nil { + return errors.New("cannot parse denom units") + } + row.ReceivedAmount = conversionAmount.Text('f', -1) + row.ReceivedCurrency = conversionSymbol + + return nil +} + +func parseAndAddReceivedAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddReceivedAmount(row, event) + if err != nil { + row.ReceivedAmount = util.NumericToString(event.AmountReceived) + row.ReceivedCurrency = event.DenominationReceived.Base + } +} + +func parseAndAddFee(row *Row, fee db.Fee) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(fee.Amount), fee.Denomination) + if err != nil { + return errors.New("cannot parse denom units") + } + row.FeeAmount = conversionAmount.Text('f', -1) + row.FeeCurrency = conversionSymbol + + return nil +} + +func parseAndAddFeeWithDefault(row *Row, fee db.Fee) { + err := parseAndAddFee(row, fee) + if err != nil { + row.FeeAmount = util.NumericToString(fee.Amount) + row.FeeCurrency = fee.Denomination.Base + } +} diff --git a/csv/parsers/cryptotaxcalculator/cryptotaxcalculator.go b/csv/parsers/cryptotaxcalculator/cryptotaxcalculator.go index 0296211b..c1d3beaa 100644 --- a/csv/parsers/cryptotaxcalculator/cryptotaxcalculator.go +++ b/csv/parsers/cryptotaxcalculator/cryptotaxcalculator.go @@ -55,7 +55,7 @@ func (p *Parser) ProcessTaxableTx(address string, taxableTxs []db.TaxableTransac // Parse all the TXs found in the Parsing Groups for _, txParsingGroup := range p.ParsingGroups { - err := txParsingGroup.ParseGroup(ParseGroup) + err := txParsingGroup.ParseGroup() if err != nil { return err } @@ -63,7 +63,7 @@ func (p *Parser) ProcessTaxableTx(address string, taxableTxs []db.TaxableTransac for _, fee := range feesWithoutTx { row := Row{} - err := row.ParseFee(address, fee) + err := row.ParseFee(fee) if err != nil { return err } @@ -369,7 +369,7 @@ func ParseMsgRecvPacket(address string, event db.TaxableTransaction) (Row, error } func (p *Parser) InitializeParsingGroups() { - p.ParsingGroups = append(p.ParsingGroups, parsers.GetOsmosisTxParsingGroups()...) + p.ParsingGroups = append(p.ParsingGroups, &OsmosisLpTxGroup{}, &OsmosisConcentratedLiquidityTxGroup{}) } func ParseOsmosisReward(event db.TaxableEvent) (Row, error) { diff --git a/csv/parsers/cryptotaxcalculator/osmosis.go b/csv/parsers/cryptotaxcalculator/osmosis.go index b72649eb..b2ab8572 100644 --- a/csv/parsers/cryptotaxcalculator/osmosis.go +++ b/csv/parsers/cryptotaxcalculator/osmosis.go @@ -3,11 +3,41 @@ package cryptotaxcalculator import ( "github.com/DefiantLabs/cosmos-tax-cli/csv/parsers" "github.com/DefiantLabs/cosmos-tax-cli/db" + "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/concentratedliquidity" "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/gamm" "github.com/DefiantLabs/cosmos-tax-cli/util" ) -func ParseGroup(sf *parsers.WrapperLpTxGroup) error { +type OsmosisLpTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisLpTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisLpTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisLpTxGroup[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisLpTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisLpTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisLpTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +func (sf *OsmosisLpTxGroup) ParseGroup() error { for _, txMessages := range sf.GroupedTxes { for _, message := range txMessages { row := Row{} @@ -49,3 +79,86 @@ func ParseGroup(sf *parsers.WrapperLpTxGroup) error { } return nil } + +type OsmosisConcentratedLiquidityTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisConcentratedLiquidity[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + // Add tx to group using the TX ID as key and appending to array + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +// Concentrated liquidit txs are grouped to be parsed together. Complex analysis may be require later, so group them now for later extension. +func (sf *OsmosisConcentratedLiquidityTxGroup) ParseGroup() error { + txsToFees := parsers.GetTxToFeesMap(sf.GroupedTxes) + for _, txMessages := range sf.GroupedTxes { + for _, message := range txMessages { + + row := Row{} + row.Date = message.Message.Tx.Block.TimeStamp.Format(TimeLayout) + row.ID = message.Message.Tx.Hash + switch message.Message.MessageType.MessageType { + case concentratedliquidity.MsgCreatePosition: + parseAndAddSentAmountWithDefault(&row, message) + row.Type = Sell + case concentratedliquidity.MsgWithdrawPosition, concentratedliquidity.MsgTransferPositions: + parseAndAddReceivedAmountWithDefault(&row, message) + row.Type = Buy + case concentratedliquidity.MsgAddToPosition: + if message.DenominationReceivedID != nil { + parseAndAddReceivedAmountWithDefault(&row, message) + row.Type = Buy + } else { + parseAndAddSentAmountWithDefault(&row, message) + row.Type = Sell + } + } + + messageFee := txsToFees[message.Message.Tx.ID] + if len(messageFee) > 0 { + fee := messageFee[0] + parseAndAddFeeWithDefault(&row, fee) + + // This fee has been processed, pop it off the stack + txsToFees[message.Message.Tx.ID] = txsToFees[message.Message.Tx.ID][1:] + + } + sf.Rows = append(sf.Rows, row) + } + } + + // If there are any fees left over, add them to the CSV + for _, fees := range txsToFees { + for _, fee := range fees { + row := Row{} + err := row.ParseFee(fee) + if err != nil { + return err + } + sf.Rows = append(sf.Rows, row) + } + } + return nil +} diff --git a/csv/parsers/cryptotaxcalculator/rows.go b/csv/parsers/cryptotaxcalculator/rows.go index 4b4705c7..5cc0de1a 100644 --- a/csv/parsers/cryptotaxcalculator/rows.go +++ b/csv/parsers/cryptotaxcalculator/rows.go @@ -1,6 +1,7 @@ package cryptotaxcalculator import ( + "errors" "fmt" "github.com/DefiantLabs/cosmos-tax-cli/db" @@ -87,7 +88,7 @@ func (row *Row) ParseBasic(address string, event db.TaxableTransaction) error { return nil } -func (row *Row) ParseFee(address string, fee db.Fee) error { +func (row *Row) ParseFee(fee db.Fee) error { row.Date = fee.Tx.Block.TimeStamp.Format(TimeLayout) row.ID = fee.Tx.Hash row.Type = Fee @@ -138,3 +139,60 @@ func (row *Row) ParseSwap(event db.TaxableTransaction, address, eventType string return nil } + +func parseAndAddSentAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountSent), event.DenominationSent) + if err != nil { + return errors.New("cannot parse denom units") + } + row.QuoteAmount = conversionAmount.Text('f', -1) + row.QuoteCurrency = conversionSymbol + + return nil +} + +func parseAndAddSentAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddSentAmount(row, event) + if err != nil { + row.QuoteAmount = util.NumericToString(event.AmountSent) + row.QuoteCurrency = event.DenominationSent.Base + } +} + +func parseAndAddReceivedAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountReceived), event.DenominationReceived) + if err != nil { + return errors.New("cannot parse denom units") + } + row.BaseAmount = conversionAmount.Text('f', -1) + row.BaseCurrency = conversionSymbol + + return nil +} + +func parseAndAddReceivedAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddReceivedAmount(row, event) + if err != nil { + row.BaseAmount = util.NumericToString(event.AmountReceived) + row.BaseCurrency = event.DenominationReceived.Base + } +} + +func parseAndAddFee(row *Row, fee db.Fee) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(fee.Amount), fee.Denomination) + if err != nil { + return errors.New("cannot parse denom units") + } + row.FeeAmount = conversionAmount.Text('f', -1) + row.FeeCurrency = conversionSymbol + + return nil +} + +func parseAndAddFeeWithDefault(row *Row, fee db.Fee) { + err := parseAndAddFee(row, fee) + if err != nil { + row.FeeAmount = util.NumericToString(fee.Amount) + row.FeeCurrency = fee.Denomination.Base + } +} diff --git a/csv/parsers/koinly/koinly.go b/csv/parsers/koinly/koinly.go index 0fc20c79..005fc259 100644 --- a/csv/parsers/koinly/koinly.go +++ b/csv/parsers/koinly/koinly.go @@ -73,7 +73,7 @@ func (p *Parser) ProcessTaxableTx(address string, taxableTxs []db.TaxableTransac // Parse all the TXs found in the Parsing Groups for _, txParsingGroup := range p.ParsingGroups { - err := txParsingGroup.ParseGroup(ParseGroup) + err := txParsingGroup.ParseGroup() if err != nil { return err } @@ -107,7 +107,7 @@ func (p *Parser) ProcessTaxableEvent(taxableEvents []db.TaxableEvent) error { } func (p *Parser) InitializeParsingGroups() { - p.ParsingGroups = append(p.ParsingGroups, parsers.GetOsmosisTxParsingGroups()...) + p.ParsingGroups = append(p.ParsingGroups, &OsmosisLpTxGroup{}, &OsmosisConcentratedLiquidityTxGroup{}) } func (p *Parser) GetRows(address string, startDate, endDate *time.Time) ([]parsers.CsvRow, error) { diff --git a/csv/parsers/koinly/osmosis.go b/csv/parsers/koinly/osmosis.go index 2f623963..c250edb5 100644 --- a/csv/parsers/koinly/osmosis.go +++ b/csv/parsers/koinly/osmosis.go @@ -7,12 +7,42 @@ import ( "github.com/DefiantLabs/cosmos-tax-cli/config" "github.com/DefiantLabs/cosmos-tax-cli/csv/parsers" "github.com/DefiantLabs/cosmos-tax-cli/db" + "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/concentratedliquidity" "github.com/DefiantLabs/cosmos-tax-cli/util" "github.com/preichenberger/go-coinbasepro/v2" ) -func ParseGroup(sf *parsers.WrapperLpTxGroup) error { +type OsmosisLpTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisLpTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisLpTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisLpTxGroup[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisLpTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisLpTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisLpTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +func (sf *OsmosisLpTxGroup) ParseGroup() error { cbClient := coinbasepro.NewClient() for _, txMessages := range sf.GroupedTxes { for _, message := range txMessages { @@ -78,3 +108,86 @@ func ParseGroup(sf *parsers.WrapperLpTxGroup) error { } return nil } + +type OsmosisConcentratedLiquidityTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisConcentratedLiquidity[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + // Add tx to group using the TX ID as key and appending to array + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +// Concentrated liquidit txs are grouped to be parsed together. Complex analysis may be require later, so group them now for later extension. +func (sf *OsmosisConcentratedLiquidityTxGroup) ParseGroup() error { + txsToFees := parsers.GetTxToFeesMap(sf.GroupedTxes) + for _, txMessages := range sf.GroupedTxes { + for _, message := range txMessages { + + row := Row{} + row.Date = message.Message.Tx.Block.TimeStamp.Format(TimeLayout) + row.TxHash = message.Message.Tx.Hash + switch message.Message.MessageType.MessageType { + case concentratedliquidity.MsgCreatePosition: + parseAndAddSentAmountWithDefault(&row, message) + row.Label = LiquidityIn + case concentratedliquidity.MsgWithdrawPosition, concentratedliquidity.MsgTransferPositions: + parseAndAddReceivedAmountWithDefault(&row, message) + row.Label = LiquidityOut + case concentratedliquidity.MsgAddToPosition: + if message.DenominationReceivedID != nil { + parseAndAddReceivedAmountWithDefault(&row, message) + row.Label = LiquidityIn + } else { + parseAndAddSentAmountWithDefault(&row, message) + row.Label = LiquidityOut + } + } + + messageFee := txsToFees[message.Message.Tx.ID] + if len(messageFee) > 0 { + fee := messageFee[0] + parseAndAddFeeWithDefault(&row, fee) + + // This fee has been processed, pop it off the stack + txsToFees[message.Message.Tx.ID] = txsToFees[message.Message.Tx.ID][1:] + + } + sf.Rows = append(sf.Rows, row) + } + } + + // If there are any fees left over, add them to the CSV + for _, fees := range txsToFees { + for _, fee := range fees { + row := Row{} + err := row.ParseFee(fee.Tx, fee) + if err != nil { + return err + } + sf.Rows = append(sf.Rows, row) + } + } + return nil +} diff --git a/csv/parsers/koinly/rows.go b/csv/parsers/koinly/rows.go index c4c04a53..218ae30d 100644 --- a/csv/parsers/koinly/rows.go +++ b/csv/parsers/koinly/rows.go @@ -1,6 +1,7 @@ package koinly import ( + "errors" "fmt" "github.com/DefiantLabs/cosmos-tax-cli/db" @@ -110,3 +111,60 @@ func (row *Row) ParseFee(tx db.Tx, fee db.Fee) error { return nil } + +func parseAndAddSentAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountSent), event.DenominationSent) + if err != nil { + return fmt.Errorf("cannot parse denom units") + } + row.SentAmount = conversionAmount.Text('f', -1) + row.SentCurrency = conversionSymbol + + return nil +} + +func parseAndAddSentAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddSentAmount(row, event) + if err != nil { + row.SentAmount = util.NumericToString(event.AmountSent) + row.SentCurrency = event.DenominationSent.Base + } +} + +func parseAndAddReceivedAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountReceived), event.DenominationReceived) + if err != nil { + return fmt.Errorf("cannot parse denom units") + } + row.ReceivedAmount = conversionAmount.Text('f', -1) + row.ReceivedCurrency = conversionSymbol + + return nil +} + +func parseAndAddReceivedAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddReceivedAmount(row, event) + if err != nil { + row.ReceivedAmount = util.NumericToString(event.AmountReceived) + row.ReceivedCurrency = event.DenominationReceived.Base + } +} + +func parseAndAddFee(row *Row, fee db.Fee) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(fee.Amount), fee.Denomination) + if err != nil { + return errors.New("cannot parse denom units") + } + row.FeeAmount = conversionAmount.Text('f', -1) + row.FeeCurrency = conversionSymbol + + return nil +} + +func parseAndAddFeeWithDefault(row *Row, fee db.Fee) { + err := parseAndAddFee(row, fee) + if err != nil { + row.FeeAmount = util.NumericToString(fee.Amount) + row.FeeCurrency = fee.Denomination.Base + } +} diff --git a/csv/parsers/osmosis.go b/csv/parsers/osmosis.go index 18ba5cb0..c13e8959 100644 --- a/csv/parsers/osmosis.go +++ b/csv/parsers/osmosis.go @@ -1,7 +1,7 @@ package parsers import ( - "github.com/DefiantLabs/cosmos-tax-cli/db" + "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/concentratedliquidity" "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/gamm" ) @@ -17,6 +17,13 @@ var IsOsmosisExit = map[string]bool{ gamm.MsgExitPool: true, } +var IsOsmosisConcentratedLiquidity = map[string]bool{ + concentratedliquidity.MsgAddToPosition: true, + concentratedliquidity.MsgWithdrawPosition: true, + concentratedliquidity.MsgCreatePosition: true, + concentratedliquidity.MsgTransferPositions: true, +} + // IsOsmosisLpTxGroup is used as a guard for adding messages to the group. var IsOsmosisLpTxGroup = make(map[string]bool) @@ -29,53 +36,3 @@ func init() { IsOsmosisLpTxGroup[messageType] = true } } - -type WrapperLpTxGroup struct { - GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages - Rows []CsvRow -} - -func (sf *WrapperLpTxGroup) ParseGroup(parsingFunc func(sf *WrapperLpTxGroup) error) error { - return parsingFunc(sf) -} - -func (sf *WrapperLpTxGroup) GetRowsForParsingGroup() []CsvRow { - return sf.Rows -} - -func (sf *WrapperLpTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { - _, isInGroup := IsOsmosisLpTxGroup[message.Message.MessageType.MessageType] - return isInGroup -} - -func (sf *WrapperLpTxGroup) String() string { - return "OsmosisLpTxGroup" -} - -func (sf *WrapperLpTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { - return sf.GroupedTxes -} - -func (sf *WrapperLpTxGroup) AddTxToGroup(tx db.TaxableTransaction) { - // Add tx to group using the TX ID as key and appending to array - if _, ok := sf.GroupedTxes[tx.Message.Tx.ID]; ok { - sf.GroupedTxes[tx.Message.Tx.ID] = append(sf.GroupedTxes[tx.Message.Tx.ID], tx) - } else { - var txGrouping []db.TaxableTransaction - txGrouping = append(txGrouping, tx) - sf.GroupedTxes[tx.Message.Tx.ID] = txGrouping - } -} - -func GetOsmosisTxParsingGroups() []ParsingGroup { - var messageGroups []ParsingGroup - - // This appending of parsing groups establishes a precedence - // There is a break statement in the loop doing grouping - // Which means parsers further up the array will be preferred - LpTxGroup := WrapperLpTxGroup{} - LpTxGroup.GroupedTxes = make(map[uint][]db.TaxableTransaction) - messageGroups = append(messageGroups, &LpTxGroup) - - return messageGroups -} diff --git a/csv/parsers/taxbit/osmosis.go b/csv/parsers/taxbit/osmosis.go index 7e5b9874..9fb5f69c 100644 --- a/csv/parsers/taxbit/osmosis.go +++ b/csv/parsers/taxbit/osmosis.go @@ -3,10 +3,40 @@ package taxbit import ( "github.com/DefiantLabs/cosmos-tax-cli/csv/parsers" "github.com/DefiantLabs/cosmos-tax-cli/db" + "github.com/DefiantLabs/cosmos-tax-cli/osmosis/modules/concentratedliquidity" "github.com/DefiantLabs/cosmos-tax-cli/util" ) -func ParseGroup(sf *parsers.WrapperLpTxGroup) error { +type OsmosisLpTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisLpTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisLpTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisLpTxGroup[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisLpTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisLpTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisLpTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +func (sf *OsmosisLpTxGroup) ParseGroup() error { // cbClient := coinbasepro.NewClient() for _, txMessages := range sf.GroupedTxes { for _, message := range txMessages { @@ -74,3 +104,86 @@ func ParseGroup(sf *parsers.WrapperLpTxGroup) error { } return nil } + +type OsmosisConcentratedLiquidityTxGroup struct { + GroupedTxes map[uint][]db.TaxableTransaction // TX db ID to its messages + Rows []parsers.CsvRow +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetRowsForParsingGroup() []parsers.CsvRow { + return sf.Rows +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) BelongsToGroup(message db.TaxableTransaction) bool { + _, isInGroup := parsers.IsOsmosisConcentratedLiquidity[message.Message.MessageType.MessageType] + return isInGroup +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) GetGroupedTxes() map[uint][]db.TaxableTransaction { + return sf.GroupedTxes +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) String() string { + return "OsmosisLpTxGroup" +} + +func (sf *OsmosisConcentratedLiquidityTxGroup) AddTxToGroup(tx db.TaxableTransaction) { + // Add tx to group using the TX ID as key and appending to array + if sf.GroupedTxes == nil { + sf.GroupedTxes = make(map[uint][]db.TaxableTransaction) + } + sf.GroupedTxes = parsers.AddTxToGroupMap(sf.GroupedTxes, tx) +} + +// Concentrated liquidit txs are grouped to be parsed together. Complex analysis may be require later, so group them now for later extension. +func (sf *OsmosisConcentratedLiquidityTxGroup) ParseGroup() error { + txsToFees := parsers.GetTxToFeesMap(sf.GroupedTxes) + for _, txMessages := range sf.GroupedTxes { + for _, message := range txMessages { + + row := Row{} + row.Date = message.Message.Tx.Block.TimeStamp.Format(TimeLayout) + row.TxHash = message.Message.Tx.Hash + switch message.Message.MessageType.MessageType { + case concentratedliquidity.MsgCreatePosition: + parseAndAddSentAmountWithDefault(&row, message) + row.TransactionType = Sale + case concentratedliquidity.MsgWithdrawPosition, concentratedliquidity.MsgTransferPositions: + parseAndAddReceivedAmountWithDefault(&row, message) + row.TransactionType = Buy + case concentratedliquidity.MsgAddToPosition: + if message.DenominationReceivedID != nil { + parseAndAddReceivedAmountWithDefault(&row, message) + row.TransactionType = Buy + } else { + parseAndAddSentAmountWithDefault(&row, message) + row.TransactionType = Sale + } + } + + messageFee := txsToFees[message.Message.Tx.ID] + if len(messageFee) > 0 { + fee := messageFee[0] + parseAndAddFeeWithDefault(&row, fee) + + // This fee has been processed, pop it off the stack + txsToFees[message.Message.Tx.ID] = txsToFees[message.Message.Tx.ID][1:] + + } + sf.Rows = append(sf.Rows, row) + } + } + + // If there are any fees left over, add them to the CSV + for _, fees := range txsToFees { + for _, fee := range fees { + row := Row{} + err := row.ParseFee(fee.Tx, fee) + if err != nil { + return err + } + sf.Rows = append(sf.Rows, row) + } + } + return nil +} diff --git a/csv/parsers/taxbit/rows.go b/csv/parsers/taxbit/rows.go index 72c5d7c7..def70905 100644 --- a/csv/parsers/taxbit/rows.go +++ b/csv/parsers/taxbit/rows.go @@ -1,6 +1,7 @@ package taxbit import ( + "errors" "fmt" "github.com/DefiantLabs/cosmos-tax-cli/db" @@ -120,3 +121,60 @@ func (row *Row) ParseFee(tx db.Tx, fee db.Fee) error { return nil } + +func parseAndAddSentAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountSent), event.DenominationSent) + if err != nil { + return fmt.Errorf("cannot parse denom units") + } + row.SentAmount = conversionAmount.Text('f', -1) + row.SentCurrency = conversionSymbol + + return nil +} + +func parseAndAddSentAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddSentAmount(row, event) + if err != nil { + row.SentAmount = util.NumericToString(event.AmountSent) + row.SentCurrency = event.DenominationSent.Base + } +} + +func parseAndAddReceivedAmount(row *Row, event db.TaxableTransaction) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(event.AmountReceived), event.DenominationReceived) + if err != nil { + return fmt.Errorf("cannot parse denom units") + } + row.ReceivedAmount = conversionAmount.Text('f', -1) + row.ReceivedCurrency = conversionSymbol + + return nil +} + +func parseAndAddReceivedAmountWithDefault(row *Row, event db.TaxableTransaction) { + err := parseAndAddReceivedAmount(row, event) + if err != nil { + row.ReceivedAmount = util.NumericToString(event.AmountReceived) + row.ReceivedCurrency = event.DenominationReceived.Base + } +} + +func parseAndAddFee(row *Row, fee db.Fee) error { + conversionAmount, conversionSymbol, err := db.ConvertUnits(util.FromNumeric(fee.Amount), fee.Denomination) + if err != nil { + return errors.New("cannot parse denom units") + } + row.FeeAmount = conversionAmount.Text('f', -1) + row.FeeCurrency = conversionSymbol + + return nil +} + +func parseAndAddFeeWithDefault(row *Row, fee db.Fee) { + err := parseAndAddFee(row, fee) + if err != nil { + row.FeeAmount = util.NumericToString(fee.Amount) + row.FeeCurrency = fee.Denomination.Base + } +} diff --git a/csv/parsers/taxbit/taxbit.go b/csv/parsers/taxbit/taxbit.go index d460dc72..0ad26e16 100644 --- a/csv/parsers/taxbit/taxbit.go +++ b/csv/parsers/taxbit/taxbit.go @@ -45,7 +45,7 @@ func (p *Parser) ProcessTaxableTx(address string, taxableTxs []db.TaxableTransac // Parse all the TXs found in the Parsing Groups for _, txParsingGroup := range p.ParsingGroups { - err := txParsingGroup.ParseGroup(ParseGroup) + err := txParsingGroup.ParseGroup() if err != nil { return err } @@ -79,7 +79,7 @@ func (p *Parser) ProcessTaxableEvent(taxableEvents []db.TaxableEvent) error { } func (p *Parser) InitializeParsingGroups() { - p.ParsingGroups = append(p.ParsingGroups, parsers.GetOsmosisTxParsingGroups()...) + p.ParsingGroups = append(p.ParsingGroups, &OsmosisLpTxGroup{}, &OsmosisConcentratedLiquidityTxGroup{}) } func (p *Parser) GetRows(address string, startDate, endDate *time.Time) ([]parsers.CsvRow, error) { diff --git a/csv/parsers/types.go b/csv/parsers/types.go index b1191d7a..19e98456 100644 --- a/csv/parsers/types.go +++ b/csv/parsers/types.go @@ -20,7 +20,7 @@ type ParsingGroup interface { String() string AddTxToGroup(db.TaxableTransaction) GetGroupedTxes() map[uint][]db.TaxableTransaction - ParseGroup(parsingFunc func(sf *WrapperLpTxGroup) error) error + ParseGroup() error GetRowsForParsingGroup() []CsvRow } diff --git a/csv/parsers/utils.go b/csv/parsers/utils.go index a9b93649..b9282583 100644 --- a/csv/parsers/utils.go +++ b/csv/parsers/utils.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/DefiantLabs/cosmos-tax-cli/db" "github.com/preichenberger/go-coinbasepro/v2" ) @@ -22,3 +23,31 @@ func GetRate(cbClient *coinbasepro.Client, coin string, transactionTime time.Tim return histRate[0].Close, nil } + +func AddTxToGroupMap(groupedTxs map[uint][]db.TaxableTransaction, tx db.TaxableTransaction) map[uint][]db.TaxableTransaction { + // Add tx to group using the TX ID as key and appending to array + if _, ok := groupedTxs[tx.Message.Tx.ID]; ok { + groupedTxs[tx.Message.Tx.ID] = append(groupedTxs[tx.Message.Tx.ID], tx) + } else { + var txGrouping []db.TaxableTransaction + txGrouping = append(txGrouping, tx) + groupedTxs[tx.Message.Tx.ID] = txGrouping + } + return groupedTxs +} + +func GetTxToFeesMap(groupedTxes map[uint][]db.TaxableTransaction) map[uint][]db.Fee { + txToFees := make(map[uint][]db.Fee) + + for _, txMessages := range groupedTxes { + for _, message := range txMessages { + messageTx := message.Message.Tx + if _, ok := txToFees[messageTx.ID]; !ok { + txToFees[messageTx.ID] = append(txToFees[messageTx.ID], messageTx.Fees...) + break + } + } + } + + return txToFees +} diff --git a/db/search.go b/db/search.go index 352b4bd2..c313ba91 100644 --- a/db/search.go +++ b/db/search.go @@ -14,6 +14,7 @@ func GetTaxableTransactions(address string, db *gorm.DB) ([]TaxableTransaction, Preload("Message.Tx.Block"). Preload("Message.Tx.SignerAddress").Preload("Message.Tx.Fees"). Preload("Message.Tx.Fees.Denomination").Preload("Message.Tx.Fees.PayerAddress"). + Preload("Message.Tx.Fees.Tx").Preload("Message.Tx.Fees.Tx.Block"). Preload("SenderAddress").Preload("ReceiverAddress").Preload("DenominationSent"). Preload("DenominationReceived").Find(&taxableTransactions)