mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 10:16:15 +00:00
9b397bd45e
- add TON chain, token, RPC, address normalization, and order matching support - add liteclient scanner with masterchain/shard catch-up and Jetton validation - add TON manual payment verification and focused tests
174 lines
6.3 KiB
Go
174 lines
6.3 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"math/big"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/GMWalletApp/epusdt/model/mdb"
|
|
addressutil "github.com/GMWalletApp/epusdt/util/address"
|
|
"github.com/xssnick/tonutils-go/address"
|
|
"github.com/xssnick/tonutils-go/tlb"
|
|
"github.com/xssnick/tonutils-go/ton/jetton"
|
|
"github.com/xssnick/tonutils-go/tvm/cell"
|
|
)
|
|
|
|
func tonTestInboundTx(t *testing.T, src, dst *address.Address, amount tlb.Coins, body *cell.Cell) *tlb.Transaction {
|
|
t.Helper()
|
|
return &tlb.Transaction{
|
|
LT: 123,
|
|
Now: uint32(time.Now().Unix()),
|
|
Hash: bytes.Repeat([]byte{0x11}, 32),
|
|
IO: struct {
|
|
In *tlb.Message `tlb:"maybe ^"`
|
|
Out *tlb.MessagesList `tlb:"maybe ^"`
|
|
}{
|
|
In: &tlb.Message{
|
|
MsgType: tlb.MsgTypeInternal,
|
|
Msg: &tlb.InternalMessage{
|
|
SrcAddr: src,
|
|
DstAddr: dst,
|
|
Amount: amount,
|
|
Body: body,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestParseTonInboundTransferNativeTON(t *testing.T) {
|
|
receive := address.MustParseAddr("EQC6KV4zs8TJtSZapOrRFmqSkxzpq-oSCoxekQRKElf4nC1I")
|
|
sender := address.NewAddress(0, 0, bytes.Repeat([]byte{0x22}, 32)).Bounce(false).Testnet(false)
|
|
native := &mdb.ChainToken{BaseModel: mdb.BaseModel{ID: 1}, Network: mdb.NetworkTon, Symbol: "TON", Decimals: 9, Enabled: true}
|
|
|
|
tx := tonTestInboundTx(t, sender, receive, tlb.MustFromTON("1.23"), cell.BeginCell().EndCell())
|
|
transfer, err := ParseTonInboundTransfer(tx, receive, native, nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseTonInboundTransfer(): %v", err)
|
|
}
|
|
if transfer == nil {
|
|
t.Fatal("expected native TON transfer")
|
|
}
|
|
if transfer.Token.Symbol != "TON" {
|
|
t.Fatalf("token = %q, want TON", transfer.Token.Symbol)
|
|
}
|
|
if transfer.Amount != 1.23 {
|
|
t.Fatalf("amount = %v, want 1.23", transfer.Amount)
|
|
}
|
|
if transfer.ReceiveAddress != addressutil.NormalizeTonAddressObject(receive) {
|
|
t.Fatalf("receive address = %q", transfer.ReceiveAddress)
|
|
}
|
|
if !strings.HasPrefix(transfer.BlockID, "ton:"+addressutil.TonRawAddressObjectKey(receive)+":123:") {
|
|
t.Fatalf("block id = %q, want canonical TON prefix", transfer.BlockID)
|
|
}
|
|
}
|
|
|
|
func TestParseTonInboundTransferJettonNotification(t *testing.T) {
|
|
receive := address.MustParseAddr("EQC6KV4zs8TJtSZapOrRFmqSkxzpq-oSCoxekQRKElf4nC1I")
|
|
sender := address.NewAddress(0, 0, bytes.Repeat([]byte{0x33}, 32)).Bounce(false).Testnet(false)
|
|
jettonWallet := address.NewAddress(0, 0, bytes.Repeat([]byte{0x44}, 32)).Bounce(false).Testnet(false)
|
|
native := &mdb.ChainToken{BaseModel: mdb.BaseModel{ID: 1}, Network: mdb.NetworkTon, Symbol: "TON", Decimals: 9, Enabled: true}
|
|
usdt := mdb.ChainToken{BaseModel: mdb.BaseModel{ID: 2}, Network: mdb.NetworkTon, Symbol: "USDT", Decimals: 6, Enabled: true}
|
|
|
|
body, err := tlb.ToCell(jetton.TransferNotification{
|
|
QueryID: 7,
|
|
Amount: tlb.MustFromNano(big.NewInt(1230000), 6),
|
|
Sender: sender,
|
|
ForwardPayload: cell.BeginCell().EndCell(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("build jetton notification body: %v", err)
|
|
}
|
|
|
|
tx := tonTestInboundTx(t, jettonWallet, receive, tlb.FromNanoTONU(1), body)
|
|
transfer, err := ParseTonInboundTransfer(tx, receive, native, map[string]mdb.ChainToken{
|
|
addressutil.TonRawAddressObjectKey(jettonWallet): usdt,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ParseTonInboundTransfer(): %v", err)
|
|
}
|
|
if transfer == nil {
|
|
t.Fatal("expected USDT jetton transfer")
|
|
}
|
|
if transfer.Token.Symbol != "USDT" {
|
|
t.Fatalf("token = %q, want USDT", transfer.Token.Symbol)
|
|
}
|
|
if transfer.Amount != 1.23 {
|
|
t.Fatalf("amount = %v, want 1.23", transfer.Amount)
|
|
}
|
|
if transfer.SenderAddress != addressutil.NormalizeTonAddressObject(sender) {
|
|
t.Fatalf("sender address = %q", transfer.SenderAddress)
|
|
}
|
|
if transfer.JettonWallet != addressutil.NormalizeTonAddressObject(jettonWallet) {
|
|
t.Fatalf("jetton wallet = %q", transfer.JettonWallet)
|
|
}
|
|
}
|
|
|
|
func TestParseTonInboundTransferJettonNotificationWithNoGasComputeSkip(t *testing.T) {
|
|
receive := address.MustParseAddr("EQC6KV4zs8TJtSZapOrRFmqSkxzpq-oSCoxekQRKElf4nC1I")
|
|
sender := address.NewAddress(0, 0, bytes.Repeat([]byte{0x33}, 32)).Bounce(false).Testnet(false)
|
|
jettonWallet := address.NewAddress(0, 0, bytes.Repeat([]byte{0x44}, 32)).Bounce(false).Testnet(false)
|
|
usdt := mdb.ChainToken{BaseModel: mdb.BaseModel{ID: 2}, Network: mdb.NetworkTon, Symbol: "USDT", Decimals: 6, Enabled: true}
|
|
|
|
body, err := tlb.ToCell(jetton.TransferNotification{
|
|
QueryID: 7,
|
|
Amount: tlb.MustFromNano(big.NewInt(150000), 6),
|
|
Sender: sender,
|
|
ForwardPayload: cell.BeginCell().EndCell(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("build jetton notification body: %v", err)
|
|
}
|
|
|
|
tx := tonTestInboundTx(t, jettonWallet, receive, tlb.FromNanoTONU(1), body)
|
|
tx.Description = tlb.TransactionDescriptionOrdinary{
|
|
ComputePhase: tlb.ComputePhase{Phase: tlb.ComputePhaseSkipped{
|
|
Reason: tlb.ComputeSkipReason{Type: tlb.ComputeSkipReasonNoGas},
|
|
}},
|
|
Aborted: true,
|
|
}
|
|
transfer, err := ParseTonInboundTransfer(tx, receive, nil, map[string]mdb.ChainToken{
|
|
addressutil.TonRawAddressObjectKey(jettonWallet): usdt,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ParseTonInboundTransfer(): %v", err)
|
|
}
|
|
if transfer == nil {
|
|
t.Fatal("expected no-gas jetton notification to be accepted")
|
|
}
|
|
if transfer.Token.Symbol != "USDT" {
|
|
t.Fatalf("token = %q, want USDT", transfer.Token.Symbol)
|
|
}
|
|
if transfer.Amount != 0.15 {
|
|
t.Fatalf("amount = %v, want 0.15", transfer.Amount)
|
|
}
|
|
}
|
|
|
|
func TestParseTonInboundTransferDoesNotCountUntrustedJettonNotificationAsTON(t *testing.T) {
|
|
receive := address.MustParseAddr("EQC6KV4zs8TJtSZapOrRFmqSkxzpq-oSCoxekQRKElf4nC1I")
|
|
sender := address.NewAddress(0, 0, bytes.Repeat([]byte{0x33}, 32)).Bounce(false).Testnet(false)
|
|
untrustedJettonWallet := address.NewAddress(0, 0, bytes.Repeat([]byte{0x55}, 32)).Bounce(false).Testnet(false)
|
|
native := &mdb.ChainToken{BaseModel: mdb.BaseModel{ID: 1}, Network: mdb.NetworkTon, Symbol: "TON", Decimals: 9, Enabled: true}
|
|
|
|
body, err := tlb.ToCell(jetton.TransferNotification{
|
|
QueryID: 7,
|
|
Amount: tlb.MustFromNano(big.NewInt(1230000), 6),
|
|
Sender: sender,
|
|
ForwardPayload: cell.BeginCell().EndCell(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("build jetton notification body: %v", err)
|
|
}
|
|
|
|
tx := tonTestInboundTx(t, untrustedJettonWallet, receive, tlb.FromNanoTONU(1), body)
|
|
transfer, err := ParseTonInboundTransfer(tx, receive, native, nil)
|
|
if err != nil {
|
|
t.Fatalf("ParseTonInboundTransfer(): %v", err)
|
|
}
|
|
if transfer != nil {
|
|
t.Fatalf("untrusted jetton notification produced transfer: %#v", transfer)
|
|
}
|
|
}
|