diff --git a/pkg/contracts/ton/gateway_test.go b/pkg/contracts/ton/gateway_test.go new file mode 100644 index 0000000000..5e05bd4ec7 --- /dev/null +++ b/pkg/contracts/ton/gateway_test.go @@ -0,0 +1,54 @@ +package ton + +import ( + "embed" + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +func TestFixtures(t *testing.T) { + // ACT + tx := getFixtureTX(t, "01") + + // ASSERT + require.Equal(t, uint64(26023788000003), tx.Lt) + require.Equal(t, "cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", tx.Hash().Hex()) +} + +//go:embed testdata +var fixtures embed.FS + +// testdata/$name.json tx +func getFixtureTX(t *testing.T, name string) ton.Transaction { + t.Helper() + + filename := fmt.Sprintf("testdata/%s.json", name) + + b, err := fixtures.ReadFile(filename) + require.NoError(t, err, filename) + + // bag of cells + var raw struct { + BOC string `json:"boc"` + } + + require.NoError(t, json.Unmarshal(b, &raw)) + + cells, err := boc.DeserializeBocHex(raw.BOC) + require.NoError(t, err) + require.Len(t, cells, 1) + + cell := cells[0] + + var tx ton.Transaction + + require.NoError(t, tx.UnmarshalTLB(cell, &tlb.Decoder{})) + + return tx +} diff --git a/pkg/contracts/ton/testdata/01.json b/pkg/contracts/ton/testdata/01.json new file mode 100644 index 0000000000..5c05bcc425 --- /dev/null +++ b/pkg/contracts/ton/testdata/01.json @@ -0,0 +1,8 @@ +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c7201020a0100023d0003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017ab22a3b3031b48df020aa3647a59163a25772d81991916a2bf523b771d89deec9e5be15d58000017ab20f8740366ead1e30003465b1d0080102030201e004050082723e346a6461f48fab691e87d9fd5954595eb15351fa1c536486a863d9708b79c313c7e41677dcade29f2b424cdad8712132fbd5465b37df2e1763369e2fb12da0021904222490ee6b28018646dca110080900f168012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d0ee6b28000608235a00002f5645476604cdd5a3c60000003280000000000000006c6d35f934b257cebf76cf01f29a0ae9bd54b022c00101df06015de004cbec44e40ad75610e23fc357071c1c1f7e1e1a31833ac17b131d6a462d0a42d800002f5645476608cdd5a3c6c007008b0000006500000000000000008012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1e876046701b1b4d7e4d2c95f3afddb3c07ca682ba6f552c08b009e42d5ac3d090000000000000000007e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc98510184c2880c0000000000002000000000003da7f47a5d1898330bd18801617ae23a388cbaf527c312921718ef36ce9cf8c4e40901d04", + "description": "A deposit to gw contract on testnet", + "hash": "cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", + "logicalTime": 26023788000003, + "test": false +} diff --git a/pkg/contracts/ton/testdata/readme.md b/pkg/contracts/ton/testdata/readme.md new file mode 100644 index 0000000000..30a59a1639 --- /dev/null +++ b/pkg/contracts/ton/testdata/readme.md @@ -0,0 +1,29 @@ +# TON transaction scraper + +`scraper.go` represents a handy tool that allows to fetch transactions from TON blockchain +for further usage in test cases. + +`go run pkg/contracts/ton/testdata/scraper.go
[--testnet]` + +## Example usage + +```sh +go run pkg/contracts/ton/testdata/scraper.go \ + kQCZfYicgVrqwhxH-Grg44OD78PDRjBnWC9iY61IxaFIW77M \ + 26023788000003 \ + cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf | jq +``` + +Returns + +```json +{ + "account": "0:997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b", + "boc": "b5ee9c7201020a0100023d0003b57997d889c815aeac21c47f86ae0e38383efc3c3463067582f6263ad48c5a1485b000017ab22a3b3031b48df020aa3647a59163a25772d81991916a2bf523b771d89deec9e5be15d58000017ab20f8740366ead1e30003465b1d0080102030201e004050082723e346a6461f48fab691e87d9fd5954595eb15351fa1c536486a863d9708b79c313c7e41677dcade29f2b424cdad8712132fbd5465b37df2e1763369e2fb12da0021904222490ee6b28018646dca110080900f168012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1f00265f62272056bab08711fe1ab838e0e0fbf0f0d18c19d60bd898eb5231685216d0ee6b28000608235a00002f5645476604cdd5a3c60000003280000000000000006c6d35f934b257cebf76cf01f29a0ae9bd54b022c00101df06015de004cbec44e40ad75610e23fc357071c1c1f7e1e1a31833ac17b131d6a462d0a42d800002f5645476608cdd5a3c6c007008b0000006500000000000000008012b298e33d8992beccd0765f63941613bc9483edf610f74991a4cb72d4999ca1e876046701b1b4d7e4d2c95f3afddb3c07ca682ba6f552c08b009e42d5ac3d090000000000000000007e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc98510184c2880c0000000000002000000000003da7f47a5d1898330bd18801617ae23a388cbaf527c312921718ef36ce9cf8c4e40901d04", + "description": "todo", + "hash": "cbd6e2261334d08120e2fef428ecbb4e7773606ced878d0e6da204f2b4bf42bf", + "logicalTime": 26023788000003, + "test": false +} +``` + diff --git a/pkg/contracts/ton/testdata/scraper.go b/pkg/contracts/ton/testdata/scraper.go new file mode 100644 index 0000000000..c9c2705375 --- /dev/null +++ b/pkg/contracts/ton/testdata/scraper.go @@ -0,0 +1,131 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "strconv" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/liteapi" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/ton" +) + +func main() { + var testnet bool + + flag.BoolVar(&testnet, "testnet", false, "Use testnet network") + flag.Parse() + + if len(flag.Args()) != 3 { + log.Fatalf("Usage: go run scrape.go [--testnet]") + } + + // Parse account + acc, err := ton.ParseAccountID(flag.Arg(0)) + must(err, "Unable to parse account") + + // Parse LT + lt, err := strconv.ParseUint(flag.Arg(1), 10, 64) + must(err, "Unable to parse logical time") + + // Parse hash + var hash ton.Bits256 + + must(hash.FromHex(flag.Arg(2)), "Unable to parse hash") + + ctx, client := context.Background(), getClient(testnet) + + state, err := client.GetAccountState(ctx, acc) + must(err, "Unable to get account state") + + if state.Account.Status() != tlb.AccountActive { + fail("account %s is not active", acc.ToRaw()) + } + + txs, err := client.GetTransactions(ctx, 1, acc, lt, hash) + must(err, "Unable to get transactions") + + switch { + case len(txs) == 0: + fail("Not found") + case len(txs) > 1: + fail("invalid tx list length (got %d, want 1); lt %d, hash %s", len(txs), hash.Hex()) + } + + // Print the transaction + tx := txs[0] + + cell, err := transactionToCell(tx) + must(err, "unable to convert tx to cell") + + bocRaw, err := cell.MarshalJSON() + must(err, "unable to marshal cell to JSON") + + printAny(map[string]any{ + "test": testnet, + "account": acc.ToRaw(), + "description": "todo", + "logicalTime": lt, + "hash": hash.Hex(), + "boc": json.RawMessage(bocRaw), + }) +} + +func getClient(testnet bool) *liteapi.Client { + if testnet { + c, err := liteapi.NewClientWithDefaultTestnet() + must(err, "unable to create testnet lite client") + + return c + } + + c, err := liteapi.NewClientWithDefaultTestnet() + must(err, "unable to create mainnet lite client") + + return c +} + +func printAny(v any) { + b, err := json.MarshalIndent(v, "", " ") + must(err, "unable marshal data") + + fmt.Println(string(b)) +} + +func transactionToCell(tx ton.Transaction) (*boc.Cell, error) { + b, err := tx.SourceBoc() + if err != nil { + return nil, err + } + + cells, err := boc.DeserializeBoc(b) + if err != nil { + return nil, err + } + + if len(cells) != 1 { + return nil, fmt.Errorf("invalid cell count: %d", len(cells)) + } + + return cells[0], nil +} + +func must(err error, msg string) { + if err == nil { + return + } + + if msg == "" { + log.Fatalf("Error: %s", err.Error()) + } + + log.Fatalf("%s; error: %s", msg, err.Error()) +} + +func fail(msg string, args ...any) { + must(fmt.Errorf(msg, args...), "FAIL") +}