[API] Standard payload changes (#520)
### Summary This pull request modifies the API service to expose two endpoints for the Wormhole Scan UI: * `GET /api/v1/transactions`: data needed to render the transactions list page. * `GET /api/v1/transactions/{chain}/{id}/{sequence}`: data needed to render the transaction detail page.
This commit is contained in:
parent
4f1987c698
commit
fe196e35f0
216
api/docs/docs.go
216
api/docs/docs.go
|
@ -1077,6 +1077,52 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/transactions/{chain_id}/{emitter}/{seq}": {
|
||||
"get": {
|
||||
"description": "Find VAA metadata by ID.",
|
||||
"tags": [
|
||||
"Wormscan"
|
||||
],
|
||||
"operationId": "get-transaction-by-id",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "id of the blockchain",
|
||||
"name": "chain_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "address of the emitter",
|
||||
"name": "emitter",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "sequence of the VAA",
|
||||
"name": "seq",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/transactions.TransactionDetail"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/vaas/": {
|
||||
"get": {
|
||||
"description": "Returns all VAAs. Output is paginated and can also be be sorted.",
|
||||
|
@ -1734,52 +1780,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"github_com_wormhole-foundation_wormhole-explorer_api_routes_wormscan_transactions.TransactionOverview": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destinationAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"destinationChain": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"emitterAddress": {
|
||||
"description": "EmitterAddress contains the VAA's emitter address, encoded in hex.",
|
||||
"type": "string"
|
||||
},
|
||||
"emitterNativeAddress": {
|
||||
"description": "EmitterNativeAddress contains the VAA's emitter address, encoded in the emitter chain's native format.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"originAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"originChain": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/transactions.TxStatus"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokenAmount": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"usdAmount": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"governor.AvailableNotionalItemResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -2531,17 +2531,83 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"transactions.DestinationTx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blockNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"chainId": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.GlobalTransactionDoc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destinationTx": {
|
||||
"$ref": "#/definitions/transactions.DestinationTx"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"originTx": {
|
||||
"$ref": "#/definitions/transactions.OriginTx"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.ListTransactionsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"transactions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_wormhole-foundation_wormhole-explorer_api_routes_wormscan_transactions.TransactionOverview"
|
||||
"$ref": "#/definitions/transactions.TransactionDetail"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.OriginTx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chainId": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.ScorecardsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -2617,6 +2683,51 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"transactions.TransactionDetail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emitterAddress": {
|
||||
"description": "EmitterAddress contains the VAA's emitter address, encoded in hex.",
|
||||
"type": "string"
|
||||
},
|
||||
"emitterChain": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"emitterNativeAddress": {
|
||||
"description": "EmitterNativeAddress contains the VAA's emitter address, encoded in the emitter chain's native format.",
|
||||
"type": "string"
|
||||
},
|
||||
"globalTx": {
|
||||
"$ref": "#/definitions/transactions.GlobalTransactionDoc"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"standardizedProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokenAmount": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"usdAmount": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.Tx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -2637,17 +2748,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"transactions.TxStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ongoing",
|
||||
"completed"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"TxStatusOngoing",
|
||||
"TxStatusCompleted"
|
||||
]
|
||||
},
|
||||
"vaa.ChainID": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
|
|
|
@ -1070,6 +1070,52 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/transactions/{chain_id}/{emitter}/{seq}": {
|
||||
"get": {
|
||||
"description": "Find VAA metadata by ID.",
|
||||
"tags": [
|
||||
"Wormscan"
|
||||
],
|
||||
"operationId": "get-transaction-by-id",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "id of the blockchain",
|
||||
"name": "chain_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "address of the emitter",
|
||||
"name": "emitter",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "sequence of the VAA",
|
||||
"name": "seq",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/transactions.TransactionDetail"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/vaas/": {
|
||||
"get": {
|
||||
"description": "Returns all VAAs. Output is paginated and can also be be sorted.",
|
||||
|
@ -1727,52 +1773,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"github_com_wormhole-foundation_wormhole-explorer_api_routes_wormscan_transactions.TransactionOverview": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destinationAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"destinationChain": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"emitterAddress": {
|
||||
"description": "EmitterAddress contains the VAA's emitter address, encoded in hex.",
|
||||
"type": "string"
|
||||
},
|
||||
"emitterNativeAddress": {
|
||||
"description": "EmitterNativeAddress contains the VAA's emitter address, encoded in the emitter chain's native format.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"originAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"originChain": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/transactions.TxStatus"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokenAmount": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"usdAmount": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"governor.AvailableNotionalItemResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -2524,17 +2524,83 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"transactions.DestinationTx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blockNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"chainId": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.GlobalTransactionDoc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destinationTx": {
|
||||
"$ref": "#/definitions/transactions.DestinationTx"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"originTx": {
|
||||
"$ref": "#/definitions/transactions.OriginTx"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.ListTransactionsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"transactions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_wormhole-foundation_wormhole-explorer_api_routes_wormscan_transactions.TransactionOverview"
|
||||
"$ref": "#/definitions/transactions.TransactionDetail"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.OriginTx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chainId": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.ScorecardsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -2610,6 +2676,51 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"transactions.TransactionDetail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emitterAddress": {
|
||||
"description": "EmitterAddress contains the VAA's emitter address, encoded in hex.",
|
||||
"type": "string"
|
||||
},
|
||||
"emitterChain": {
|
||||
"$ref": "#/definitions/vaa.ChainID"
|
||||
},
|
||||
"emitterNativeAddress": {
|
||||
"description": "EmitterNativeAddress contains the VAA's emitter address, encoded in the emitter chain's native format.",
|
||||
"type": "string"
|
||||
},
|
||||
"globalTx": {
|
||||
"$ref": "#/definitions/transactions.GlobalTransactionDoc"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"standardizedProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokenAmount": {
|
||||
"type": "string"
|
||||
},
|
||||
"txHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"usdAmount": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transactions.Tx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -2630,17 +2741,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"transactions.TxStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ongoing",
|
||||
"completed"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"TxStatusOngoing",
|
||||
"TxStatusCompleted"
|
||||
]
|
||||
},
|
||||
"vaa.ChainID": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
|
|
|
@ -16,39 +16,6 @@ definitions:
|
|||
index:
|
||||
type: integer
|
||||
type: object
|
||||
github_com_wormhole-foundation_wormhole-explorer_api_routes_wormscan_transactions.TransactionOverview:
|
||||
properties:
|
||||
destinationAddress:
|
||||
type: string
|
||||
destinationChain:
|
||||
$ref: '#/definitions/vaa.ChainID'
|
||||
emitterAddress:
|
||||
description: EmitterAddress contains the VAA's emitter address, encoded in
|
||||
hex.
|
||||
type: string
|
||||
emitterNativeAddress:
|
||||
description: EmitterNativeAddress contains the VAA's emitter address, encoded
|
||||
in the emitter chain's native format.
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
originAddress:
|
||||
type: string
|
||||
originChain:
|
||||
$ref: '#/definitions/vaa.ChainID'
|
||||
status:
|
||||
$ref: '#/definitions/transactions.TxStatus'
|
||||
symbol:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
tokenAmount:
|
||||
type: string
|
||||
txHash:
|
||||
type: string
|
||||
usdAmount:
|
||||
type: string
|
||||
type: object
|
||||
governor.AvailableNotionalItemResponse:
|
||||
properties:
|
||||
bigTransactionSize:
|
||||
|
@ -535,13 +502,56 @@ definitions:
|
|||
volume:
|
||||
type: number
|
||||
type: object
|
||||
transactions.DestinationTx:
|
||||
properties:
|
||||
blockNumber:
|
||||
type: string
|
||||
chainId:
|
||||
$ref: '#/definitions/vaa.ChainID'
|
||||
from:
|
||||
type: string
|
||||
method:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
to:
|
||||
type: string
|
||||
txHash:
|
||||
type: string
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
transactions.GlobalTransactionDoc:
|
||||
properties:
|
||||
destinationTx:
|
||||
$ref: '#/definitions/transactions.DestinationTx'
|
||||
id:
|
||||
type: string
|
||||
originTx:
|
||||
$ref: '#/definitions/transactions.OriginTx'
|
||||
type: object
|
||||
transactions.ListTransactionsResponse:
|
||||
properties:
|
||||
transactions:
|
||||
items:
|
||||
$ref: '#/definitions/github_com_wormhole-foundation_wormhole-explorer_api_routes_wormscan_transactions.TransactionOverview'
|
||||
$ref: '#/definitions/transactions.TransactionDetail'
|
||||
type: array
|
||||
type: object
|
||||
transactions.OriginTx:
|
||||
properties:
|
||||
chainId:
|
||||
$ref: '#/definitions/vaa.ChainID'
|
||||
from:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
txHash:
|
||||
type: string
|
||||
type: object
|
||||
transactions.ScorecardsResponse:
|
||||
properties:
|
||||
24h_messages:
|
||||
|
@ -595,6 +605,39 @@ definitions:
|
|||
time:
|
||||
type: string
|
||||
type: object
|
||||
transactions.TransactionDetail:
|
||||
properties:
|
||||
emitterAddress:
|
||||
description: EmitterAddress contains the VAA's emitter address, encoded in
|
||||
hex.
|
||||
type: string
|
||||
emitterChain:
|
||||
$ref: '#/definitions/vaa.ChainID'
|
||||
emitterNativeAddress:
|
||||
description: EmitterNativeAddress contains the VAA's emitter address, encoded
|
||||
in the emitter chain's native format.
|
||||
type: string
|
||||
globalTx:
|
||||
$ref: '#/definitions/transactions.GlobalTransactionDoc'
|
||||
id:
|
||||
type: string
|
||||
payload:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
standardizedProperties:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
symbol:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
tokenAmount:
|
||||
type: string
|
||||
txHash:
|
||||
type: string
|
||||
usdAmount:
|
||||
type: string
|
||||
type: object
|
||||
transactions.Tx:
|
||||
properties:
|
||||
chain:
|
||||
|
@ -608,14 +651,6 @@ definitions:
|
|||
volume:
|
||||
type: number
|
||||
type: object
|
||||
transactions.TxStatus:
|
||||
enum:
|
||||
- ongoing
|
||||
- completed
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- TxStatusOngoing
|
||||
- TxStatusCompleted
|
||||
vaa.ChainID:
|
||||
enum:
|
||||
- 0
|
||||
|
@ -1455,6 +1490,37 @@ paths:
|
|||
description: Internal Server Error
|
||||
tags:
|
||||
- Wormscan
|
||||
/api/v1/transactions/{chain_id}/{emitter}/{seq}:
|
||||
get:
|
||||
description: Find VAA metadata by ID.
|
||||
operationId: get-transaction-by-id
|
||||
parameters:
|
||||
- description: id of the blockchain
|
||||
in: path
|
||||
name: chain_id
|
||||
required: true
|
||||
type: integer
|
||||
- description: address of the emitter
|
||||
in: path
|
||||
name: emitter
|
||||
required: true
|
||||
type: string
|
||||
- description: sequence of the VAA
|
||||
in: path
|
||||
name: seq
|
||||
required: true
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/transactions.TransactionDetail'
|
||||
"400":
|
||||
description: Bad Request
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
tags:
|
||||
- Wormscan
|
||||
/api/v1/vaas/:
|
||||
get:
|
||||
description: Returns all VAAs. Output is paginated and can also be be sorted.
|
||||
|
|
|
@ -181,22 +181,16 @@ type Token struct {
|
|||
Decimals int64 `json:"decimals"`
|
||||
}
|
||||
|
||||
// TransactionOverview models a brief overview of a transactions (ID, txHash, status, etc.)
|
||||
type TransactionOverview struct {
|
||||
ID string `bson:"_id"`
|
||||
EmitterChain sdk.ChainID `bson:"emitterChain"`
|
||||
EmitterAddr string `bson:"emitterAddr"`
|
||||
TxHash string `bson:"txHash"`
|
||||
Timestamp time.Time `bson:"timestamp"`
|
||||
ToAddress string `bson:"toAddress"`
|
||||
ToChain sdk.ChainID `bson:"toChain"`
|
||||
Symbol string `bson:"symbol"`
|
||||
UsdAmount string `bson:"usdAmount"`
|
||||
TokenAmount string `bson:"tokenAmount"`
|
||||
GlobalTransations []GlobalTransactionDoc `bson:"globalTransactions"`
|
||||
}
|
||||
|
||||
// ListTransactionsInput is used as the output for the function `ListTransactions`
|
||||
type ListTransactonsOutput struct {
|
||||
Transactions []TransactionOverview
|
||||
type TransactionDto struct {
|
||||
ID string `bson:"_id"`
|
||||
EmitterChain sdk.ChainID `bson:"emitterChain"`
|
||||
EmitterAddr string `bson:"emitterAddr"`
|
||||
TxHash string `bson:"txHash"`
|
||||
Timestamp time.Time `bson:"timestamp"`
|
||||
Symbol string `bson:"symbol"`
|
||||
UsdAmount string `bson:"usdAmount"`
|
||||
TokenAmount string `bson:"tokenAmount"`
|
||||
GlobalTransations []GlobalTransactionDoc `bson:"globalTransactions"`
|
||||
Payload map[string]interface{} `bson:"payload"`
|
||||
StandardizedProperties map[string]interface{} `bson:"standardizedProperties"`
|
||||
}
|
||||
|
|
|
@ -726,24 +726,43 @@ func (r *Repository) findGlobalTransactionByID(ctx context.Context, q *GlobalTra
|
|||
return &globalTranstaction, nil
|
||||
}
|
||||
|
||||
// ListTransactions returns a sorted list of transactions.
|
||||
//
|
||||
// Pagination is implemented using a keyset cursor pattern, based on the (timestamp, ID) pair.
|
||||
func (r *Repository) ListTransactions(
|
||||
// FindTransactionsInput is used to pass parameters to the `FindTransactions` method.
|
||||
type FindTransactionsInput struct {
|
||||
// id specifies the VAA ID of the transaction to be found.
|
||||
id string
|
||||
// sort specifies whether the results should be sorted
|
||||
//
|
||||
// If set to true, the results will be sorted by descending timestamp and ID.
|
||||
// If set to false, the results will not be sorted.
|
||||
sort bool
|
||||
pagination *pagination.Pagination
|
||||
}
|
||||
|
||||
// FindTransactions returns transactions matching a specified search criteria.
|
||||
func (r *Repository) FindTransactions(
|
||||
ctx context.Context,
|
||||
pagination *pagination.Pagination,
|
||||
) (*ListTransactonsOutput, error) {
|
||||
input *FindTransactionsInput,
|
||||
) ([]TransactionDto, error) {
|
||||
|
||||
// Build the aggregation pipeline
|
||||
var pipeline mongo.Pipeline
|
||||
{
|
||||
// Specify sorting criteria
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$sort", bson.D{
|
||||
bson.E{"timestamp", -1},
|
||||
bson.E{"_id", -1},
|
||||
}},
|
||||
})
|
||||
if input.sort {
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$sort", bson.D{
|
||||
bson.E{"timestamp", -1},
|
||||
bson.E{"_id", -1},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
// Filter by ID
|
||||
if input.id != "" {
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$match", bson.D{{"_id", input.id}}},
|
||||
})
|
||||
}
|
||||
|
||||
// left outer join on the `transferPrices` collection
|
||||
pipeline = append(pipeline, bson.D{
|
||||
|
@ -789,8 +808,8 @@ func (r *Repository) ListTransactions(
|
|||
pipeline = append(pipeline, bson.D{
|
||||
{"$addFields", bson.D{
|
||||
{"txHash", bson.M{"$arrayElemAt": []interface{}{"$vaaIdTxHash.txHash", 0}}},
|
||||
{"toAddress", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.result.toAddress", 0}}},
|
||||
{"toChain", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.result.toChain", 0}}},
|
||||
{"payload", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.parsedPayload", 0}}},
|
||||
{"standardizedProperties", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.standardizedProperties", 0}}},
|
||||
{"symbol", bson.M{"$arrayElemAt": []interface{}{"$transferPrices.symbol", 0}}},
|
||||
{"usdAmount", bson.M{"$arrayElemAt": []interface{}{"$transferPrices.usdAmount", 0}}},
|
||||
{"tokenAmount", bson.M{"$arrayElemAt": []interface{}{"$transferPrices.tokenAmount", 0}}},
|
||||
|
@ -803,14 +822,18 @@ func (r *Repository) ListTransactions(
|
|||
})
|
||||
|
||||
// Skip initial results
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$skip", pagination.Skip},
|
||||
})
|
||||
if input.pagination != nil {
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$skip", input.pagination.Skip},
|
||||
})
|
||||
}
|
||||
|
||||
// Limit size of results
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$limit", pagination.Limit},
|
||||
})
|
||||
if input.pagination != nil {
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$limit", input.pagination.Limit},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the aggregation pipeline
|
||||
|
@ -821,18 +844,14 @@ func (r *Repository) ListTransactions(
|
|||
}
|
||||
|
||||
// Read results from cursor
|
||||
var documents []TransactionOverview
|
||||
var documents []TransactionDto
|
||||
err = cur.All(ctx, &documents)
|
||||
if err != nil {
|
||||
r.logger.Error("failed to decode cursor", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build result and return
|
||||
response := ListTransactonsOutput{
|
||||
Transactions: documents,
|
||||
}
|
||||
return &response, nil
|
||||
return documents, nil
|
||||
}
|
||||
|
||||
// ListTransactionsByAddress returns a sorted list of transactions for a given address.
|
||||
|
@ -842,14 +861,14 @@ func (r *Repository) ListTransactionsByAddress(
|
|||
ctx context.Context,
|
||||
address *types.Address,
|
||||
pagination *pagination.Pagination,
|
||||
) (*ListTransactonsOutput, error) {
|
||||
) ([]TransactionDto, error) {
|
||||
|
||||
// Build the aggregation pipeline
|
||||
var pipeline mongo.Pipeline
|
||||
{
|
||||
// filter by address
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$match", bson.D{{"result.toAddress", bson.M{"$eq": "0x" + address.Hex()}}}},
|
||||
{"$match", bson.D{{"parsedPayload.toAddress", bson.M{"$eq": "0x" + address.Hex()}}}},
|
||||
})
|
||||
|
||||
// specify sorting criteria
|
||||
|
@ -912,8 +931,8 @@ func (r *Repository) ListTransactionsByAddress(
|
|||
{"$addFields", bson.D{
|
||||
{"txHash", bson.M{"$arrayElemAt": []interface{}{"$vaaIdTxHash.txHash", 0}}},
|
||||
{"timestamp", bson.M{"$arrayElemAt": []interface{}{"$vaas.timestamp", 0}}},
|
||||
{"toAddress", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.result.toAddress", 0}}},
|
||||
{"toChain", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.result.toChain", 0}}},
|
||||
{"payload", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.parsedPayload", 0}}},
|
||||
{"standardizedProperties", bson.M{"$arrayElemAt": []interface{}{"$parsedVaa.standardizedProperties", 0}}},
|
||||
{"symbol", bson.M{"$arrayElemAt": []interface{}{"$transferPrices.symbol", 0}}},
|
||||
{"usdAmount", bson.M{"$arrayElemAt": []interface{}{"$transferPrices.usdAmount", 0}}},
|
||||
{"tokenAmount", bson.M{"$arrayElemAt": []interface{}{"$transferPrices.tokenAmount", 0}}},
|
||||
|
@ -944,16 +963,12 @@ func (r *Repository) ListTransactionsByAddress(
|
|||
}
|
||||
|
||||
// Read results from cursor
|
||||
var documents []TransactionOverview
|
||||
var documents []TransactionDto
|
||||
err = cur.All(ctx, &documents)
|
||||
if err != nil {
|
||||
r.logger.Error("failed to decode cursor", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build result and return
|
||||
response := ListTransactonsOutput{
|
||||
Transactions: documents,
|
||||
}
|
||||
return &response, nil
|
||||
return documents, nil
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/cacheable"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
|
||||
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/types"
|
||||
|
@ -118,16 +119,43 @@ func (s *Service) GetTokenByChainAndAddress(ctx context.Context, chainID vaa.Cha
|
|||
func (s *Service) ListTransactions(
|
||||
ctx context.Context,
|
||||
pagination *pagination.Pagination,
|
||||
) (*ListTransactonsOutput, error) {
|
||||
) ([]TransactionDto, error) {
|
||||
|
||||
return s.repo.ListTransactions(ctx, pagination)
|
||||
input := FindTransactionsInput{
|
||||
sort: true,
|
||||
pagination: pagination,
|
||||
}
|
||||
return s.repo.FindTransactions(ctx, &input)
|
||||
}
|
||||
|
||||
func (s *Service) ListTransactionsByAddress(
|
||||
ctx context.Context,
|
||||
address *types.Address,
|
||||
pagination *pagination.Pagination,
|
||||
) (*ListTransactonsOutput, error) {
|
||||
) ([]TransactionDto, error) {
|
||||
|
||||
return s.repo.ListTransactionsByAddress(ctx, address, pagination)
|
||||
}
|
||||
|
||||
func (s *Service) GetTransactionByID(
|
||||
ctx context.Context,
|
||||
chain vaa.ChainID,
|
||||
emitter *types.Address,
|
||||
seq string,
|
||||
) (*TransactionDto, error) {
|
||||
|
||||
// Execute the database query
|
||||
input := FindTransactionsInput{
|
||||
id: fmt.Sprintf("%d/%s/%s", chain, emitter.Hex(), seq),
|
||||
}
|
||||
output, err := s.repo.FindTransactions(ctx, &input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(output) == 0 {
|
||||
return nil, errors.ErrNotFound
|
||||
}
|
||||
|
||||
// Return matching document
|
||||
return &output[0], nil
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ func RegisterRoutes(
|
|||
api.Get("/top-chain-pairs-by-num-transfers", transactionCtrl.GetTopChainPairs)
|
||||
api.Get("token/:chain/:token_address", transactionCtrl.GetTokenByChainAndAddress)
|
||||
api.Get("/transactions", transactionCtrl.ListTransactions)
|
||||
api.Get("/transactions/:chain/:emitter/:sequence", transactionCtrl.GetTransactionByID)
|
||||
|
||||
// vaas resource
|
||||
vaas := api.Group("/vaas")
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/transactions"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/common/domain"
|
||||
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
||||
|
@ -370,55 +371,55 @@ func (c *Controller) ListTransactions(ctx *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
// Query transactions from the database
|
||||
var queryResult *transactions.ListTransactonsOutput
|
||||
var dtos []transactions.TransactionDto
|
||||
if address != nil {
|
||||
queryResult, err = c.srv.ListTransactionsByAddress(ctx.Context(), address, pagination)
|
||||
dtos, err = c.srv.ListTransactionsByAddress(ctx.Context(), address, pagination)
|
||||
} else {
|
||||
queryResult, err = c.srv.ListTransactions(ctx.Context(), pagination)
|
||||
dtos, err = c.srv.ListTransactions(ctx.Context(), pagination)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Populate the response struct and return
|
||||
response := c.makeTransactionsResponse(queryResult)
|
||||
response := c.makeTransactionsResponse(dtos)
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
func (c *Controller) makeTransactionsResponse(queryResult *transactions.ListTransactonsOutput) ListTransactionsResponse {
|
||||
func (c *Controller) makeTransactionsResponse(dtos []transactions.TransactionDto) ListTransactionsResponse {
|
||||
|
||||
response := ListTransactionsResponse{
|
||||
Transactions: make([]*TransactionOverview, 0, len(queryResult.Transactions)),
|
||||
Transactions: make([]*TransactionDetail, 0, len(dtos)),
|
||||
}
|
||||
|
||||
for i := range queryResult.Transactions {
|
||||
tx := c.makeTransactionOverview(&queryResult.Transactions[i])
|
||||
for i := range dtos {
|
||||
tx := c.makeTransactionDetail(&dtos[i])
|
||||
response.Transactions = append(response.Transactions, tx)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func (c *Controller) makeTransactionOverview(input *transactions.TransactionOverview) *TransactionOverview {
|
||||
func (c *Controller) makeTransactionDetail(input *transactions.TransactionDto) *TransactionDetail {
|
||||
|
||||
tx := TransactionOverview{
|
||||
ID: input.ID,
|
||||
OriginChain: input.EmitterChain,
|
||||
EmitterAddress: input.EmitterAddr,
|
||||
Timestamp: input.Timestamp,
|
||||
DestinationAddress: input.ToAddress,
|
||||
DestinationChain: input.ToChain,
|
||||
Symbol: input.Symbol,
|
||||
TokenAmount: input.TokenAmount,
|
||||
UsdAmount: input.UsdAmount,
|
||||
tx := TransactionDetail{
|
||||
ID: input.ID,
|
||||
EmitterChain: input.EmitterChain,
|
||||
EmitterAddress: input.EmitterAddr,
|
||||
Timestamp: input.Timestamp,
|
||||
Symbol: input.Symbol,
|
||||
TokenAmount: input.TokenAmount,
|
||||
UsdAmount: input.UsdAmount,
|
||||
Payload: input.Payload,
|
||||
StandardizedProperties: input.StandardizedProperties,
|
||||
}
|
||||
|
||||
// Translate the emitter address into the emitter chain's native format
|
||||
var err error
|
||||
tx.EmitterNativeAddress, err = domain.TranslateEmitterAddress(tx.OriginChain, tx.EmitterAddress)
|
||||
tx.EmitterNativeAddress, err = domain.TranslateEmitterAddress(tx.EmitterChain, tx.EmitterAddress)
|
||||
if err != nil {
|
||||
c.logger.Warn("failed to translate emitter address",
|
||||
zap.Stringer("chain", tx.OriginChain),
|
||||
zap.Stringer("chain", tx.EmitterChain),
|
||||
zap.String("address", tx.EmitterAddress),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
@ -429,29 +430,54 @@ func (c *Controller) makeTransactionOverview(input *transactions.TransactionOver
|
|||
if isSolanaOrAptos {
|
||||
// For Solana and Aptos VAAs, the txHash that we get from the gossip network is
|
||||
// not the real transacion hash. We have to overwrite it with the real one.
|
||||
if len(input.GlobalTransations) == 1 &&
|
||||
input.GlobalTransations[0].OriginTx != nil {
|
||||
|
||||
if len(input.GlobalTransations) == 1 && input.GlobalTransations[0].OriginTx != nil {
|
||||
tx.TxHash = input.GlobalTransations[0].OriginTx.TxHash
|
||||
}
|
||||
} else {
|
||||
tx.TxHash = input.TxHash
|
||||
}
|
||||
|
||||
// Set the status based on the outcome of the redeem transaction.
|
||||
if len(input.GlobalTransations) == 1 &&
|
||||
input.GlobalTransations[0].DestinationTx != nil &&
|
||||
input.GlobalTransations[0].DestinationTx.Status == domain.DstTxStatusConfirmed {
|
||||
|
||||
tx.Status = TxStatusCompleted
|
||||
} else {
|
||||
tx.Status = TxStatusOngoing
|
||||
}
|
||||
|
||||
// Set the origin address, if available
|
||||
// Set the global transaction, if available
|
||||
if len(input.GlobalTransations) == 1 && input.GlobalTransations[0].OriginTx != nil {
|
||||
tx.OriginAddress = input.GlobalTransations[0].OriginTx.From
|
||||
tx.GlobalTx = &input.GlobalTransations[0]
|
||||
}
|
||||
|
||||
return &tx
|
||||
}
|
||||
|
||||
// GetTransactionByID godoc
|
||||
// @Description Find VAA metadata by ID.
|
||||
// @Tags Wormscan
|
||||
// @ID get-transaction-by-id
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the VAA"
|
||||
// @Success 200 {object} TransactionDetail
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /api/v1/transactions/{chain_id}/{emitter}/{seq} [get]
|
||||
func (c *Controller) GetTransactionByID(ctx *fiber.Ctx) error {
|
||||
|
||||
// Extract query params
|
||||
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Look up the VAA by ID
|
||||
dto, err := c.srv.GetTransactionByID(
|
||||
ctx.Context(),
|
||||
chainID,
|
||||
emitter,
|
||||
strconv.FormatUint(seq, 10),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dto == nil {
|
||||
return errors.ErrNotFound
|
||||
}
|
||||
|
||||
tx := c.makeTransactionDetail(dto)
|
||||
return ctx.JSON(tx)
|
||||
}
|
||||
|
|
|
@ -3,36 +3,29 @@ package transactions
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/transactions"
|
||||
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
||||
)
|
||||
|
||||
type TxStatus string
|
||||
|
||||
const (
|
||||
TxStatusOngoing TxStatus = "ongoing"
|
||||
TxStatusCompleted TxStatus = "completed"
|
||||
)
|
||||
|
||||
// TransactionOverview is a brief description of a transaction (e.g. ID, txHash, status, etc.).
|
||||
type TransactionOverview struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
TxHash string `json:"txHash,omitempty"`
|
||||
OriginAddress string `json:"originAddress,omitempty"`
|
||||
OriginChain sdk.ChainID `json:"originChain"`
|
||||
// TransactionDetail is a brief description of a transaction (e.g. ID, txHash, payload, etc.)
|
||||
type TransactionDetail struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
TxHash string `json:"txHash,omitempty"`
|
||||
EmitterChain sdk.ChainID `json:"emitterChain"`
|
||||
// EmitterAddress contains the VAA's emitter address, encoded in hex.
|
||||
EmitterAddress string `json:"emitterAddress"`
|
||||
// EmitterNativeAddress contains the VAA's emitter address, encoded in the emitter chain's native format.
|
||||
EmitterNativeAddress string `json:"emitterNativeAddress,omitempty"`
|
||||
DestinationAddress string `json:"destinationAddress,omitempty"`
|
||||
DestinationChain sdk.ChainID `json:"destinationChain,omitempty"`
|
||||
TokenAmount string `json:"tokenAmount,omitempty"`
|
||||
UsdAmount string `json:"usdAmount,omitempty"`
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
Status TxStatus `json:"status"`
|
||||
EmitterNativeAddress string `json:"emitterNativeAddress,omitempty"`
|
||||
TokenAmount string `json:"tokenAmount,omitempty"`
|
||||
UsdAmount string `json:"usdAmount,omitempty"`
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
Payload map[string]interface{} `json:"payload,omitempty"`
|
||||
StandardizedProperties map[string]interface{} `json:"standardizedProperties,omitempty"`
|
||||
GlobalTx *transactions.GlobalTransactionDoc `json:"globalTx,omitempty"`
|
||||
}
|
||||
|
||||
// ListTransactionsResponse is the "200 OK" response model for `GET /api/v1/transactions`.
|
||||
type ListTransactionsResponse struct {
|
||||
Transactions []*TransactionOverview `json:"transactions"`
|
||||
Transactions []*TransactionDetail `json:"transactions"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue