diff --git a/cosmos/modules/tx/logic.go b/cosmos/modules/tx/logic.go index 58d9fee..83b7be3 100644 --- a/cosmos/modules/tx/logic.go +++ b/cosmos/modules/tx/logic.go @@ -78,34 +78,52 @@ func ParseTransferEvent(evt LogMessageEvent) ([]TransferEvent, error) { return nil, errInvalidTransfer } - for i := 0; i < len(evt.Attributes); i++ { - attrRecipient := evt.Attributes[i] - if attrRecipient.Key == "recipient" { - attrSenderIdx := i + 1 - attrAmountIdx := i + 2 - if attrAmountIdx < len(evt.Attributes) { - attrSender := evt.Attributes[attrSenderIdx] - attrAmount := evt.Attributes[attrAmountIdx] - if attrSender.Key == "sender" && attrAmount.Key == EventAttributeAmount { - transfers = append(transfers, TransferEvent{ - Recipient: attrRecipient.Value, - Sender: attrSender.Value, - Amount: attrAmount.Value, - }) - } else { - return nil, errInvalidTransfer - } - } else { - return nil, errInvalidTransfer - } - } else if i%3 == 0 { // every third attr should be "recipient" + if len(evt.Attributes)%3 != 0 { + return nil, errInvalidTransfer + } + + // chunk the attributes into groups of 3 + for i := 0; i < len(evt.Attributes); i += 3 { + transferEvent := TransferEvent{} + err := parseTransferAttributeIntoEvent(evt.Attributes[i], &transferEvent) + if err != nil { + return nil, err + } + err = parseTransferAttributeIntoEvent(evt.Attributes[i+1], &transferEvent) + if err != nil { + return nil, err + } + err = parseTransferAttributeIntoEvent(evt.Attributes[i+2], &transferEvent) + if err != nil { + return nil, err + } + + // validate the transfer event + if transferEvent.Recipient == "" || transferEvent.Sender == "" || transferEvent.Amount == "" { return nil, errInvalidTransfer } + + transfers = append(transfers, transferEvent) } return transfers, nil } +func parseTransferAttributeIntoEvent(attr Attribute, evt *TransferEvent) error { + switch attr.Key { + case "recipient": + evt.Recipient = attr.Value + case "sender": + evt.Sender = attr.Value + case "amount": + evt.Amount = attr.Value + default: + return fmt.Errorf("unknown attribute %s", attr.Key) + } + + return nil +} + // If order is reversed, the last attribute containing the given key will be returned // otherwise the first attribute will be returned func GetValueForAttribute(key string, evt *LogMessageEvent) (string, error) { diff --git a/osmosis/modules/poolmanager/types.go b/osmosis/modules/poolmanager/types.go index 079cde3..69292a0 100644 --- a/osmosis/modules/poolmanager/types.go +++ b/osmosis/modules/poolmanager/types.go @@ -152,42 +152,56 @@ func (sf *WrapperMsgSwapExactAmountIn) HandleMsg(msgType string, msg sdk.Msg, lo transferEvents := txModule.GetEventsWithType("transfer", log) - var transferEvt *txModule.LogMessageEvent - if len(transferEvents) == 0 { - transferEvt = nil - } else { - transferEvt = &transferEvents[len(transferEvents)-1] - } - - if !parsed && transferEvt != nil { - transferEvts, err := txModule.ParseTransferEvent(*transferEvt) - if err != nil { - return err - } + if !parsed && len(transferEvents) > 0 { + + var parserError error + var lastParsedIndex int + // We will attempt to get the last transfer event that executed for the sender + // We are scoping it for now so as not to blast all the way to the beginning but to address + // poolmanager CosmWasm pool executions that seem to send some small amount to a different address right at the end + for i := len(transferEvents) - 1; !parsed && len(transferEvents)-2 >= 0 && i >= len(transferEvents)-2; i-- { + lastParsedIndex = i + transferEvt := &transferEvents[i] + + transferEvts, err := txModule.ParseTransferEvent(*transferEvt) + if err != nil { + parserError = err + continue + } + + // The last transfer event should contain the final transfer to the sender + lastTransferEvt := transferEvts[len(transferEvts)-1] + + if lastTransferEvt.Recipient != sf.Address { + parserError = errors.New("transfer event recipient does not match message sender") + continue + } + + tokenOut, err := sdk.ParseCoinNormalized(lastTransferEvt.Amount) + if err != nil { + parserError = err + continue + } + + // The last route in the hops gives the token out denom and pool ID for the final output + lastRoute := sf.OsmosisMsgSwapExactAmountIn.Routes[len(sf.OsmosisMsgSwapExactAmountIn.Routes)-1] + lastRouteDenom := lastRoute.TokenOutDenom + + if tokenOut.Denom != lastRouteDenom { + parserError = errors.New("final transfer denom does not match last route denom") + continue + } - // The last transfer event should contain the final transfer to the sender - lastTransferEvt := transferEvts[len(transferEvts)-1] - - if lastTransferEvt.Recipient != sf.Address { - return errors.New("transfer event recipient does not match message sender") - } + sf.TokenOut = tokenOut - tokenOut, err := sdk.ParseCoinNormalized(lastTransferEvt.Amount) - if err != nil { - return err + parsed = true + parserError = nil } - // The last route in the hops gives the token out denom and pool ID for the final output - lastRoute := sf.OsmosisMsgSwapExactAmountIn.Routes[len(sf.OsmosisMsgSwapExactAmountIn.Routes)-1] - lastRouteDenom := lastRoute.TokenOutDenom - - if tokenOut.Denom != lastRouteDenom { - return errors.New("final transfer denom does not match last route denom") + if parserError != nil { + return fmt.Errorf("error parsing transfer event. Last processed index (%d): %s", lastParsedIndex, parserError) } - sf.TokenOut = tokenOut - - parsed = true } if !parsed {