From 98d6edeb0844fad7383270d8d8e80f31eb2e7286 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Fri, 12 Jul 2024 12:31:35 +0400 Subject: [PATCH] Jetton accept example & address Equals func --- address/addr.go | 5 +++++ example/accept-payments/main.go | 39 ++++++++++++++++++++++++++++----- tlb/transaction.go | 10 ++++++--- ton/jetton/jetton.go | 11 ++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/address/addr.go b/address/addr.go index 5822628a..fd661377 100644 --- a/address/addr.go +++ b/address/addr.go @@ -1,6 +1,7 @@ package address import ( + "bytes" "encoding/base64" "encoding/binary" "encoding/hex" @@ -337,3 +338,7 @@ func (a *Address) Workchain() int32 { func (a *Address) Data() []byte { return a.data } + +func (a *Address) Equals(b *Address) bool { + return a.workchain == b.workchain && bytes.Equal(a.data, b.data) +} diff --git a/example/accept-payments/main.go b/example/accept-payments/main.go index 97765f25..c9620276 100644 --- a/example/accept-payments/main.go +++ b/example/accept-payments/main.go @@ -6,6 +6,7 @@ import ( "github.com/xssnick/tonutils-go/liteclient" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" + "github.com/xssnick/tonutils-go/ton/jetton" "log" ) @@ -26,19 +27,17 @@ func main() { } // initialize ton api lite connection wrapper with full proof checks - api := ton.NewAPIClient(client, ton.ProofCheckPolicySecure).WithRetry() + api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry() api.SetTrustedBlockFromConfig(cfg) - log.Println("fetching and checking proofs since config init block, it may take near a minute...") master, err := api.CurrentMasterchainInfo(context.Background()) // we fetch block just to trigger chain proof check if err != nil { log.Fatalln("get masterchain info err: ", err.Error()) return } - log.Println("master proof checks are completed successfully, now communication is 100% safe!") // address on which we are accepting payments - treasuryAddress := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") + treasuryAddress := address.MustParseAddr("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO") acc, err := api.GetAccount(context.Background(), master, treasuryAddress) if err != nil { @@ -58,10 +57,38 @@ func main() { log.Println("waiting for transfers...") + // USDT master contract addr, but can be any jetton + usdt := jetton.NewJettonMasterClient(api, address.MustParseAddr("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs")) + // get our jetton wallet address + treasuryJettonWallet, err := usdt.GetJettonWalletAtBlock(context.Background(), treasuryAddress, master) + if err != nil { + log.Fatalln("get jetton wallet address err: ", err.Error()) + return + } + // listen for new transactions from channel for tx := range transactions { - // process transaction here - log.Println(tx.String()) + // only internal messages can increase the balance + if tx.IO.In != nil && tx.IO.In.MsgType == tlb.MsgTypeInternal { + ti := tx.IO.In.AsInternal() + src := ti.SrcAddr + + // verify that sender is our jetton wallet + if ti.SrcAddr.Equals(treasuryJettonWallet.Address()) { + var transfer jetton.TransferNotification + if err = tlb.LoadFromCell(&transfer, ti.Body.BeginParse()); err == nil { + // convert decimals to 6 for USDT (it can be fetched from jetton details too), default is 9 + amt := tlb.MustFromNano(transfer.Amount.Nano(), 6) + + // reassign sender to real jetton sender instead of its jetton wallet contract + src = transfer.Sender + log.Println("received", amt.String(), "USDT from", src.String()) + } + } + + // show received ton amount + log.Println("received", ti.Amount.String(), "TON from", src.String()) + } // update last processed lt and save it in db lastProcessedLT = tx.LT diff --git a/tlb/transaction.go b/tlb/transaction.go index 974aa9a1..76b9da44 100644 --- a/tlb/transaction.go +++ b/tlb/transaction.go @@ -1,6 +1,7 @@ package tlb import ( + "encoding/hex" "fmt" "math/big" "reflect" @@ -290,17 +291,20 @@ func (t *Transaction) String() string { case TransactionDescriptionOrdinary: } if t.IO.In != nil { + build += fmt.Sprintf("LT: %d", t.LT) + if t.IO.In.MsgType == MsgTypeInternal { in = t.IO.In.AsInternal().Amount.Nano() - } - if in.Cmp(big.NewInt(0)) != 0 { intTx := t.IO.In.AsInternal() - build += fmt.Sprintf("LT: %d, In: %s TON, From %s", t.LT, FromNanoTON(in).String(), intTx.SrcAddr) + build += fmt.Sprintf(", In: %s TON, From %s", FromNanoTON(in).String(), intTx.SrcAddr) comment := intTx.Comment() if comment != "" { build += ", Comment: " + comment } + } else if t.IO.In.MsgType == MsgTypeExternalIn { + exTx := t.IO.In.AsExternalIn() + build += ", ExternalIn, hash: " + hex.EncodeToString(exTx.Body.Hash()) } } diff --git a/ton/jetton/jetton.go b/ton/jetton/jetton.go index fa51471a..554d5f93 100644 --- a/ton/jetton/jetton.go +++ b/ton/jetton/jetton.go @@ -2,6 +2,7 @@ package jetton import ( "context" + "errors" "fmt" "math/big" @@ -19,6 +20,8 @@ type TonApi interface { SubscribeOnTransactions(workerCtx context.Context, addr *address.Address, lastProcessedLT uint64, channel chan<- *tlb.Transaction) } +var ErrInvalidTransfer = errors.New("transfer is not verified") + type MintPayloadMasterMsg struct { Opcode uint32 `tlb:"## 32"` QueryID uint64 `tlb:"## 64"` @@ -34,6 +37,14 @@ type MintPayload struct { MasterMsg MintPayloadMasterMsg `tlb:"^"` } +type TransferNotification struct { + _ tlb.Magic `tlb:"#7362d09c"` + QueryID uint64 `tlb:"## 64"` + Amount tlb.Coins `tlb:"."` + Sender *address.Address `tlb:"addr"` + ForwardPayload *cell.Cell `tlb:"either . ^"` +} + type Data struct { TotalSupply *big.Int Mintable bool