Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jetton operations #563

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/api/event_converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ func (h *Handler) toEvent(ctx context.Context, trace *core.Trace, result *bath.A
}
event.Actions[i] = convertedAction
}
event.IsScam = h.spamFilter.CheckActions(event.Actions, nil, trace.Account)
event.IsScam = h.spamFilter.CheckActions(event.Actions, nil, &trace.Account)
previews := make(map[tongo.AccountID]oas.JettonPreview)
for _, flow := range result.ValueFlow.Accounts {
for jettonMaster := range flow.Jettons {
Expand Down Expand Up @@ -849,7 +849,7 @@ func (h *Handler) toAccountEvent(ctx context.Context, account tongo.AccountID, t
e.Actions = append(e.Actions, convertedAction)
}
if h.spamFilter != nil {
e.IsScam = h.spamFilter.CheckActions(e.Actions, &account, trace.Account)
e.IsScam = h.spamFilter.CheckActions(e.Actions, &account, &trace.Account)
}
if len(e.Actions) == 0 {
e.Actions = []oas.Action{
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ type storage interface {
GetJettonHolders(ctx context.Context, jettonMaster tongo.AccountID, limit, offset int) ([]core.JettonHolder, error)
GetJettonMasterMetadata(ctx context.Context, master tongo.AccountID) (tongo.JettonMetadata, error)
GetJettonMasterData(ctx context.Context, master tongo.AccountID) (core.JettonMaster, error)
GetAccountJettonsHistory(ctx context.Context, address tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) ([]tongo.Bits256, error)
GetAccountJettonHistoryByID(ctx context.Context, address, jettonMaster tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) ([]tongo.Bits256, error)
GetAccountJettonsHistory(ctx context.Context, address tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) (map[core.TraceID][]core.JettonOperation, error)
GetAccountJettonHistoryByID(ctx context.Context, address, jettonMaster tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) (map[core.TraceID][]core.JettonOperation, error)
GetJettonTransferPayload(ctx context.Context, accountID, jettonMaster ton.AccountID) (*core.JettonTransferPayload, error)

GetAllAuctions(ctx context.Context) ([]core.Auction, error)
Expand Down Expand Up @@ -179,7 +179,7 @@ type ratesSource interface {
}

type SpamFilter interface {
CheckActions(actions []oas.Action, viewer *ton.AccountID, initiator ton.AccountID) bool
CheckActions(actions []oas.Action, viewer *ton.AccountID, initiator *ton.AccountID) bool
JettonTrust(address tongo.AccountID, symbol, name, image string) core.TrustType
NftTrust(address tongo.AccountID, collection *ton.AccountID, description, image string) core.TrustType
}
Expand Down
102 changes: 65 additions & 37 deletions pkg/api/jetton_converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package api

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/tonkeeper/tongo/abi"
"github.com/tonkeeper/tongo/tlb"
"net/http"
"sort"
"strings"

"github.com/tonkeeper/opentonapi/pkg/bath"
Expand Down Expand Up @@ -51,59 +55,83 @@ func jettonMetadata(account ton.AccountID, meta NormalizedMetadata) oas.JettonMe
return metadata
}

func (h *Handler) convertJettonHistory(ctx context.Context, account ton.AccountID, master *ton.AccountID, traceIDs []ton.Bits256, acceptLanguage oas.OptString) ([]oas.AccountEvent, int64, error) {
func (h *Handler) convertJettonHistory(ctx context.Context, account ton.AccountID, master *ton.AccountID, history map[core.TraceID][]core.JettonOperation, acceptLanguage oas.OptString) ([]oas.AccountEvent, int64, error) {
var lastLT uint64
events := make([]oas.AccountEvent, 0, len(traceIDs))
for _, traceID := range traceIDs {
trace, err := h.storage.GetTrace(ctx, traceID)
if err != nil {
if errors.Is(err, core.ErrTraceIsTooLong) {
// we ignore this for now, because we believe that this case is extremely rare.
continue
}
return nil, 0, err
}
result, err := bath.FindActions(ctx, trace,
bath.WithStraws(bath.JettonTransfersBurnsMints),
bath.WithInformationSource(h.storage))
if err != nil {
return nil, 0, err
}
var events []oas.AccountEvent

for id, ops := range history {
event := oas.AccountEvent{
EventID: trace.Hash.Hex(),
Account: convertAccountAddress(account, h.addressBook),
Timestamp: trace.Utime,
IsScam: false,
Lt: int64(trace.Lt),
InProgress: trace.InProgress(),
Extra: result.Extra(account),
EventID: id.Hash.Hex(),
Account: convertAccountAddress(account, h.addressBook),
Timestamp: id.UTime, // TODO: or first/last op Utime
IsScam: false,
Lt: int64(id.Lt), // TODO: or first/last op Lt
Extra: 0,
}
for _, action := range result.Actions {
if action.Type != bath.JettonTransfer && action.Type != bath.JettonBurn && action.Type != bath.JettonMint {
continue
}
if master != nil && ((action.JettonTransfer != nil && action.JettonTransfer.Jetton != *master) ||
(action.JettonMint != nil && action.JettonMint.Jetton != *master) ||
(action.JettonBurn != nil && action.JettonBurn.Jetton != *master)) {
continue
}
if !action.IsSubject(account) {
for _, op := range ops {
var action bath.Action
switch op.Operation {
case core.TransferJettonOperation:
transferAction := bath.JettonTransferAction{
Jetton: op.JettonMaster,
Recipient: op.Destination,
Sender: op.Source,
Amount: tlb.VarUInteger16(*op.Amount.BigInt()),
}
action.Type = "JettonTransfer"
action.JettonTransfer = &transferAction
var payload abi.JettonPayload
err := json.Unmarshal([]byte(op.ForwardPayload), &payload)
if err != nil {
break
}
switch p := payload.Value.(type) {
case abi.TextCommentJettonPayload:
comment := string(p.Text)
action.JettonTransfer.Comment = &comment
case abi.EncryptedTextCommentJettonPayload:
action.JettonTransfer.EncryptedComment = &bath.EncryptedComment{EncryptionType: "simple", CipherText: p.CipherText}
}
case core.MintJettonOperation:
mintAction := bath.JettonMintAction{
Jetton: op.JettonMaster,
Recipient: *op.Destination,
Amount: tlb.VarUInteger16(*op.Amount.BigInt()),
}
action.Type = "JettonMint"
action.JettonMint = &mintAction
case core.BurnJettonOperation:
burnAction := bath.JettonBurnAction{
Jetton: op.JettonMaster,
Sender: *op.Source,
Amount: tlb.VarUInteger16(*op.Amount.BigInt()),
}
action.Type = "JettonTransfer"
action.JettonBurn = &burnAction
default:
continue
}
convertedAction, err := h.convertAction(ctx, &account, action, acceptLanguage)
if err != nil {
return nil, 0, err
}
event.Actions = append(event.Actions, convertedAction)
if lastLT == 0 {
lastLT = op.Lt
}
if op.Lt < lastLT {
lastLT = op.Lt
}
}
event.IsScam = h.spamFilter.CheckActions(event.Actions, &account, trace.Account)
if len(event.Actions) == 0 {
continue
}
event.IsScam = h.spamFilter.CheckActions(event.Actions, &account, nil)
events = append(events, event)
lastLT = trace.Lt
}

sort.Slice(events, func(i, j int) bool {
return events[i].Lt > events[j].Lt
})
return events, int64(lastLT), nil
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/api/jetton_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ func (h *Handler) GetAccountJettonsHistory(ctx context.Context, params oas.GetAc
if err != nil {
return nil, toError(http.StatusBadRequest, err)
}
traceIDs, err := h.storage.GetAccountJettonsHistory(ctx, account.ID, params.Limit, optIntToPointer(params.BeforeLt), optIntToPointer(params.StartDate), optIntToPointer(params.EndDate))
history, err := h.storage.GetAccountJettonsHistory(ctx, account.ID, params.Limit, optIntToPointer(params.BeforeLt), optIntToPointer(params.StartDate), optIntToPointer(params.EndDate))
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
events, lastLT, err := h.convertJettonHistory(ctx, account.ID, nil, traceIDs, params.AcceptLanguage)
events, lastLT, err := h.convertJettonHistory(ctx, account.ID, nil, history, params.AcceptLanguage)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
Expand All @@ -120,14 +120,14 @@ func (h *Handler) GetAccountJettonHistoryByID(ctx context.Context, params oas.Ge
if err != nil {
return nil, toError(http.StatusBadRequest, err)
}
traceIDs, err := h.storage.GetAccountJettonHistoryByID(ctx, account.ID, jettonMasterAccount.ID, params.Limit, optIntToPointer(params.BeforeLt), optIntToPointer(params.StartDate), optIntToPointer(params.EndDate))
history, err := h.storage.GetAccountJettonHistoryByID(ctx, account.ID, jettonMasterAccount.ID, params.Limit, optIntToPointer(params.BeforeLt), optIntToPointer(params.StartDate), optIntToPointer(params.EndDate))
if errors.Is(err, core.ErrEntityNotFound) {
return &oas.AccountEvents{}, nil
}
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
events, lastLT, err := h.convertJettonHistory(ctx, account.ID, &jettonMasterAccount.ID, traceIDs, params.AcceptLanguage)
events, lastLT, err := h.convertJettonHistory(ctx, account.ID, &jettonMasterAccount.ID, history, params.AcceptLanguage)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/nft_converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (h *Handler) convertNftHistory(ctx context.Context, account tongo.AccountID
}
event.Actions = append(event.Actions, convertedAction)
}
event.IsScam = h.spamFilter.CheckActions(event.Actions, &account, trace.Account)
event.IsScam = h.spamFilter.CheckActions(event.Actions, &account, &trace.Account)
if len(event.Actions) > 0 {
events = append(events, event)
lastLT = trace.Lt
Expand Down
23 changes: 23 additions & 0 deletions pkg/core/jetton.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,26 @@ type JettonWalletLockData struct {
FullBalance decimal.Decimal
UnlockTime int64
}

type JettonOperationType = string

const (
TransferJettonOperation JettonOperationType = "transfer"
MintJettonOperation JettonOperationType = "mint"
BurnJettonOperation JettonOperationType = "burn"
UnknownJettonOperation JettonOperationType = "unknown"
)

type JettonOperation struct {
Operation JettonOperationType
Source *tongo.AccountID
Destination *tongo.AccountID
JettonMaster tongo.AccountID
TraceID TraceID
DestEndBalance decimal.Decimal
Amount decimal.Decimal
QueryID uint64
ForwardPayload string
Lt uint64
Utime int64
}
4 changes: 2 additions & 2 deletions pkg/litestorage/jetton.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ func (s *LiteStorage) GetJettonMasterData(ctx context.Context, master tongo.Acco
return jettonMaster, nil
}

func (s *LiteStorage) GetAccountJettonsHistory(ctx context.Context, address tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) ([]tongo.Bits256, error) {
func (s *LiteStorage) GetAccountJettonsHistory(ctx context.Context, address tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) (map[core.TraceID][]core.JettonOperation, error) {
return nil, nil
}

func (s *LiteStorage) GetAccountJettonHistoryByID(ctx context.Context, address, jettonMaster tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) ([]tongo.Bits256, error) {
func (s *LiteStorage) GetAccountJettonHistoryByID(ctx context.Context, address, jettonMaster tongo.AccountID, limit int, beforeLT, startTime, endTime *int64) (map[core.TraceID][]core.JettonOperation, error) {
return nil, nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/spam/spam.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func NewSpamFilter() *Filter {
}
}

func (f Filter) CheckActions(actions []oas.Action, viewer *ton.AccountID, initiator ton.AccountID) bool {
func (f Filter) CheckActions(actions []oas.Action, viewer *ton.AccountID, initiator *ton.AccountID) bool {
var comment string
for _, action := range actions {
switch {
Expand Down
Loading