Handle transfer-redeemed event from blockchain-watcher (#1039)
This commit is contained in:
parent
5744e25495
commit
49f9bedd29
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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](¬ification)
|
||||
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,
|
||||
|
|
Loading…
Reference in New Issue