Handle transfer-redeemed event from blockchain-watcher (#1039)

This commit is contained in:
ftocal 2024-01-24 14:51:04 -03:00 committed by GitHub
parent 5744e25495
commit 49f9bedd29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 144 additions and 6 deletions

View File

@ -9,6 +9,7 @@ const (
SignedVaaType = "signed-vaa"
LogMessagePublishedType = "log-message-published"
EvmTransactionFoundType = "evm-transaction-found"
TransferRedeemedType = "transfer-redeemed"
EvmTransferRedeemedName = "transfer-redeemed"
)
@ -37,7 +38,7 @@ func NewNotificationEvent[T EventData](trackID, source, _type string, data T) (*
}
type EventData interface {
SignedVaa | LogMessagePublished | EvmTransactionFound
SignedVaa | LogMessagePublished | EvmTransactionFound | TransferRedeemed
}
func GetEventData[T EventData](e *NotificationEvent) (T, error) {
@ -94,3 +95,22 @@ type EvmTransactionFoundAttributes struct {
To string `json:"to"`
Status string `json:"status"`
}
type TransferRedeemed struct {
ChainID int `json:"chainId"`
Emitter string `json:"emitter"`
TxHash string `json:"txHash"`
BlockHeight string `json:"blockHeight"`
BlockTime time.Time `json:"blockTime"`
Attributes TransferRedeemedAttributes `json:"attributes"`
}
type TransferRedeemedAttributes struct {
EmitterChain int `json:"emitterChain"`
EmitterAddress string `json:"emitterAddress"`
Sequence uint64 `json:"sequence"`
Method string `json:"methodsByAddress"`
From string `json:"from"`
To string `json:"to"`
Status string `json:"status"`
}

View File

@ -116,3 +116,62 @@ func Test_GetEvmTransactionFoundPayload(t *testing.T) {
assert.Equal(t, "completed", etf.Attributes.Status)
assert.Equal(t, "transfer-redeemed", etf.Attributes.Name)
}
func Test_GetTransferRedeemedPayload(t *testing.T) {
body := `{
"trackId": "chain-event-0xe210617eb9fdf2e970a3c9a1bc5e4ca2509066f40f0a7a663d3bcae0d60e72a9-8899811",
"source": "blockchain-watcher",
"event": "transfer-redeemed",
"timestamp": "2024-01-11T13:59:35.082Z",
"version": "1",
"data": {
"chainId": 2,
"emitter": "0x4cb69fae7e7af841e44e1a1c30af640739378bb2",
"txHash": "0xe210617eb9fdf2e970a3c9a1bc5e4ca2509066f40f0a7a663d3bcae0d60e72a9",
"blockHeight": "18971205",
"blockTime": "2024-01-09T18:31:23.000Z",
"attributes": {
"from": "0x8afbb9925104c39463d3b502335e3514ec92553e",
"to": "0x4cb69fae7e7af841e44e1a1c30af640739378bb2",
"status": "completed",
"blockNumber": "0x1217a45",
"input": "0x0a55d7350000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000005200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000048701000000030d010e33dc45352e9983cb3a827c5ced0570f909a61e94d6336e4a55c9aa76aa74096ecdeec6e2a686491ae8051c3e804e10686cadb5f6bb88f0a92932507a6c2d6d000219df93c68219df38d6ea5e5fe72910fa1c511bb6332ca1d93bc4fd5aeb043f2c0e616f2e42b8e08ebdfc02928a0f13d70967b200a7af80ad1cffc80934b6a6e601033c6e1450d094fecd575e16536f9c30df997597401b2ad352931cb3bb598766f144fbdefb5ed141e1608e76b1c103bd95d476de60bf5562c5f1cdbc6d154ed329000541b5a745447e7467c46af651c192ebd3823740cd95c32f61de10865777b0f3310cdad20ca89d83d24651c28c9371aa16a1159affe4fd3c9d3c0b2f44565c3f360106c9413d59e5accda81ee95ac3fa88487b350528da94e57103c4ce48e71895cfd5748f005ff52b05e6aef746d56d14b8fb1df737341f75723cc7979b24d196271b01078db25338a7d16038a0da53e5803bcc3d1d23bd85e98fba2a94315aa6595f74fc7bff4238864878ff6c5c19a43ea8878d2a92afa0523a7b0113f2279bc78700bb010a74b542994b5af8d4193c0b7e3b8485a14d7f5b9ad86aa288ed830be6e654661753a6a6172e9645349485df5e7c9649f0c85ef79c448a0fff8e8c4efedfe05864000b9ea722831c60712d72471754678ffbd15b990dd984b2d6bdc3a5f935d7a2cb7416a8f00398204d47a569e2889fae1c7e3a5148b4787713b7012f296ec583514a000c057a930f767bea9a4e1d53a67286fc597d85fe9d3948f9f5982ddd9cb0b38c3f30cc8f54548b659bd74ade4916d40c27e846a79377386a4b3c0ef0646b293a29010d7aaee47c8e8ec4f2f977c8f4023dd3f7e4cd765749382a0a4f162b01fcff8ae7033036c1c99b9407fc940c4a7f345eb75707cad0e399fe46085b076c24c0005e000f44d47ce1be2287aa1a61eb47a540d0a7f2f8c45869bf115a15171d8ecf264ffc32623e9ce9848b8643f84fab8b19421734a7ca911b625d3c96a20e542daa77740110485bac142b1aead1fc29de9e4544b48fe78824e3f9e39e2776e941d3e81977a10529554c64d34cf21dcf03e0047f7613eea0531756d0b63f93c6ea997e56bd5f0012869d9aae8de982b4df0f42c31afb5edea5201a15a38990b39df55a54a0d81d9e6287d1000892a3e6a4c2f1b0fe9c252771472beae8556adde579a7e5499dfc4900659d8bfc0000000000170000000000000000000000002703483b1a5a7c577e8680de9df8be03c6f30e3c000000000000250f0101000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000000000000000000000000000000000000e87547000000000300000000000000000000a9080000000000000000000000004cb69fae7e7af841e44e1a1c30af640739378bb20000000000000000000000004cb69fae7e7af841e44e1a1c30af640739378bb20061010000000000000000000000000000000000000000000000000000000002faf080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030eeff183dce51bd0738e931f6d7b73e232388680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8000000000000000300000000000000000000a90800000000000000000000000019330d10d9cc8751218eaf51e8885d058642e08a000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000aada05bd399372f0b0463744c09113c137636f6a00000000000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000004cb69fae7e7af841e44e1a1c30af640739378bb200000000000000000000000000000000000000000000000000000000e87547000000000000000000000000002703483b1a5a7c577e8680de9df8be03c6f30e3c0000000000000000000000000000000000000000000000000000000000000000000000000000008205e0644ae6febfdd7c92c56070c85acc73a33d9f9e26258e7e39bd407a451af4530004bb163c223e53cb20e1d3e52320982d6f19ca49ff5fefeb1c696a15a9271b5dd3cae4915aa6792037a8aa4b378f4e6e62be358ee8560879d47cbaa823f6df0d3dda6973cfef81781fd5bf8731af4e65a826481914ff07dc0398f61403e3fb1c000000000000000000000000000000000000000000000000000000000000",
"methodsByAddress": "MethodRedeemTokensCCTP",
"timestamp": 1704825083,
"blockHash": "0x144f135187a3a38be44f95d5476683b8de1b32de370f9a79ea3ffbfa1d503d60",
"gas": "0x6ac2d",
"gasPrice": "0x58590ac18",
"maxFeePerGas": "0xac6f82c10",
"maxPriorityFeePerGas": "0x59682f00",
"nonce": "0x74",
"r": "0xdf1c2ece8c0c89b496c86b63b19cadf99241350f592838c0bef5d85cd7a334fe",
"s": "0x18e146e167052b0f3d0c5f2dce6db226775bf6671bb801f4573b98635eef7499",
"transactionIndex": "0x44",
"type": "0x2",
"v": "0x0",
"value": "0x0",
"sequence": 9487,
"emitterAddress": "0000000000000000000000002703483B1A5A7C577E8680DE9DF8BE03C6F30E3C",
"emitterChain": 23
}
}
}
`
event := NotificationEvent{}
err := json.Unmarshal([]byte(body), &event)
assert.NoError(t, err)
assert.Equal(t, "chain-event-0xe210617eb9fdf2e970a3c9a1bc5e4ca2509066f40f0a7a663d3bcae0d60e72a9-8899811", event.TrackID)
assert.Equal(t, "blockchain-watcher", event.Source)
assert.Equal(t, TransferRedeemedType, event.Event)
etf, err := GetEventData[TransferRedeemed](&event)
assert.NoError(t, err)
assert.Equal(t, "0xe210617eb9fdf2e970a3c9a1bc5e4ca2509066f40f0a7a663d3bcae0d60e72a9", etf.TxHash)
assert.Equal(t, 23, etf.Attributes.EmitterChain)
assert.Equal(t, "0000000000000000000000002703483B1A5A7C577E8680DE9DF8BE03C6F30E3C", etf.Attributes.EmitterAddress)
assert.Equal(t, uint64(9487), etf.Attributes.Sequence)
assert.Equal(t, "0x4cb69fae7e7af841e44e1a1c30af640739378bb2", etf.Attributes.To)
assert.Equal(t, "0x8afbb9925104c39463d3b502335e3514ec92553e", etf.Attributes.From)
assert.Equal(t, "MethodRedeemTokensCCTP", etf.Attributes.Method)
assert.Equal(t, "completed", etf.Attributes.Status)
}

View File

@ -8,7 +8,6 @@ import (
"github.com/pkg/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
"github.com/wormhole-foundation/wormhole-explorer/common/domain"
"github.com/wormhole-foundation/wormhole-explorer/common/repository"
"github.com/wormhole-foundation/wormhole-explorer/txtracker/chains"
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.mongodb.org/mongo-driver/bson"
@ -35,6 +34,7 @@ type DestinationTx struct {
type TargetTxUpdate struct {
ID string `bson:"_id"`
Destination *DestinationTx `bson:"destinationTx"`
TrackID string `bson:"-"`
}
// Repository exposes operations over the `globalTransactions` collection.
@ -61,16 +61,34 @@ func NewRepository(logger *zap.Logger, db *mongo.Database) *Repository {
// UpsertOriginTxParams is a struct that contains the parameters for the upsertDocument method.
type UpsertOriginTxParams struct {
VaaId string
TrackID string
ChainId sdk.ChainID
TxDetail *chains.TxDetail
TxStatus domain.SourceTxStatus
Timestamp *time.Time
}
func createChangesDoc(source, _type string, timestamp *time.Time) bson.D {
return bson.D{
{
Key: "changes",
Value: bson.D{
{Key: "type", Value: _type},
{Key: "source", Value: source},
{Key: "timestamp", Value: timestamp},
},
},
}
}
func (r *Repository) UpsertOriginTx(ctx context.Context, params *UpsertOriginTxParams) error {
now := time.Now()
fields := bson.D{
{Key: "chainId", Value: params.ChainId},
{Key: "status", Value: params.TxStatus},
{Key: "updatedAt", Value: now},
}
if params.TxDetail != nil {
@ -94,6 +112,10 @@ func (r *Repository) UpsertOriginTx(ctx context.Context, params *UpsertOriginTxP
},
},
},
{
Key: "$push",
Value: createChangesDoc(params.TrackID, "originTx", &now),
},
}
opts := options.Update().SetUpsert(true)
@ -404,9 +426,8 @@ func (r *Repository) GetVaaIdTxHash(ctx context.Context, id string) (*VaaIdTxHas
func (r *Repository) UpsertTargetTx(ctx context.Context, globalTx *TargetTxUpdate) error {
update := bson.M{
"$set": globalTx,
"$setOnInsert": repository.IndexedAt(time.Now()),
"$inc": bson.D{{Key: "revision", Value: 1}},
"$set": globalTx,
"$push": createChangesDoc(globalTx.TrackID, "destinationTx", globalTx.Destination.UpdatedAt),
}
_, err := r.globalTransactions.UpdateByID(ctx, globalTx.ID, update, options.Update().SetUpsert(true))

View File

@ -108,6 +108,7 @@ func ProcessSourceTx(
// Store source transaction details in the database
p := UpsertOriginTxParams{
VaaId: params.VaaId,
TrackID: params.TrackID,
ChainId: params.ChainId,
Timestamp: params.Timestamp,
TxDetail: txDetail,

View File

@ -40,7 +40,8 @@ func ProcessTargetTx(
now := time.Now()
update := &TargetTxUpdate{
ID: params.VaaId,
ID: params.VaaId,
TrackID: params.TrackID,
Destination: &DestinationTx{
ChainID: params.ChainId,
Status: params.Status,

View File

@ -54,6 +54,7 @@ func (c *Controller) Process(ctx *fiber.Ctx) error {
}
p := &consumer.ProcessSourceTxParams{
TrackID: "controller",
Timestamp: &vaa.Timestamp,
VaaId: vaa.MessageID(),
ChainId: vaa.EmitterChain,

View File

@ -134,6 +134,41 @@ func NewNotificationEvent(log *zap.Logger) ConverterFunc {
return nil, nil
}
return &Event{
TrackID: notification.TrackID,
Type: TargetChainEvent,
ID: vaa.MessageID(),
ChainID: sdk.ChainID(tr.ChainID),
EmitterAddress: tr.Attributes.EmitterAddress,
Sequence: strconv.FormatUint(tr.Attributes.Sequence, 10),
Timestamp: &tr.BlockTime,
TxHash: tr.TxHash,
Attributes: &TargetChainAttributes{
Emitter: tr.Emitter,
BlockHeight: tr.BlockHeight,
TxHash: tr.TxHash,
From: tr.Attributes.From,
To: tr.Attributes.To,
Method: tr.Attributes.Method,
Status: tr.Attributes.Status,
},
}, nil
case events.TransferRedeemedType:
tr, err := events.GetEventData[events.TransferRedeemed](&notification)
if err != nil {
log.Error("Error decoding transferRedeemed from notification event", zap.String("trackId", notification.TrackID), zap.Error(err))
return nil, nil
}
address, err := sdk.StringToAddress(tr.Attributes.EmitterAddress)
if err != nil {
return nil, fmt.Errorf("error converting emitter address [%s]: %w", tr.Attributes.EmitterAddress, err)
}
vaa := sdk.VAA{
EmitterChain: sdk.ChainID(tr.Attributes.EmitterChain),
EmitterAddress: address,
Sequence: tr.Attributes.Sequence,
}
return &Event{
TrackID: notification.TrackID,
Type: TargetChainEvent,