wormhole-explorer/analytics/cmd/token/token.go

153 lines
3.5 KiB
Go

package token
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/wormhole-foundation/wormhole-explorer/common/client/parser"
"github.com/wormhole-foundation/wormhole-explorer/common/domain"
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
)
var ErrUnknownToken = errors.New("token is unknown")
type TransferredToken struct {
AppId string
FromChain sdk.ChainID
ToChain sdk.ChainID
TokenAddress sdk.Address
TokenChain sdk.ChainID
Amount *big.Int
}
func (t *TransferredToken) Clone() *TransferredToken {
if t == nil {
return nil
}
var amount *big.Int
if t.Amount != nil {
amount = new(big.Int).Set(t.Amount)
}
return &TransferredToken{
AppId: t.AppId,
FromChain: t.FromChain,
ToChain: t.ToChain,
TokenAddress: t.TokenAddress,
TokenChain: t.TokenChain,
Amount: amount,
}
}
type GetTransferredTokenByVaa func(context.Context, *sdk.VAA) (*TransferredToken, error)
type TokenResolver struct {
client parser.ParserVAAAPIClient
logger *zap.Logger
}
func NewTokenResolver(client parser.ParserVAAAPIClient, logger *zap.Logger) *TokenResolver {
return &TokenResolver{
client: client,
logger: logger,
}
}
func (r *TokenResolver) GetTransferredTokenByVaa(ctx context.Context, vaa *sdk.VAA) (*TransferredToken, error) {
// Ignore PythNet VAAs
if vaa.EmitterChain == sdk.ChainIDPythNet {
return nil, nil
}
// Decode the VAA payload
payload, err := sdk.DecodeTransferPayloadHdr(vaa.Payload)
if err == nil && payload.OriginChain != sdk.ChainIDUnset {
return &TransferredToken{
AppId: domain.AppIdPortalTokenBridge,
FromChain: vaa.EmitterChain,
ToChain: payload.TargetChain,
TokenAddress: payload.OriginAddress,
TokenChain: payload.OriginChain,
Amount: payload.Amount,
}, nil
}
// Parse the VAA with standarized properties
result, err := r.client.ParseVaaWithStandarizedProperties(vaa)
if err != nil {
r.logger.Error("Parsing vaa with standarized properties",
zap.String("vaaId", vaa.MessageID()),
zap.Error(err))
return nil, err
}
if result == nil {
r.logger.Error("VAA with standarized properties is empty",
zap.String("vaaId", vaa.MessageID()),
zap.Error(err))
return nil, nil
}
token, err := createToken(result.StandardizedProperties, vaa.EmitterChain)
if err != nil {
r.logger.Debug("Creating transferred token",
zap.String("vaaId", vaa.MessageID()),
zap.Error(err))
return nil, ErrUnknownToken
}
return token, err
}
func createToken(s parser.StandardizedProperties, emitterChain sdk.ChainID) (*TransferredToken, error) {
if s.TokenChain.String() == sdk.ChainIDUnset.String() {
return nil, errors.New("tokenChain is unset")
}
if s.ToChain.String() == sdk.ChainIDUnset.String() {
return nil, errors.New("toChain is unset")
}
if emitterChain.String() == sdk.ChainIDUnset.String() {
return nil, errors.New("emitterChain is unset")
}
if s.TokenAddress == "" {
return nil, errors.New("tokenAddress is empty")
}
if s.Amount == "" {
return nil, errors.New("amount is empty")
}
address, err := sdk.StringToAddress(s.TokenAddress)
if err != nil {
return nil, err
}
n := new(big.Int)
n, ok := n.SetString(s.Amount, 10)
if !ok {
return nil, fmt.Errorf("amount [%s] is not a number", s.Amount)
}
appId := domain.AppIdUnkonwn
if len(s.AppIds) > 0 {
appId = s.AppIds[0]
}
return &TransferredToken{
AppId: appId,
FromChain: emitterChain,
ToChain: s.ToChain,
TokenAddress: address,
TokenChain: s.TokenChain,
Amount: n,
}, nil
}