├── .gitignore ├── Golang ├── Chapter 2 │ └── External and Internal transactions.go ├── Chapter 3 │ └── Deploying our wallet.go ├── Chapter 4 │ ├── Contract deploy via wallet.go │ ├── Get methods in Wallet V3 and Wallet v4.go │ ├── NFT Transfer.go │ └── Sending multiple messages simultaneously.go ├── Chapter 5 │ ├── Deploying high-load wallet.go │ └── Sending transactions from high-load wallet.go ├── go.mod ├── go.sum └── main.go ├── JavaScript - TypeScript ├── nodemon.json ├── package-lock.json ├── package.json ├── src │ ├── Chapter 2 │ │ └── External and Internal transactions.ts │ ├── Chapter 3 │ │ └── Deploying our wallet.ts │ ├── Chapter 4 │ │ ├── Contract deploy via wallet.ts │ │ ├── Get methods in Wallet V3 and Wallet v4.ts │ │ ├── NFT Transfer.ts │ │ └── Sending multiple messages simultaneously.ts │ ├── Chapter 5 │ │ ├── Deploying high-load wallet.ts │ │ └── Sending transactions from high-load wallet.ts │ ├── Chapter 6 │ │ ├── Deploying high-load wallet.ts │ │ ├── Sending transactions from high-load wallet.ts │ │ └── wrappers │ │ │ ├── HighloadQueryId.ts │ │ │ ├── HighloadWalletV3.ts │ │ │ └── const.ts │ ├── highload_wallet.fc │ ├── index.ts │ ├── stdlib.fc │ └── wallet_v3.fc ├── tsconfig.json └── tsconfigs.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /Golang/Chapter 2/External and Internal transactions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "encoding/base64" 9 | "log" 10 | "strings" 11 | "time" 12 | 13 | "github.com/xssnick/tonutils-go/address" 14 | "github.com/xssnick/tonutils-go/liteclient" 15 | "github.com/xssnick/tonutils-go/tl" 16 | "github.com/xssnick/tonutils-go/tlb" 17 | "github.com/xssnick/tonutils-go/ton" 18 | "github.com/xssnick/tonutils-go/tvm/cell" 19 | "golang.org/x/crypto/pbkdf2" 20 | ) 21 | 22 | func main() { 23 | internalMessageBody := cell.BeginCell(). 24 | MustStoreUInt(0, 32). // write 32 zero bits to indicate that a text comment will follow 25 | MustStoreStringSnake("Hello, TON!"). // write our text comment 26 | EndCell() 27 | 28 | walletAddress := address.MustParseAddr("put your address") 29 | 30 | internalMessage := cell.BeginCell(). 31 | MustStoreUInt(0, 1). // indicate that it is an internal message -> int_msg_info$0 32 | MustStoreBoolBit(true). // IHR Disabled 33 | MustStoreBoolBit(true). // bounce 34 | MustStoreBoolBit(false). // bounced 35 | MustStoreUInt(0, 2). // src -> addr_none 36 | MustStoreAddr(walletAddress). 37 | MustStoreCoins(tlb.MustFromTON("0.2").NanoTON().Uint64()). // amount 38 | MustStoreBoolBit(false). // Extra currency 39 | MustStoreCoins(0). // IHR Fee 40 | MustStoreCoins(0). // Forwarding Fee 41 | MustStoreUInt(0, 64). // Logical time of creation 42 | MustStoreUInt(0, 32). // UNIX time of creation 43 | MustStoreBoolBit(false). // No State Init 44 | MustStoreBoolBit(true). // We store Message Body as a reference 45 | MustStoreRef(internalMessageBody). // Store Message Body as a reference 46 | EndCell() 47 | 48 | mnemonic := strings.Split("put your mnemonic", " ") // get our mnemonic as array 49 | 50 | connection := liteclient.NewConnectionPool() 51 | configUrl := "https://ton-blockchain.github.io/global.config.json" 52 | err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 53 | if err != nil { 54 | panic(err) 55 | } 56 | client := ton.NewAPIClient(connection) // create client 57 | 58 | block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer 59 | if err != nil { 60 | log.Fatalln("CurrentMasterchainInfo err:", err.Error()) 61 | return 62 | } 63 | 64 | getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract 65 | if err != nil { 66 | log.Fatalln("RunGetMethod err:", err.Error()) 67 | return 68 | } 69 | seqno := getMethodResult.MustInt(0) // get seqno from response 70 | 71 | // The next three lines will extract the private key using the mnemonic phrase. We will not go into cryptographic details. With the tonutils-go library, this is all implemented, but we’re doing it again to get a full understanding. 72 | mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) 73 | hash := mac.Sum(nil) 74 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 75 | 76 | privateKey := ed25519.NewKeyFromSeed(k) 77 | 78 | toSign := cell.BeginCell(). 79 | MustStoreUInt(698983191, 32). // subwallet_id | We consider this further 80 | MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // Message expiration time, +60 = 1 minute 81 | MustStoreUInt(seqno.Uint64(), 32). // store seqno 82 | MustStoreUInt(uint64(3), 8). // store mode of our internal message 83 | MustStoreRef(internalMessage) // store our internalMessage as a reference 84 | 85 | signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature 86 | 87 | body := cell.BeginCell(). 88 | MustStoreSlice(signature, 512). // store signature 89 | MustStoreBuilder(toSign). // store our message 90 | EndCell() 91 | 92 | externalMessage := cell.BeginCell(). 93 | MustStoreUInt(0b10, 2). // 0b10 -> 10 in binary 94 | MustStoreUInt(0, 2). // src -> addr_none 95 | MustStoreAddr(walletAddress). // Destination address 96 | MustStoreCoins(0). // Import Fee 97 | MustStoreBoolBit(false). // No State Init 98 | MustStoreBoolBit(true). // We store Message Body as a reference 99 | MustStoreRef(body). // Store Message Body as a reference 100 | EndCell() 101 | 102 | log.Println(base64.StdEncoding.EncodeToString(externalMessage.ToBOCWithFlags(false))) 103 | 104 | var resp tl.Serializable 105 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 106 | 107 | if err != nil { 108 | log.Fatalln(err.Error()) 109 | return 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Golang/Chapter 3/Deploying our wallet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "encoding/base64" 9 | "log" 10 | "strings" 11 | "time" 12 | 13 | "github.com/xssnick/tonutils-go/address" 14 | "github.com/xssnick/tonutils-go/liteclient" 15 | "github.com/xssnick/tonutils-go/tl" 16 | "github.com/xssnick/tonutils-go/tlb" 17 | "github.com/xssnick/tonutils-go/ton" 18 | "github.com/xssnick/tonutils-go/ton/wallet" 19 | "github.com/xssnick/tonutils-go/tvm/cell" 20 | "golang.org/x/crypto/pbkdf2" 21 | ) 22 | 23 | func main() { 24 | // mnemonic := strings.Split("put your mnemonic", " ") // get our mnemonic as array 25 | mnemonic := wallet.NewSeed() // get new mnemonic 26 | 27 | // The following three lines will extract the private key using the mnemonic phrase. We will not go into cryptographic details. It has all been implemented in the tonutils-go library, but it immediately returns the finished object of the wallet with the address and ready methods. So we’ll have to write the lines to get the key separately. Goland IDE will automatically import all required libraries (crypto, pbkdf2 and others). 28 | mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) 29 | hash := mac.Sum(nil) 30 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 31 | // 32 is a key len 32 | 33 | privateKey := ed25519.NewKeyFromSeed(k) // get private key 34 | publicKey := privateKey.Public().(ed25519.PublicKey) // get public key from private key 35 | log.Println(publicKey) // print publicKey so that at this stage the compiler does not complain that we do not use our variable 36 | log.Println(mnemonic) // if we want, we can print our mnemonic 37 | 38 | var subWallet uint64 = 698983191 39 | 40 | base64BOC := "te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==" // save our base64 encoded output from compiler to variable 41 | codeCellBytes, _ := base64.StdEncoding.DecodeString(base64BOC) // decode base64 in order to get byte array 42 | codeCell, err := cell.FromBOC(codeCellBytes) // get cell with code from byte array 43 | if err != nil { // check if there are any error 44 | panic(err) 45 | } 46 | 47 | log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type and output to the terminal 48 | 49 | dataCell := cell.BeginCell(). 50 | MustStoreUInt(0, 32). // Seqno 51 | MustStoreUInt(698983191, 32). // Subwallet ID 52 | MustStoreSlice(publicKey, 256). // Public Key 53 | EndCell() 54 | 55 | stateInit := cell.BeginCell(). 56 | MustStoreBoolBit(false). // No split_depth 57 | MustStoreBoolBit(false). // No special 58 | MustStoreBoolBit(true). // We have code 59 | MustStoreRef(codeCell). 60 | MustStoreBoolBit(true). // We have data 61 | MustStoreRef(dataCell). 62 | MustStoreBoolBit(false). // No library 63 | EndCell() 64 | 65 | contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 66 | log.Println("Contract address:", contractAddress.String()) // Output contract address to console 67 | 68 | internalMessageBody := cell.BeginCell(). 69 | MustStoreUInt(0, 32). 70 | MustStoreStringSnake("Hello, TON!"). 71 | EndCell() 72 | 73 | internalMessage := cell.BeginCell(). 74 | MustStoreUInt(0x10, 6). // no bounce 75 | MustStoreAddr(address.MustParseAddr("put your first wallet address from were you sent 0.1 TON")). 76 | MustStoreBigCoins(tlb.MustFromTON("0.03").NanoTON()). 77 | MustStoreUInt(1, 1+4+4+64+32+1+1). // We store 1 that means we have body as a reference 78 | MustStoreRef(internalMessageBody). 79 | EndCell() 80 | 81 | // message for our wallet 82 | toSign := cell.BeginCell(). 83 | MustStoreUInt(subWallet, 32). 84 | MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). 85 | MustStoreUInt(0, 32). // We put seqno = 0, because after deploying wallet will store 0 as seqno 86 | MustStoreUInt(3, 8). 87 | MustStoreRef(internalMessage) 88 | 89 | signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) 90 | body := cell.BeginCell(). 91 | MustStoreSlice(signature, 512). 92 | MustStoreBuilder(toSign). 93 | EndCell() 94 | 95 | externalMessage := cell.BeginCell(). 96 | MustStoreUInt(0b10, 2). // indicate that it is an incoming external message 97 | MustStoreUInt(0, 2). // src -> addr_none 98 | MustStoreAddr(contractAddress). 99 | MustStoreCoins(0). // Import fee 100 | MustStoreBoolBit(true). // We have State Init 101 | MustStoreBoolBit(true). // We store State Init as a reference 102 | MustStoreRef(stateInit). // Store State Init as a reference 103 | MustStoreBoolBit(true). // We store Message Body as a reference 104 | MustStoreRef(body). // Store Message Body as a reference 105 | EndCell() 106 | 107 | connection := liteclient.NewConnectionPool() 108 | configUrl := "https://ton-blockchain.github.io/global.config.json" 109 | err = connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 110 | if err != nil { 111 | panic(err) 112 | } 113 | client := ton.NewAPIClient(connection) 114 | 115 | var resp tl.Serializable 116 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 117 | if err != nil { 118 | log.Fatalln(err.Error()) 119 | return 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Golang/Chapter 4/Contract deploy via wallet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "encoding/base64" 9 | "log" 10 | "strings" 11 | "time" 12 | 13 | "github.com/xssnick/tonutils-go/address" 14 | "github.com/xssnick/tonutils-go/liteclient" 15 | "github.com/xssnick/tonutils-go/tl" 16 | "github.com/xssnick/tonutils-go/tlb" 17 | "github.com/xssnick/tonutils-go/ton" 18 | "github.com/xssnick/tonutils-go/tvm/cell" 19 | "golang.org/x/crypto/pbkdf2" 20 | ) 21 | 22 | func main() { 23 | mnemonicArray := strings.Split("put your mnemonic", " ") 24 | // The following three lines will extract the private key using the mnemonic phrase. 25 | // We will not go into cryptographic details. In the library tonutils-go, it is all implemented, 26 | // but it immediately returns the finished object of the wallet with the address and ready-made methods. 27 | // So we’ll have to write the lines to get the key separately. Goland IDE will automatically import 28 | // all required libraries (crypto, pbkdf2 and others). 29 | mac := hmac.New(sha512.New, []byte(strings.Join(mnemonicArray, " "))) 30 | hash := mac.Sum(nil) 31 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 32 | // 32 is a key len 33 | privateKey := ed25519.NewKeyFromSeed(k) // get private key 34 | publicKey := privateKey.Public().(ed25519.PublicKey) // get public key from private key 35 | 36 | BOCBytes, _ := base64.StdEncoding.DecodeString("te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A==") 37 | codeCell, _ := cell.FromBOC(BOCBytes) 38 | dataCell := cell.BeginCell(). 39 | MustStoreUInt(0, 32). // Seqno 40 | MustStoreUInt(3, 32). // Subwallet ID 41 | MustStoreSlice(publicKey, 256). // Public Key 42 | EndCell() 43 | 44 | stateInit := cell.BeginCell(). 45 | MustStoreBoolBit(false). // No split_depth 46 | MustStoreBoolBit(false). // No special 47 | MustStoreBoolBit(true). // We have code 48 | MustStoreRef(codeCell). 49 | MustStoreBoolBit(true). // We have data 50 | MustStoreRef(dataCell). 51 | MustStoreBoolBit(false). // No library 52 | EndCell() 53 | 54 | contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 55 | log.Println("Contract address:", contractAddress.String()) // Output contract address to console 56 | 57 | internalMessageBody := cell.BeginCell(). 58 | MustStoreUInt(0, 32). 59 | MustStoreStringSnake("Deploying..."). 60 | EndCell() 61 | 62 | internalMessage := cell.BeginCell(). 63 | MustStoreUInt(0x10, 6). // no bounce 64 | MustStoreAddr(contractAddress). 65 | MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()). 66 | MustStoreUInt(0, 1+4+4+64+32). 67 | MustStoreBoolBit(true). // We have State Init 68 | MustStoreBoolBit(true). // We store State Init as a reference 69 | MustStoreRef(stateInit). // Store State Init as a reference 70 | MustStoreBoolBit(true). // We store Message Body as a reference 71 | MustStoreRef(internalMessageBody). // Store Message Body Init as a reference 72 | EndCell() 73 | 74 | connection := liteclient.NewConnectionPool() 75 | configUrl := "https://ton-blockchain.github.io/global.config.json" 76 | err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 77 | if err != nil { 78 | panic(err) 79 | } 80 | client := ton.NewAPIClient(connection) 81 | 82 | block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer 83 | if err != nil { 84 | log.Fatalln("CurrentMasterchainInfo err:", err.Error()) 85 | return 86 | } 87 | 88 | walletMnemonicArray := strings.Split("put your mnemonic", " ") 89 | mac = hmac.New(sha512.New, []byte(strings.Join(walletMnemonicArray, " "))) 90 | hash = mac.Sum(nil) 91 | k = pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 92 | // 32 is a key len 93 | walletPrivateKey := ed25519.NewKeyFromSeed(k) // get private key 94 | walletAddress := address.MustParseAddr("put your wallet address with which you will deploy") 95 | 96 | getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract 97 | if err != nil { 98 | log.Fatalln("RunGetMethod err:", err.Error()) 99 | return 100 | } 101 | seqno := getMethodResult.MustInt(0) // get seqno from response 102 | 103 | toSign := cell.BeginCell(). 104 | MustStoreUInt(698983191, 32). // subwallet_id | We consider this further 105 | MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // message expiration time, +60 = 1 minute 106 | MustStoreUInt(seqno.Uint64(), 32). // store seqno 107 | // Do not forget that if we use Wallet V4, we need to add .MustStoreUInt(0, 8) 108 | MustStoreUInt(3, 8). // store mode of our internal message 109 | MustStoreRef(internalMessage) // store our internalMessage as a reference 110 | 111 | signature := ed25519.Sign(walletPrivateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature 112 | 113 | body := cell.BeginCell(). 114 | MustStoreSlice(signature, 512). // store signature 115 | MustStoreBuilder(toSign). // store our message 116 | EndCell() 117 | 118 | externalMessage := cell.BeginCell(). 119 | MustStoreUInt(0b10, 2). // ext_in_msg_info$10 120 | MustStoreUInt(0, 2). // src -> addr_none 121 | MustStoreAddr(walletAddress). // Destination address 122 | MustStoreCoins(0). // Import Fee 123 | MustStoreBoolBit(false). // No State Init 124 | MustStoreBoolBit(true). // We store Message Body as a reference 125 | MustStoreRef(body). // Store Message Body as a reference 126 | EndCell() 127 | 128 | var resp tl.Serializable 129 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 130 | 131 | if err != nil { 132 | log.Fatalln(err.Error()) 133 | return 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Golang/Chapter 4/Get methods in Wallet V3 and Wallet v4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/xssnick/tonutils-go/address" 6 | "github.com/xssnick/tonutils-go/liteclient" 7 | "github.com/xssnick/tonutils-go/ton" 8 | "log" 9 | "math/big" 10 | ) 11 | 12 | func main() { 13 | connection := liteclient.NewConnectionPool() 14 | configUrl := "https://ton-blockchain.github.io/global.config.json" 15 | err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 16 | if err != nil { 17 | panic(err) 18 | } 19 | client := ton.NewAPIClient(connection) 20 | 21 | block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer 22 | if err != nil { 23 | log.Fatalln("CurrentMasterchainInfo err:", err.Error()) 24 | return 25 | } 26 | 27 | walletAddress := address.MustParseAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF") // my wallet address as an example 28 | 29 | getResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "get_public_key") // run get_public_key GET Method 30 | if err != nil { 31 | log.Fatalln("RunGetMethod err:", err.Error()) 32 | return 33 | } 34 | 35 | // We have a response as an array with values and should specify the index when reading it 36 | // In the case of get_public_key, we have only one returned value that is stored at 0 index 37 | publicKeyUInt := getResult.MustInt(0) // read answer that contains uint256 38 | publicKey := publicKeyUInt.Text(16) // get hex string from bigint (uint256) 39 | log.Println(publicKey) 40 | 41 | oldWalletAddress := address.MustParseAddr("EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k") 42 | subscriptionAddress := address.MustParseAddr("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ") // subscription plugin address which is already installed on the wallet 43 | 44 | hash := big.NewInt(0).SetBytes(subscriptionAddress.Data()) 45 | // runGetMethod will automatically identify types of passed values 46 | getResult, err = client.RunGetMethod(context.Background(), block, oldWalletAddress, 47 | "is_plugin_installed", 48 | 0, // pass workchain 49 | hash) // pass plugin address 50 | if err != nil { 51 | log.Fatalln("RunGetMethod err:", err.Error()) 52 | return 53 | } 54 | 55 | log.Println(getResult.MustInt(0)) // -1 56 | } 57 | -------------------------------------------------------------------------------- /Golang/Chapter 4/NFT Transfer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "log" 9 | "strings" 10 | "time" 11 | 12 | "github.com/xssnick/tonutils-go/address" 13 | "github.com/xssnick/tonutils-go/liteclient" 14 | "github.com/xssnick/tonutils-go/tl" 15 | "github.com/xssnick/tonutils-go/tlb" 16 | "github.com/xssnick/tonutils-go/ton" 17 | "github.com/xssnick/tonutils-go/tvm/cell" 18 | "golang.org/x/crypto/pbkdf2" 19 | ) 20 | 21 | func main() { 22 | destinationAddress := address.MustParseAddr("put your wallet where you want to send NFT") 23 | walletAddress := address.MustParseAddr("put your wallet which is the owner of NFT") 24 | nftAddress := address.MustParseAddr("put your nft address") 25 | 26 | // We can add a comment, but it will not be displayed in the explorers, 27 | // as it is not supported by them at the time of writing the tutorial. 28 | forwardPayload := cell.BeginCell(). 29 | MustStoreUInt(0, 32). 30 | MustStoreStringSnake("Hello, TON!"). 31 | EndCell() 32 | 33 | transferNftBody := cell.BeginCell(). 34 | MustStoreUInt(0x5fcc3d14, 32). // Opcode for NFT transfer 35 | MustStoreUInt(0, 64). // query_id 36 | MustStoreAddr(destinationAddress). // new_owner 37 | MustStoreAddr(walletAddress). // response_destination for excesses 38 | MustStoreBoolBit(false). // we do not have custom_payload 39 | MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()). // forward_payload 40 | MustStoreBoolBit(true). // we store forward_payload as a reference 41 | MustStoreRef(forwardPayload). // store forward_payload as a reference 42 | EndCell() 43 | 44 | internalMessage := cell.BeginCell(). 45 | MustStoreUInt(0x18, 6). // bounce 46 | MustStoreAddr(nftAddress). 47 | MustStoreBigCoins(tlb.MustFromTON("0.05").NanoTON()). 48 | MustStoreUInt(1, 1+4+4+64+32+1+1). // We store 1 that means we have body as a reference 49 | MustStoreRef(transferNftBody). 50 | EndCell() 51 | 52 | connection := liteclient.NewConnectionPool() 53 | configUrl := "https://ton-blockchain.github.io/global.config.json" 54 | err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 55 | if err != nil { 56 | panic(err) 57 | } 58 | client := ton.NewAPIClient(connection) 59 | 60 | mnemonic := strings.Split("put your mnemonic", " ") // word1 word2 word3 61 | // The following three lines will extract the private key using the mnemonic phrase. 62 | // We will not go into cryptographic details. In the library tonutils-go, it is all implemented, 63 | // but it immediately returns the finished object of the wallet with the address and ready-made methods. 64 | // So we’ll have to write the lines to get the key separately. Goland IDE will automatically import 65 | // all required libraries (crypto, pbkdf2 and others). 66 | mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) 67 | hash := mac.Sum(nil) 68 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 69 | // 32 is a key len 70 | privateKey := ed25519.NewKeyFromSeed(k) // get private key 71 | 72 | block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer 73 | if err != nil { 74 | log.Fatalln("CurrentMasterchainInfo err:", err.Error()) 75 | return 76 | } 77 | 78 | getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract 79 | if err != nil { 80 | log.Fatalln("RunGetMethod err:", err.Error()) 81 | return 82 | } 83 | seqno := getMethodResult.MustInt(0) // get seqno from response 84 | 85 | toSign := cell.BeginCell(). 86 | MustStoreUInt(698983191, 32). // subwallet_id | We consider this further 87 | MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // message expiration time, +60 = 1 minute 88 | MustStoreUInt(seqno.Uint64(), 32). // store seqno 89 | // Do not forget that if we use Wallet V4, we need to add .MustStoreUInt(0, 8) 90 | MustStoreUInt(3, 8). // store mode of our internal message 91 | MustStoreRef(internalMessage) // store our internalMessage as a reference 92 | 93 | signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature 94 | 95 | body := cell.BeginCell(). 96 | MustStoreSlice(signature, 512). // store signature 97 | MustStoreBuilder(toSign). // store our message 98 | EndCell() 99 | 100 | externalMessage := cell.BeginCell(). 101 | MustStoreUInt(0b10, 2). // ext_in_msg_info$10 102 | MustStoreUInt(0, 2). // src -> addr_none 103 | MustStoreAddr(walletAddress). // Destination address 104 | MustStoreCoins(0). // Import Fee 105 | MustStoreBoolBit(false). // No State Init 106 | MustStoreBoolBit(true). // We store Message Body as a reference 107 | MustStoreRef(body). // Store Message Body as a reference 108 | EndCell() 109 | 110 | var resp tl.Serializable 111 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 112 | 113 | if err != nil { 114 | log.Fatalln(err.Error()) 115 | return 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Golang/Chapter 4/Sending multiple messages simultaneously.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "log" 9 | "strings" 10 | "time" 11 | 12 | "github.com/xssnick/tonutils-go/address" 13 | "github.com/xssnick/tonutils-go/liteclient" 14 | "github.com/xssnick/tonutils-go/tl" 15 | "github.com/xssnick/tonutils-go/tlb" 16 | "github.com/xssnick/tonutils-go/ton" 17 | "github.com/xssnick/tonutils-go/tvm/cell" 18 | "golang.org/x/crypto/pbkdf2" 19 | ) 20 | 21 | func main() { 22 | internalMessagesAmount := [4]string{"0.01", "0.02", "0.03", "0.04"} 23 | internalMessagesComment := [4]string{ 24 | "Hello, TON! #1", 25 | "Hello, TON! #2", 26 | "", // Let's leave the third message without comment 27 | "Hello, TON! #4", 28 | } 29 | destinationAddresses := [4]string{ 30 | "Put any address that belongs to you", 31 | "Put any address that belongs to you", 32 | "Put any address that belongs to you", 33 | "Put any address that belongs to you", 34 | } // All 4 addresses can be the same 35 | 36 | var internalMessages [len(internalMessagesAmount)]*cell.Cell // array for our internal messages 37 | 38 | for i := 0; i < len(internalMessagesAmount); i++ { 39 | amount := internalMessagesAmount[i] 40 | 41 | internalMessage := cell.BeginCell(). 42 | MustStoreUInt(0x18, 6). // bounce 43 | MustStoreAddr(address.MustParseAddr(destinationAddresses[i])). 44 | MustStoreBigCoins(tlb.MustFromTON(amount).NanoTON()). 45 | MustStoreUInt(0, 1+4+4+64+32+1) 46 | 47 | /* 48 | At this stage, it is not clear if we will have a message body. 49 | So put a bit only for stateInit, and if we have a comment, in means 50 | we have a body message. In that case, set the bit to 1 and store the 51 | body as a reference. 52 | */ 53 | 54 | if internalMessagesComment[i] != "" { 55 | internalMessage.MustStoreBoolBit(true) // we store Message Body as a reference 56 | 57 | internalMessageBody := cell.BeginCell(). 58 | MustStoreUInt(0, 32). 59 | MustStoreStringSnake(internalMessagesComment[i]). 60 | EndCell() 61 | 62 | internalMessage.MustStoreRef(internalMessageBody) 63 | } else { 64 | /* 65 | Since we do not have a message body, we indicate that 66 | the message body is in this message, but do not write it, 67 | which means it is absent. In that case, just set the bit to 0. 68 | */ 69 | internalMessage.MustStoreBoolBit(false) 70 | } 71 | internalMessages[i] = internalMessage.EndCell() 72 | } 73 | 74 | walletAddress := address.MustParseAddr("put your wallet address") 75 | 76 | connection := liteclient.NewConnectionPool() 77 | configUrl := "https://ton-blockchain.github.io/global.config.json" 78 | err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 79 | if err != nil { 80 | panic(err) 81 | } 82 | client := ton.NewAPIClient(connection) 83 | 84 | mnemonic := strings.Split("put your mnemonic", " ") // word1 word2 word3 85 | // The following three lines will extract the private key using the mnemonic phrase. 86 | // We will not go into cryptographic details. In the library tonutils-go, it is all implemented, 87 | // but it immediately returns the finished object of the wallet with the address and ready-made methods. 88 | // So we’ll have to write the lines to get the key separately. Goland IDE will automatically import 89 | // all required libraries (crypto, pbkdf2 and others). 90 | mac := hmac.New(sha512.New, []byte(strings.Join(mnemonic, " "))) 91 | hash := mac.Sum(nil) 92 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 93 | // 32 is a key len 94 | privateKey := ed25519.NewKeyFromSeed(k) // get private key 95 | 96 | block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer 97 | if err != nil { 98 | log.Fatalln("CurrentMasterchainInfo err:", err.Error()) 99 | return 100 | } 101 | 102 | getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract 103 | if err != nil { 104 | log.Fatalln("RunGetMethod err:", err.Error()) 105 | return 106 | } 107 | seqno := getMethodResult.MustInt(0) // get seqno from response 108 | 109 | toSign := cell.BeginCell(). 110 | MustStoreUInt(698983191, 32). // subwallet_id | We consider this further 111 | MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // message expiration time, +60 = 1 minute 112 | MustStoreUInt(seqno.Uint64(), 32) // store seqno 113 | // Do not forget that if we use Wallet V4, we need to add .MustStoreUInt(0, 8) 114 | 115 | for i := 0; i < len(internalMessages); i++ { 116 | internalMessage := internalMessages[i] 117 | toSign.MustStoreUInt(3, 8) // store mode of our internal message 118 | toSign.MustStoreRef(internalMessage) // store our internalMessage as a reference 119 | } 120 | 121 | signature := ed25519.Sign(privateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature 122 | 123 | body := cell.BeginCell(). 124 | MustStoreSlice(signature, 512). // store signature 125 | MustStoreBuilder(toSign). // store our message 126 | EndCell() 127 | 128 | externalMessage := cell.BeginCell(). 129 | MustStoreUInt(0b10, 2). // ext_in_msg_info$10 130 | MustStoreUInt(0, 2). // src -> addr_none 131 | MustStoreAddr(walletAddress). // Destination address 132 | MustStoreCoins(0). // Import Fee 133 | MustStoreBoolBit(false). // No State Init 134 | MustStoreBoolBit(true). // We store Message Body as a reference 135 | MustStoreRef(body). // Store Message Body as a reference 136 | EndCell() 137 | 138 | var resp tl.Serializable 139 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 140 | 141 | if err != nil { 142 | log.Fatalln(err.Error()) 143 | return 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Golang/Chapter 5/Deploying high-load wallet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "encoding/base64" 9 | "log" 10 | "strings" 11 | "time" 12 | 13 | "github.com/xssnick/tonutils-go/address" 14 | "github.com/xssnick/tonutils-go/liteclient" 15 | "github.com/xssnick/tonutils-go/tl" 16 | "github.com/xssnick/tonutils-go/tlb" 17 | "github.com/xssnick/tonutils-go/ton" 18 | "github.com/xssnick/tonutils-go/tvm/cell" 19 | "golang.org/x/crypto/pbkdf2" 20 | ) 21 | 22 | func main() { 23 | base64BOC := "te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+ZbgyWhyEA0gED0Q4rmMQHIyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6VsEiCUMFMDud4gkzM2AZJsIeKz" // save our base64 encoded output from compiler to variable 24 | codeCellBytes, _ := base64.StdEncoding.DecodeString(base64BOC) // decode base64 in order to get byte array 25 | codeCell, err := cell.FromBOC(codeCellBytes) // get cell with code from byte array 26 | if err != nil { // check if there is any error 27 | panic(err) 28 | } 29 | 30 | log.Println("Hash:", base64.StdEncoding.EncodeToString(codeCell.Hash())) // get the hash of our cell, encode it to base64 because it has []byte type and output to the terminal 31 | 32 | highloadMnemonicArray := strings.Split("put your mnemonic that you have generated and saved before", " ") // word1 word2 word3 33 | mac := hmac.New(sha512.New, []byte(strings.Join(highloadMnemonicArray, " "))) 34 | hash := mac.Sum(nil) 35 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 36 | // 32 is a key len 37 | highloadPrivateKey := ed25519.NewKeyFromSeed(k) // get private key 38 | highloadPublicKey := highloadPrivateKey.Public().(ed25519.PublicKey) // get public key from private key 39 | 40 | dataCell := cell.BeginCell(). 41 | MustStoreUInt(698983191, 32). // Subwallet ID 42 | MustStoreUInt(0, 64). // Last cleaned 43 | MustStoreSlice(highloadPublicKey, 256). // Public Key 44 | MustStoreBoolBit(false). // indicate that the dictionary is empty 45 | EndCell() 46 | 47 | stateInit := cell.BeginCell(). 48 | MustStoreBoolBit(false). // No split_depth 49 | MustStoreBoolBit(false). // No special 50 | MustStoreBoolBit(true). // We have code 51 | MustStoreRef(codeCell). 52 | MustStoreBoolBit(true). // We have data 53 | MustStoreRef(dataCell). 54 | MustStoreBoolBit(false). // No library 55 | EndCell() 56 | 57 | contractAddress := address.NewAddress(0, 0, stateInit.Hash()) // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 58 | log.Println("Contract address:", contractAddress.String()) // Output contract address to console 59 | 60 | internalMessageBody := cell.BeginCell(). 61 | MustStoreUInt(0, 32). 62 | MustStoreStringSnake("Deploying..."). 63 | EndCell() 64 | 65 | internalMessage := cell.BeginCell(). 66 | MustStoreUInt(0x10, 6). // no bounce 67 | MustStoreAddr(contractAddress). 68 | MustStoreBigCoins(tlb.MustFromTON("0.01").NanoTON()). 69 | MustStoreUInt(0, 1+4+4+64+32). 70 | MustStoreBoolBit(true). // We have State Init 71 | MustStoreBoolBit(true). // We store State Init as a reference 72 | MustStoreRef(stateInit). // Store State Init as a reference 73 | MustStoreBoolBit(true). // We store Message Body as a reference 74 | MustStoreRef(internalMessageBody). // Store Message Body Init as a reference 75 | EndCell() 76 | 77 | connection := liteclient.NewConnectionPool() 78 | configUrl := "https://ton-blockchain.github.io/global.config.json" 79 | err = connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 80 | if err != nil { 81 | panic(err) 82 | } 83 | client := ton.NewAPIClient(connection) 84 | 85 | block, err := client.CurrentMasterchainInfo(context.Background()) // get current block, we will need it in requests to LiteServer 86 | if err != nil { 87 | log.Fatalln("CurrentMasterchainInfo err:", err.Error()) 88 | return 89 | } 90 | 91 | walletMnemonicArray := strings.Split("put your mnemonic", " ") 92 | mac = hmac.New(sha512.New, []byte(strings.Join(walletMnemonicArray, " "))) 93 | hash = mac.Sum(nil) 94 | k = pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 95 | // 32 is a key len 96 | walletPrivateKey := ed25519.NewKeyFromSeed(k) // get private key 97 | walletAddress := address.MustParseAddr("put your wallet address with which you will deploy") 98 | 99 | getMethodResult, err := client.RunGetMethod(context.Background(), block, walletAddress, "seqno") // run "seqno" GET method from your wallet contract 100 | if err != nil { 101 | log.Fatalln("RunGetMethod err:", err.Error()) 102 | return 103 | } 104 | seqno := getMethodResult.MustInt(0) // get seqno from response 105 | 106 | toSign := cell.BeginCell(). 107 | MustStoreUInt(698983191, 32). // subwallet_id | We consider this further 108 | MustStoreUInt(uint64(time.Now().UTC().Unix()+60), 32). // message expiration time, +60 = 1 minute 109 | MustStoreUInt(seqno.Uint64(), 32). // store seqno 110 | // Do not forget that if we use Wallet V4, we need to add .MustStoreUInt(0, 8) 111 | MustStoreUInt(3, 8). // store mode of our internal message 112 | MustStoreRef(internalMessage) // store our internalMessage as a reference 113 | 114 | signature := ed25519.Sign(walletPrivateKey, toSign.EndCell().Hash()) // get the hash of our message to wallet smart contract and sign it to get signature 115 | 116 | body := cell.BeginCell(). 117 | MustStoreSlice(signature, 512). // store signature 118 | MustStoreBuilder(toSign). // store our message 119 | EndCell() 120 | 121 | externalMessage := cell.BeginCell(). 122 | MustStoreUInt(0b10, 2). // ext_in_msg_info$10 123 | MustStoreUInt(0, 2). // src -> addr_none 124 | MustStoreAddr(walletAddress). // Destination address 125 | MustStoreCoins(0). // Import Fee 126 | MustStoreBoolBit(false). // No State Init 127 | MustStoreBoolBit(true). // We store Message Body as a reference 128 | MustStoreRef(body). // Store Message Body as a reference 129 | EndCell() 130 | 131 | var resp tl.Serializable 132 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 133 | 134 | if err != nil { 135 | log.Fatalln(err.Error()) 136 | return 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Golang/Chapter 5/Sending transactions from high-load wallet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/hmac" 7 | "crypto/sha512" 8 | "fmt" 9 | "log" 10 | "math/big" 11 | "math/rand" 12 | "strings" 13 | "time" 14 | 15 | "github.com/xssnick/tonutils-go/address" 16 | "github.com/xssnick/tonutils-go/liteclient" 17 | "github.com/xssnick/tonutils-go/tl" 18 | "github.com/xssnick/tonutils-go/tlb" 19 | "github.com/xssnick/tonutils-go/ton" 20 | "github.com/xssnick/tonutils-go/tvm/cell" 21 | "golang.org/x/crypto/pbkdf2" 22 | ) 23 | 24 | func main() { 25 | var internalMessages []*cell.Cell 26 | walletAddress := address.MustParseAddr("put your wallet address from which you deployed high-load wallet") 27 | 28 | for i := 0; i < 12; i++ { 29 | comment := fmt.Sprintf("Hello, TON! #%d", i) 30 | internalMessageBody := cell.BeginCell(). 31 | MustStoreUInt(0, 32). 32 | MustStoreBinarySnake([]byte(comment)). 33 | EndCell() 34 | 35 | internalMessage := cell.BeginCell(). 36 | MustStoreUInt(0x18, 6). // bounce 37 | MustStoreAddr(walletAddress). 38 | MustStoreBigCoins(tlb.MustFromTON("0.001").NanoTON()). 39 | MustStoreUInt(0, 1+4+4+64+32). 40 | MustStoreBoolBit(false). // We do not have State Init 41 | MustStoreBoolBit(true). // We store Message Body as a reference 42 | MustStoreRef(internalMessageBody). // Store Message Body Init as a reference 43 | EndCell() 44 | 45 | messageData := cell.BeginCell(). 46 | MustStoreUInt(3, 8). // message mode 47 | MustStoreRef(internalMessage). 48 | EndCell() 49 | 50 | internalMessages = append(internalMessages, messageData) 51 | } 52 | 53 | dictionary := cell.NewDict(16) // create an empty dictionary with the key as a number and the value as a cell 54 | for i := 0; i < len(internalMessages); i++ { 55 | internalMessage := internalMessages[i] // get our message from an array 56 | err := dictionary.SetIntKey(big.NewInt(int64(i)), internalMessage) // save the message in the dictionary 57 | if err != nil { 58 | return 59 | } 60 | } 61 | 62 | queryID := rand.Uint32() 63 | timeout := 120 // timeout for message expiration, 120 seconds = 2 minutes 64 | now := time.Now().Add(time.Duration(timeout)*time.Second).UTC().Unix() << 32 // get current timestamp + timeout 65 | finalQueryID := uint64(now) + uint64(queryID) // get our final query_id 66 | log.Println(finalQueryID) // print query_id. With this query_id we can call GET method to check if our request has been processed 67 | 68 | toSign := cell.BeginCell(). 69 | MustStoreUInt(698983191, 32). // subwallet_id 70 | MustStoreUInt(finalQueryID, 64). 71 | MustStoreDict(dictionary) 72 | 73 | highloadMnemonicArray := strings.Split("put your high-load wallet mnemonic", " ") // word1 word2 word3 74 | mac := hmac.New(sha512.New, []byte(strings.Join(highloadMnemonicArray, " "))) 75 | hash := mac.Sum(nil) 76 | k := pbkdf2.Key(hash, []byte("TON default seed"), 100000, 32, sha512.New) // In TON libraries "TON default seed" is used as salt when getting keys 77 | // 32 is a key len 78 | highloadPrivateKey := ed25519.NewKeyFromSeed(k) // get private key 79 | highloadWalletAddress := address.MustParseAddr("put your high-load wallet address") 80 | 81 | signature := ed25519.Sign(highloadPrivateKey, toSign.EndCell().Hash()) 82 | 83 | body := cell.BeginCell(). 84 | MustStoreSlice(signature, 512). // store signature 85 | MustStoreBuilder(toSign). // store our message 86 | EndCell() 87 | 88 | externalMessage := cell.BeginCell(). 89 | MustStoreUInt(0b10, 2). // ext_in_msg_info$10 90 | MustStoreUInt(0, 2). // src -> addr_none 91 | MustStoreAddr(highloadWalletAddress). // Destination address 92 | MustStoreCoins(0). // Import Fee 93 | MustStoreBoolBit(false). // No State Init 94 | MustStoreBoolBit(true). // We store Message Body as a reference 95 | MustStoreRef(body). // Store Message Body as a reference 96 | EndCell() 97 | 98 | connection := liteclient.NewConnectionPool() 99 | configUrl := "https://ton-blockchain.github.io/global.config.json" 100 | err := connection.AddConnectionsFromConfigUrl(context.Background(), configUrl) 101 | if err != nil { 102 | panic(err) 103 | } 104 | client := ton.NewAPIClient(connection) 105 | 106 | var resp tl.Serializable 107 | err = client.Client().QueryLiteserver(context.Background(), ton.SendMessage{Body: externalMessage.ToBOCWithFlags(false)}, &resp) 108 | 109 | if err != nil { 110 | log.Fatalln(err.Error()) 111 | return 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Golang/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/xssnick/tonutils-go v1.7.4 7 | golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 8 | ) 9 | 10 | require ( 11 | github.com/oasisprotocol/curve25519-voi v0.0.0-20220328075252-7dd334e3daae // indirect 12 | github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect 13 | golang.org/x/sys v0.0.0-20220325203850-36772127a21f // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /Golang/go.sum: -------------------------------------------------------------------------------- 1 | github.com/oasisprotocol/curve25519-voi v0.0.0-20220328075252-7dd334e3daae h1:7smdlrfdcZic4VfsGKD2ulWL804a4GVphr4s7WZxGiY= 2 | github.com/oasisprotocol/curve25519-voi v0.0.0-20220328075252-7dd334e3daae/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= 3 | github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs= 4 | github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= 5 | github.com/xssnick/tonutils-go v1.6.2 h1:K8Kp2pQ9n8i+73gCepcdf0GJnTK826ZxGWjQk4l0i4I= 6 | github.com/xssnick/tonutils-go v1.6.2/go.mod h1:wH8ldhLueyfXW15r3MyaIq9YzA+8bzvL6UMU2BLp08g= 7 | github.com/xssnick/tonutils-go v1.7.4 h1:t27eGhwkmaiSyslzZDAdOxM5cuEEjP2fMbyVxnXYWu0= 8 | github.com/xssnick/tonutils-go v1.7.4/go.mod h1:wH8ldhLueyfXW15r3MyaIq9YzA+8bzvL6UMU2BLp08g= 9 | golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s= 10 | golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 11 | golang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU= 12 | golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 13 | -------------------------------------------------------------------------------- /Golang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func main() { 8 | log.Println("Hello, TON!") 9 | } -------------------------------------------------------------------------------- /JavaScript - TypeScript/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "npx ts-node ./src/index.ts" 6 | } -------------------------------------------------------------------------------- /JavaScript - TypeScript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial-code", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tutorial-code", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@ton-community/func-js": "^0.7.0", 13 | "@ton/core": "^0.56.3", 14 | "@ton/crypto": "^3.2.0", 15 | "@ton/ton": "^13.11.2" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^20.4.6", 19 | "nodemon": "^3.0.1", 20 | "ts-node": "^10.9.1", 21 | "typescript": "^5.1.6" 22 | } 23 | }, 24 | "node_modules/@cspotcode/source-map-support": { 25 | "version": "0.8.1", 26 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 27 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 28 | "dev": true, 29 | "dependencies": { 30 | "@jridgewell/trace-mapping": "0.3.9" 31 | }, 32 | "engines": { 33 | "node": ">=12" 34 | } 35 | }, 36 | "node_modules/@jridgewell/resolve-uri": { 37 | "version": "3.1.0", 38 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 39 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 40 | "dev": true, 41 | "engines": { 42 | "node": ">=6.0.0" 43 | } 44 | }, 45 | "node_modules/@jridgewell/sourcemap-codec": { 46 | "version": "1.4.14", 47 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 48 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 49 | "dev": true 50 | }, 51 | "node_modules/@jridgewell/trace-mapping": { 52 | "version": "0.3.9", 53 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 54 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 55 | "dev": true, 56 | "dependencies": { 57 | "@jridgewell/resolve-uri": "^3.0.3", 58 | "@jridgewell/sourcemap-codec": "^1.4.10" 59 | } 60 | }, 61 | "node_modules/@ton-community/func-js": { 62 | "version": "0.7.0", 63 | "resolved": "https://registry.npmjs.org/@ton-community/func-js/-/func-js-0.7.0.tgz", 64 | "integrity": "sha512-VYJsv6Pqz6+qh3HlZWReBG5W9RXutAdIFYDqmblPSCXfjBhx/QjON/3WoppzUVrqQQdD0BVIh4PR+xRHRCBNhw==", 65 | "dependencies": { 66 | "@ton-community/func-js-bin": "0.4.4-newops.1", 67 | "arg": "^5.0.2" 68 | }, 69 | "bin": { 70 | "func-js": "dist/cli.js" 71 | } 72 | }, 73 | "node_modules/@ton-community/func-js-bin": { 74 | "version": "0.4.4-newops.1", 75 | "resolved": "https://registry.npmjs.org/@ton-community/func-js-bin/-/func-js-bin-0.4.4-newops.1.tgz", 76 | "integrity": "sha512-TV4t6XhmItq4t+wv4pV30yEwb+YvdmsKo4Ig4B0zp4PLdI0r9iZHz4y5bBHcXmDQDRqulXzK6kTgfHvs2CIsaQ==" 77 | }, 78 | "node_modules/@ton-community/func-js/node_modules/arg": { 79 | "version": "5.0.2", 80 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 81 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" 82 | }, 83 | "node_modules/@ton/core": { 84 | "version": "0.56.3", 85 | "resolved": "https://registry.npmjs.org/@ton/core/-/core-0.56.3.tgz", 86 | "integrity": "sha512-HVkalfqw8zqLLPehtq0CNhu5KjVzc7IrbDwDHPjGoOSXmnqSobiWj8a5F+YuWnZnEbQKtrnMGNOOjVw4LG37rg==", 87 | "dependencies": { 88 | "symbol.inspect": "1.0.1" 89 | }, 90 | "peerDependencies": { 91 | "@ton/crypto": ">=3.2.0" 92 | } 93 | }, 94 | "node_modules/@ton/crypto": { 95 | "version": "3.2.0", 96 | "resolved": "https://registry.npmjs.org/@ton/crypto/-/crypto-3.2.0.tgz", 97 | "integrity": "sha512-50RkwReEuV2FkxSZ8ht/x9+n0ZGtwRKGsJ0ay4I/HFhkYVG/awIIBQeH0W4j8d5lADdO5h01UtX8PJ8AjiejjA==", 98 | "dependencies": { 99 | "@ton/crypto-primitives": "2.0.0", 100 | "jssha": "3.2.0", 101 | "tweetnacl": "1.0.3" 102 | } 103 | }, 104 | "node_modules/@ton/crypto-primitives": { 105 | "version": "2.0.0", 106 | "resolved": "https://registry.npmjs.org/@ton/crypto-primitives/-/crypto-primitives-2.0.0.tgz", 107 | "integrity": "sha512-wttiNClmGbI6Dfy/8oyNnsIV0b/qYkCJz4Gn4eP62lJZzMtVQ94Ko7nikDX1EfYHkLI1xpOitWpW+8ZuG6XtDg==", 108 | "dependencies": { 109 | "jssha": "3.2.0" 110 | } 111 | }, 112 | "node_modules/@ton/ton": { 113 | "version": "13.11.2", 114 | "resolved": "https://registry.npmjs.org/@ton/ton/-/ton-13.11.2.tgz", 115 | "integrity": "sha512-EPqW+ZTe0MmfqguJEIGMuAqTAFRKMEce95HlDx8h6CGn2y3jiMgV1/oO+WpDIOiX+1wnTu+xtajk8JTWr8nKRQ==", 116 | "dependencies": { 117 | "axios": "^1.6.7", 118 | "dataloader": "^2.0.0", 119 | "symbol.inspect": "1.0.1", 120 | "teslabot": "^1.3.0", 121 | "zod": "^3.21.4" 122 | }, 123 | "peerDependencies": { 124 | "@ton/core": ">=0.56.0", 125 | "@ton/crypto": ">=3.2.0" 126 | } 127 | }, 128 | "node_modules/@tsconfig/node10": { 129 | "version": "1.0.9", 130 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 131 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", 132 | "dev": true 133 | }, 134 | "node_modules/@tsconfig/node12": { 135 | "version": "1.0.11", 136 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 137 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 138 | "dev": true 139 | }, 140 | "node_modules/@tsconfig/node14": { 141 | "version": "1.0.3", 142 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 143 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 144 | "dev": true 145 | }, 146 | "node_modules/@tsconfig/node16": { 147 | "version": "1.0.3", 148 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", 149 | "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", 150 | "dev": true 151 | }, 152 | "node_modules/@types/node": { 153 | "version": "20.4.6", 154 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz", 155 | "integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==", 156 | "dev": true 157 | }, 158 | "node_modules/abbrev": { 159 | "version": "1.1.1", 160 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 161 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 162 | "dev": true 163 | }, 164 | "node_modules/acorn": { 165 | "version": "8.8.2", 166 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", 167 | "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", 168 | "dev": true, 169 | "bin": { 170 | "acorn": "bin/acorn" 171 | }, 172 | "engines": { 173 | "node": ">=0.4.0" 174 | } 175 | }, 176 | "node_modules/acorn-walk": { 177 | "version": "8.2.0", 178 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 179 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 180 | "dev": true, 181 | "engines": { 182 | "node": ">=0.4.0" 183 | } 184 | }, 185 | "node_modules/anymatch": { 186 | "version": "3.1.3", 187 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 188 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 189 | "dev": true, 190 | "dependencies": { 191 | "normalize-path": "^3.0.0", 192 | "picomatch": "^2.0.4" 193 | }, 194 | "engines": { 195 | "node": ">= 8" 196 | } 197 | }, 198 | "node_modules/arg": { 199 | "version": "4.1.3", 200 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 201 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 202 | "dev": true 203 | }, 204 | "node_modules/asynckit": { 205 | "version": "0.4.0", 206 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 207 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 208 | }, 209 | "node_modules/axios": { 210 | "version": "1.7.2", 211 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", 212 | "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", 213 | "dependencies": { 214 | "follow-redirects": "^1.15.6", 215 | "form-data": "^4.0.0", 216 | "proxy-from-env": "^1.1.0" 217 | } 218 | }, 219 | "node_modules/balanced-match": { 220 | "version": "1.0.2", 221 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 222 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 223 | "dev": true 224 | }, 225 | "node_modules/binary-extensions": { 226 | "version": "2.2.0", 227 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 228 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 229 | "dev": true, 230 | "engines": { 231 | "node": ">=8" 232 | } 233 | }, 234 | "node_modules/brace-expansion": { 235 | "version": "1.1.11", 236 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 237 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 238 | "dev": true, 239 | "dependencies": { 240 | "balanced-match": "^1.0.0", 241 | "concat-map": "0.0.1" 242 | } 243 | }, 244 | "node_modules/braces": { 245 | "version": "3.0.2", 246 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 247 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 248 | "dev": true, 249 | "dependencies": { 250 | "fill-range": "^7.0.1" 251 | }, 252 | "engines": { 253 | "node": ">=8" 254 | } 255 | }, 256 | "node_modules/chokidar": { 257 | "version": "3.5.3", 258 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 259 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 260 | "dev": true, 261 | "funding": [ 262 | { 263 | "type": "individual", 264 | "url": "https://paulmillr.com/funding/" 265 | } 266 | ], 267 | "dependencies": { 268 | "anymatch": "~3.1.2", 269 | "braces": "~3.0.2", 270 | "glob-parent": "~5.1.2", 271 | "is-binary-path": "~2.1.0", 272 | "is-glob": "~4.0.1", 273 | "normalize-path": "~3.0.0", 274 | "readdirp": "~3.6.0" 275 | }, 276 | "engines": { 277 | "node": ">= 8.10.0" 278 | }, 279 | "optionalDependencies": { 280 | "fsevents": "~2.3.2" 281 | } 282 | }, 283 | "node_modules/combined-stream": { 284 | "version": "1.0.8", 285 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 286 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 287 | "dependencies": { 288 | "delayed-stream": "~1.0.0" 289 | }, 290 | "engines": { 291 | "node": ">= 0.8" 292 | } 293 | }, 294 | "node_modules/concat-map": { 295 | "version": "0.0.1", 296 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 297 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 298 | "dev": true 299 | }, 300 | "node_modules/create-require": { 301 | "version": "1.1.1", 302 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 303 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 304 | "dev": true 305 | }, 306 | "node_modules/dataloader": { 307 | "version": "2.2.2", 308 | "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", 309 | "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==" 310 | }, 311 | "node_modules/debug": { 312 | "version": "3.2.7", 313 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 314 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 315 | "dev": true, 316 | "dependencies": { 317 | "ms": "^2.1.1" 318 | } 319 | }, 320 | "node_modules/delayed-stream": { 321 | "version": "1.0.0", 322 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 323 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 324 | "engines": { 325 | "node": ">=0.4.0" 326 | } 327 | }, 328 | "node_modules/diff": { 329 | "version": "4.0.2", 330 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 331 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 332 | "dev": true, 333 | "engines": { 334 | "node": ">=0.3.1" 335 | } 336 | }, 337 | "node_modules/fill-range": { 338 | "version": "7.0.1", 339 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 340 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 341 | "dev": true, 342 | "dependencies": { 343 | "to-regex-range": "^5.0.1" 344 | }, 345 | "engines": { 346 | "node": ">=8" 347 | } 348 | }, 349 | "node_modules/follow-redirects": { 350 | "version": "1.15.6", 351 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 352 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", 353 | "funding": [ 354 | { 355 | "type": "individual", 356 | "url": "https://github.com/sponsors/RubenVerborgh" 357 | } 358 | ], 359 | "engines": { 360 | "node": ">=4.0" 361 | }, 362 | "peerDependenciesMeta": { 363 | "debug": { 364 | "optional": true 365 | } 366 | } 367 | }, 368 | "node_modules/form-data": { 369 | "version": "4.0.0", 370 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 371 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 372 | "dependencies": { 373 | "asynckit": "^0.4.0", 374 | "combined-stream": "^1.0.8", 375 | "mime-types": "^2.1.12" 376 | }, 377 | "engines": { 378 | "node": ">= 6" 379 | } 380 | }, 381 | "node_modules/fsevents": { 382 | "version": "2.3.2", 383 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 384 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 385 | "dev": true, 386 | "hasInstallScript": true, 387 | "optional": true, 388 | "os": [ 389 | "darwin" 390 | ], 391 | "engines": { 392 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 393 | } 394 | }, 395 | "node_modules/glob-parent": { 396 | "version": "5.1.2", 397 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 398 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 399 | "dev": true, 400 | "dependencies": { 401 | "is-glob": "^4.0.1" 402 | }, 403 | "engines": { 404 | "node": ">= 6" 405 | } 406 | }, 407 | "node_modules/has-flag": { 408 | "version": "3.0.0", 409 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 410 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 411 | "dev": true, 412 | "engines": { 413 | "node": ">=4" 414 | } 415 | }, 416 | "node_modules/ignore-by-default": { 417 | "version": "1.0.1", 418 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 419 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 420 | "dev": true 421 | }, 422 | "node_modules/is-binary-path": { 423 | "version": "2.1.0", 424 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 425 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 426 | "dev": true, 427 | "dependencies": { 428 | "binary-extensions": "^2.0.0" 429 | }, 430 | "engines": { 431 | "node": ">=8" 432 | } 433 | }, 434 | "node_modules/is-extglob": { 435 | "version": "2.1.1", 436 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 437 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 438 | "dev": true, 439 | "engines": { 440 | "node": ">=0.10.0" 441 | } 442 | }, 443 | "node_modules/is-glob": { 444 | "version": "4.0.3", 445 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 446 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 447 | "dev": true, 448 | "dependencies": { 449 | "is-extglob": "^2.1.1" 450 | }, 451 | "engines": { 452 | "node": ">=0.10.0" 453 | } 454 | }, 455 | "node_modules/is-number": { 456 | "version": "7.0.0", 457 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 458 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 459 | "dev": true, 460 | "engines": { 461 | "node": ">=0.12.0" 462 | } 463 | }, 464 | "node_modules/jssha": { 465 | "version": "3.2.0", 466 | "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", 467 | "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==", 468 | "engines": { 469 | "node": "*" 470 | } 471 | }, 472 | "node_modules/lru-cache": { 473 | "version": "6.0.0", 474 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 475 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 476 | "dev": true, 477 | "dependencies": { 478 | "yallist": "^4.0.0" 479 | }, 480 | "engines": { 481 | "node": ">=10" 482 | } 483 | }, 484 | "node_modules/make-error": { 485 | "version": "1.3.6", 486 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 487 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 488 | "dev": true 489 | }, 490 | "node_modules/mime-db": { 491 | "version": "1.52.0", 492 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 493 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 494 | "engines": { 495 | "node": ">= 0.6" 496 | } 497 | }, 498 | "node_modules/mime-types": { 499 | "version": "2.1.35", 500 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 501 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 502 | "dependencies": { 503 | "mime-db": "1.52.0" 504 | }, 505 | "engines": { 506 | "node": ">= 0.6" 507 | } 508 | }, 509 | "node_modules/minimatch": { 510 | "version": "3.1.2", 511 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 512 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 513 | "dev": true, 514 | "dependencies": { 515 | "brace-expansion": "^1.1.7" 516 | }, 517 | "engines": { 518 | "node": "*" 519 | } 520 | }, 521 | "node_modules/ms": { 522 | "version": "2.1.3", 523 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 524 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 525 | "dev": true 526 | }, 527 | "node_modules/nodemon": { 528 | "version": "3.0.1", 529 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", 530 | "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", 531 | "dev": true, 532 | "dependencies": { 533 | "chokidar": "^3.5.2", 534 | "debug": "^3.2.7", 535 | "ignore-by-default": "^1.0.1", 536 | "minimatch": "^3.1.2", 537 | "pstree.remy": "^1.1.8", 538 | "semver": "^7.5.3", 539 | "simple-update-notifier": "^2.0.0", 540 | "supports-color": "^5.5.0", 541 | "touch": "^3.1.0", 542 | "undefsafe": "^2.0.5" 543 | }, 544 | "bin": { 545 | "nodemon": "bin/nodemon.js" 546 | }, 547 | "engines": { 548 | "node": ">=10" 549 | }, 550 | "funding": { 551 | "type": "opencollective", 552 | "url": "https://opencollective.com/nodemon" 553 | } 554 | }, 555 | "node_modules/nopt": { 556 | "version": "1.0.10", 557 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 558 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 559 | "dev": true, 560 | "dependencies": { 561 | "abbrev": "1" 562 | }, 563 | "bin": { 564 | "nopt": "bin/nopt.js" 565 | }, 566 | "engines": { 567 | "node": "*" 568 | } 569 | }, 570 | "node_modules/normalize-path": { 571 | "version": "3.0.0", 572 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 573 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 574 | "dev": true, 575 | "engines": { 576 | "node": ">=0.10.0" 577 | } 578 | }, 579 | "node_modules/picomatch": { 580 | "version": "2.3.1", 581 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 582 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 583 | "dev": true, 584 | "engines": { 585 | "node": ">=8.6" 586 | }, 587 | "funding": { 588 | "url": "https://github.com/sponsors/jonschlinkert" 589 | } 590 | }, 591 | "node_modules/proxy-from-env": { 592 | "version": "1.1.0", 593 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 594 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 595 | }, 596 | "node_modules/pstree.remy": { 597 | "version": "1.1.8", 598 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 599 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 600 | "dev": true 601 | }, 602 | "node_modules/readdirp": { 603 | "version": "3.6.0", 604 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 605 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 606 | "dev": true, 607 | "dependencies": { 608 | "picomatch": "^2.2.1" 609 | }, 610 | "engines": { 611 | "node": ">=8.10.0" 612 | } 613 | }, 614 | "node_modules/semver": { 615 | "version": "7.5.4", 616 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", 617 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", 618 | "dev": true, 619 | "dependencies": { 620 | "lru-cache": "^6.0.0" 621 | }, 622 | "bin": { 623 | "semver": "bin/semver.js" 624 | }, 625 | "engines": { 626 | "node": ">=10" 627 | } 628 | }, 629 | "node_modules/simple-update-notifier": { 630 | "version": "2.0.0", 631 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", 632 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", 633 | "dev": true, 634 | "dependencies": { 635 | "semver": "^7.5.3" 636 | }, 637 | "engines": { 638 | "node": ">=10" 639 | } 640 | }, 641 | "node_modules/supports-color": { 642 | "version": "5.5.0", 643 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 644 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 645 | "dev": true, 646 | "dependencies": { 647 | "has-flag": "^3.0.0" 648 | }, 649 | "engines": { 650 | "node": ">=4" 651 | } 652 | }, 653 | "node_modules/symbol.inspect": { 654 | "version": "1.0.1", 655 | "resolved": "https://registry.npmjs.org/symbol.inspect/-/symbol.inspect-1.0.1.tgz", 656 | "integrity": "sha512-YQSL4duoHmLhsTD1Pw8RW6TZ5MaTX5rXJnqacJottr2P2LZBF/Yvrc3ku4NUpMOm8aM0KOCqM+UAkMA5HWQCzQ==" 657 | }, 658 | "node_modules/teslabot": { 659 | "version": "1.5.0", 660 | "resolved": "https://registry.npmjs.org/teslabot/-/teslabot-1.5.0.tgz", 661 | "integrity": "sha512-e2MmELhCgrgZEGo7PQu/6bmYG36IDH+YrBI1iGm6jovXkeDIGa3pZ2WSqRjzkuw2vt1EqfkZoV5GpXgqL8QJVg==" 662 | }, 663 | "node_modules/to-regex-range": { 664 | "version": "5.0.1", 665 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 666 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 667 | "dev": true, 668 | "dependencies": { 669 | "is-number": "^7.0.0" 670 | }, 671 | "engines": { 672 | "node": ">=8.0" 673 | } 674 | }, 675 | "node_modules/touch": { 676 | "version": "3.1.0", 677 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 678 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 679 | "dev": true, 680 | "dependencies": { 681 | "nopt": "~1.0.10" 682 | }, 683 | "bin": { 684 | "nodetouch": "bin/nodetouch.js" 685 | } 686 | }, 687 | "node_modules/ts-node": { 688 | "version": "10.9.1", 689 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 690 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 691 | "dev": true, 692 | "dependencies": { 693 | "@cspotcode/source-map-support": "^0.8.0", 694 | "@tsconfig/node10": "^1.0.7", 695 | "@tsconfig/node12": "^1.0.7", 696 | "@tsconfig/node14": "^1.0.0", 697 | "@tsconfig/node16": "^1.0.2", 698 | "acorn": "^8.4.1", 699 | "acorn-walk": "^8.1.1", 700 | "arg": "^4.1.0", 701 | "create-require": "^1.1.0", 702 | "diff": "^4.0.1", 703 | "make-error": "^1.1.1", 704 | "v8-compile-cache-lib": "^3.0.1", 705 | "yn": "3.1.1" 706 | }, 707 | "bin": { 708 | "ts-node": "dist/bin.js", 709 | "ts-node-cwd": "dist/bin-cwd.js", 710 | "ts-node-esm": "dist/bin-esm.js", 711 | "ts-node-script": "dist/bin-script.js", 712 | "ts-node-transpile-only": "dist/bin-transpile.js", 713 | "ts-script": "dist/bin-script-deprecated.js" 714 | }, 715 | "peerDependencies": { 716 | "@swc/core": ">=1.2.50", 717 | "@swc/wasm": ">=1.2.50", 718 | "@types/node": "*", 719 | "typescript": ">=2.7" 720 | }, 721 | "peerDependenciesMeta": { 722 | "@swc/core": { 723 | "optional": true 724 | }, 725 | "@swc/wasm": { 726 | "optional": true 727 | } 728 | } 729 | }, 730 | "node_modules/tweetnacl": { 731 | "version": "1.0.3", 732 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", 733 | "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" 734 | }, 735 | "node_modules/typescript": { 736 | "version": "5.1.6", 737 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", 738 | "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", 739 | "dev": true, 740 | "bin": { 741 | "tsc": "bin/tsc", 742 | "tsserver": "bin/tsserver" 743 | }, 744 | "engines": { 745 | "node": ">=14.17" 746 | } 747 | }, 748 | "node_modules/undefsafe": { 749 | "version": "2.0.5", 750 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 751 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 752 | "dev": true 753 | }, 754 | "node_modules/v8-compile-cache-lib": { 755 | "version": "3.0.1", 756 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 757 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 758 | "dev": true 759 | }, 760 | "node_modules/yallist": { 761 | "version": "4.0.0", 762 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 763 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 764 | "dev": true 765 | }, 766 | "node_modules/yn": { 767 | "version": "3.1.1", 768 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 769 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 770 | "dev": true, 771 | "engines": { 772 | "node": ">=6" 773 | } 774 | }, 775 | "node_modules/zod": { 776 | "version": "3.21.4", 777 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", 778 | "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", 779 | "funding": { 780 | "url": "https://github.com/sponsors/colinhacks" 781 | } 782 | } 783 | } 784 | } 785 | -------------------------------------------------------------------------------- /JavaScript - TypeScript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial-code", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start:dev": "npx nodemon" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/node": "^20.4.6", 14 | "nodemon": "^3.0.1", 15 | "ts-node": "^10.9.1", 16 | "typescript": "^5.1.6" 17 | }, 18 | "dependencies": { 19 | "@ton-community/func-js": "^0.7.0", 20 | "@ton/core": "^0.56.3", 21 | "@ton/crypto": "^3.2.0", 22 | "@ton/ton": "^13.11.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 2/External and Internal transactions.ts: -------------------------------------------------------------------------------- 1 | import { Address, beginCell, toNano } from "@ton/core"; 2 | import { mnemonicToWalletKey, sign } from "@ton/crypto"; 3 | import { TonClient } from "@ton/ton"; 4 | 5 | async function main() { 6 | let internalMessageBody = beginCell() 7 | .storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow 8 | .storeStringTail("Hello, TON!") // write our text comment 9 | .endCell(); 10 | 11 | const walletAddress = Address.parse('put your wallet address'); 12 | 13 | let internalMessage = beginCell() 14 | .storeUint(0, 1) // indicate that it is an internal message -> int_msg_info$0 15 | .storeBit(1) // IHR Disabled 16 | .storeBit(1) // bounce 17 | .storeBit(0) // bounced 18 | .storeUint(0, 2) // src -> addr_none 19 | .storeAddress(walletAddress) 20 | .storeCoins(toNano("0.2")) // amount 21 | .storeBit(0) // Extra currency 22 | .storeCoins(0) // IHR Fee 23 | .storeCoins(0) // Forwarding Fee 24 | .storeUint(0, 64) // Logical time of creation 25 | .storeUint(0, 32) // UNIX time of creation 26 | .storeBit(0) // No State Init 27 | .storeBit(1) // We store Message Body as a reference 28 | .storeRef(internalMessageBody) // Store Message Body as a reference 29 | .endCell(); 30 | 31 | const client = new TonClient({ 32 | endpoint: "https://toncenter.com/api/v2/jsonRPC", 33 | apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram 34 | }); 35 | 36 | const mnemonic = 'put your mnemonic'; // word1 word2 word3 37 | let getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract 38 | let seqno = getMethodResult.stack.readNumber(); // get seqno from response 39 | 40 | const mnemonicArray = mnemonic.split(' '); // get array from string 41 | const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic 42 | 43 | let toSign = beginCell() 44 | .storeUint(698983191, 32) // subwallet_id | We consider this further 45 | .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute 46 | .storeUint(seqno, 32) // store seqno 47 | .storeUint(3, 8) // store mode of our internal message 48 | .storeRef(internalMessage); // store our internalMessage as a reference 49 | 50 | let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature 51 | 52 | let body = beginCell() 53 | .storeBuffer(signature) // store signature 54 | .storeBuilder(toSign) // store our message 55 | .endCell(); 56 | 57 | let externalMessage = beginCell() 58 | .storeUint(0b10, 2) // 0b10 -> 10 in binary 59 | .storeUint(0, 2) // src -> addr_none 60 | .storeAddress(walletAddress) // Destination address 61 | .storeCoins(0) // Import Fee 62 | .storeBit(0) // No State Init 63 | .storeBit(1) // We store Message Body as a reference 64 | .storeRef(body) // Store Message Body as a reference 65 | .endCell(); 66 | 67 | console.log(externalMessage.toBoc().toString("base64")) 68 | 69 | client.sendFile(externalMessage.toBoc()); 70 | } 71 | 72 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 3/Deploying our wallet.ts: -------------------------------------------------------------------------------- 1 | import { compileFunc } from "@ton-community/func-js"; 2 | import { Address, Cell, beginCell, toNano } from "@ton/core"; 3 | import { mnemonicNew, mnemonicToWalletKey, sign } from "@ton/crypto"; 4 | import { TonClient } from "@ton/ton"; 5 | import fs from 'fs'; // we use fs for reading content of files 6 | 7 | async function main() { 8 | // const mnemonicArray = 'put your mnemonic'.split(' ') // get our mnemonic as array 9 | const mnemonicArray = await mnemonicNew(24); // 24 is the number of words in a seed phrase 10 | const keyPair = await mnemonicToWalletKey(mnemonicArray); // extract private and public keys from mnemonic 11 | console.log(mnemonicArray) // if we want, we can print our mnemonic 12 | 13 | const subWallet = 698983191; 14 | 15 | const result = await compileFunc({ 16 | targets: ['wallet_v3.fc'], // targets of your project 17 | sources: { 18 | "stdlib.fc": fs.readFileSync('./src/stdlib.fc', { encoding: 'utf-8' }), 19 | "wallet_v3.fc": fs.readFileSync('./src/wallet_v3.fc', { encoding: 'utf-8' }), 20 | } 21 | }); 22 | 23 | if (result.status === 'error') { 24 | console.error(result.message) 25 | return; 26 | } 27 | 28 | const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, "base64"))[0]; // get buffer from base64 encoded BOC and get cell from this buffer 29 | 30 | // now we have base64 encoded BOC with compiled code in result.codeBoc 31 | console.log('Code BOC: ' + result.codeBoc); 32 | console.log('\nHash: ' + codeCell.hash().toString('base64')); // get the hash of cell and convert in to base64 encoded string. We will need it further 33 | 34 | const dataCell = beginCell() 35 | .storeUint(0, 32) // Seqno 36 | .storeUint(698983191, 32) // Subwallet ID 37 | .storeBuffer(keyPair.publicKey) // Public Key 38 | .endCell(); 39 | 40 | const stateInit = beginCell() 41 | .storeBit(0) // No split_depth 42 | .storeBit(0) // No special 43 | .storeBit(1) // We have code 44 | .storeRef(codeCell) 45 | .storeBit(1) // We have data 46 | .storeRef(dataCell) 47 | .storeBit(0) // No library 48 | .endCell(); 49 | 50 | const contractAddress = new Address(0, stateInit.hash()); // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 51 | console.log(`Contract address: ${contractAddress.toString()}`); // Output contract address to console 52 | 53 | const internalMessageBody = beginCell() 54 | .storeUint(0, 32) 55 | .storeStringTail("Hello, TON!") 56 | .endCell(); 57 | 58 | const internalMessage = beginCell() 59 | .storeUint(0x10, 6) // no bounce 60 | .storeAddress(Address.parse("put your first wallet address from were you sent 0.1 TON")) 61 | .storeCoins(toNano("0.03")) 62 | .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1 that means we have body as a reference 63 | .storeRef(internalMessageBody) 64 | .endCell(); 65 | 66 | // message for our wallet 67 | const toSign = beginCell() 68 | .storeUint(subWallet, 32) 69 | .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) 70 | .storeUint(0, 32) // We put seqno = 0, because after deploying wallet will store 0 as seqno 71 | .storeUint(3, 8) 72 | .storeRef(internalMessage); 73 | 74 | const signature = sign(toSign.endCell().hash(), keyPair.secretKey); 75 | const body = beginCell() 76 | .storeBuffer(signature) 77 | .storeBuilder(toSign) 78 | .endCell(); 79 | 80 | const externalMessage = beginCell() 81 | .storeUint(0b10, 2) // indicate that it is an incoming external message 82 | .storeUint(0, 2) // src -> addr_none 83 | .storeAddress(contractAddress) 84 | .storeCoins(0) // Import fee 85 | .storeBit(1) // We have State Init 86 | .storeBit(1) // We store State Init as a reference 87 | .storeRef(stateInit) // Store State Init as a reference 88 | .storeBit(1) // We store Message Body as a reference 89 | .storeRef(body) // Store Message Body as a reference 90 | .endCell(); 91 | 92 | const client = new TonClient({ 93 | endpoint: "https://toncenter.com/api/v2/jsonRPC", 94 | apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram 95 | }); 96 | 97 | client.sendFile(externalMessage.toBoc()); 98 | } 99 | 100 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 4/Contract deploy via wallet.ts: -------------------------------------------------------------------------------- 1 | import { Address, Cell, beginCell, toNano } from "@ton/core"; 2 | import { mnemonicToWalletKey, sign } from "@ton/crypto"; 3 | import { TonClient } from "@ton/ton"; 4 | 5 | 6 | async function main() { 7 | const mnemonicArray = 'put your mnemonic'.split(" "); 8 | const keyPair = await mnemonicToWalletKey(mnemonicArray); // extract private and public keys from mnemonic 9 | 10 | const codeCell = Cell.fromBase64('te6ccgEBCAEAhgABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQCW8oMI1xgg0x/TH9MfAvgju/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj+ACTINdKltMH1AL7AOgwAaTIyx/LH8v/ye1UAATQMAIBSAYHABe7Oc7UTQ0z8x1wv/gAEbjJftRNDXCx+A=='); 11 | const dataCell = beginCell() 12 | .storeUint(0, 32) // Seqno 13 | .storeUint(3, 32) // Subwallet ID 14 | .storeBuffer(keyPair.publicKey) // Public Key 15 | .endCell(); 16 | 17 | const stateInit = beginCell() 18 | .storeBit(0) // No split_depth 19 | .storeBit(0) // No special 20 | .storeBit(1) // We have code 21 | .storeRef(codeCell) 22 | .storeBit(1) // We have data 23 | .storeRef(dataCell) 24 | .storeBit(0) // No library 25 | .endCell(); 26 | 27 | const contractAddress = new Address(0, stateInit.hash()); // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 28 | console.log(`Contract address: ${contractAddress.toString()}`); // Output contract address to console 29 | 30 | const internalMessageBody = beginCell() 31 | .storeUint(0, 32) 32 | .storeStringTail('Deploying...') 33 | .endCell(); 34 | 35 | const internalMessage = beginCell() 36 | .storeUint(0x10, 6) // no bounce 37 | .storeAddress(contractAddress) 38 | .storeCoins(toNano('0.01')) 39 | .storeUint(0, 1 + 4 + 4 + 64 + 32) 40 | .storeBit(1) // We have State Init 41 | .storeBit(1) // We store State Init as a reference 42 | .storeRef(stateInit) // Store State Init as a reference 43 | .storeBit(1) // We store Message Body as a reference 44 | .storeRef(internalMessageBody) // Store Message Body Init as a reference 45 | .endCell(); 46 | 47 | const client = new TonClient({ 48 | endpoint: 'https://toncenter.com/api/v2/jsonRPC', 49 | apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram 50 | }); 51 | 52 | const walletMnemonicArray = 'put your mnemonic'.split(' '); 53 | const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic 54 | const walletAddress = Address.parse('put your wallet address with which you will deploy'); 55 | const getMethodResult = await client.runMethod(walletAddress, 'seqno'); // run "seqno" GET method from your wallet contract 56 | const seqno = getMethodResult.stack.readNumber(); // get seqno from response 57 | 58 | // message for our wallet 59 | const toSign = beginCell() 60 | .storeUint(698983191, 32) // subwallet_id 61 | .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute 62 | .storeUint(seqno, 32) // store seqno 63 | // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) 64 | .storeUint(3, 8) 65 | .storeRef(internalMessage); 66 | 67 | const signature = sign(toSign.endCell().hash(), walletKeyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature 68 | const body = beginCell() 69 | .storeBuffer(signature) // store signature 70 | .storeBuilder(toSign) // store our message 71 | .endCell(); 72 | 73 | const external = beginCell() 74 | .storeUint(0b10, 2) // indicate that it is an incoming external message 75 | .storeUint(0, 2) // src -> addr_none 76 | .storeAddress(walletAddress) 77 | .storeCoins(0) // Import fee 78 | .storeBit(0) // We do not have State Init 79 | .storeBit(1) // We store Message Body as a reference 80 | .storeRef(body) // Store Message Body as a reference 81 | .endCell(); 82 | 83 | console.log(external.toBoc().toString('base64')); 84 | client.sendFile(external.toBoc()); 85 | } 86 | 87 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 4/Get methods in Wallet V3 and Wallet v4.ts: -------------------------------------------------------------------------------- 1 | import { TonClient } from '@ton/ton'; 2 | import { Address } from '@ton/core'; 3 | 4 | async function main() { 5 | const client = new TonClient({ 6 | endpoint: "https://toncenter.com/api/v2/jsonRPC", 7 | apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram 8 | }); 9 | 10 | const walletAddress = Address.parse("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"); // my wallet address as an example 11 | 12 | // I always call runMethodWithError instead of runMethod to be able to check the exit_code of the called method. 13 | let getResult = await client.runMethodWithError(walletAddress, "get_public_key"); // run get_public_key GET Method 14 | const publicKeyUInt = getResult.stack.readBigNumber(); // read answer that contains uint256 15 | const publicKey = publicKeyUInt.toString(16); // get hex string from bigint (uint256) 16 | console.log(publicKey); 17 | 18 | const oldWalletAddress = Address.parse("EQAM7M--HGyfxlErAIUODrxBA3yj5roBeYiTuy6BHgJ3Sx8k"); // my old wallet address 19 | const subscriptionAddress = Address.parseFriendly("EQBTKTis-SWYdupy99ozeOvnEBu8LRrQP_N9qwOTSAy3sQSZ"); // subscription plugin address which is already installed on the wallet 20 | const hash = BigInt(`0x${subscriptionAddress.address.hash.toString("hex")}`) ; 21 | 22 | getResult = await client.runMethodWithError(oldWalletAddress, "is_plugin_installed", 23 | [ 24 | {type: "int", value: BigInt("0")}, // pass workchain as int 25 | {type: "int", value: hash} // pass plugin address hash as int 26 | ]); 27 | console.log(getResult.stack.readNumber()); 28 | } 29 | 30 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 4/NFT Transfer.ts: -------------------------------------------------------------------------------- 1 | import { Address, beginCell, toNano } from "@ton/core"; 2 | import { mnemonicToWalletKey, sign } from "@ton/crypto"; 3 | import { TonClient } from "@ton/ton"; 4 | 5 | 6 | async function main() { 7 | const destinationAddress = Address.parse("put your wallet where you want to send NFT"); 8 | const walletAddress = Address.parse("put your wallet which is the owner of NFT") 9 | const nftAddress = Address.parse("put your nft address"); 10 | 11 | // We can add a comment, but it will not be displayed in the explorers, 12 | // as it is not supported by them at the time of writing the tutorial. 13 | const forwardPayload = beginCell() 14 | .storeUint(0, 32) 15 | .storeStringTail("Hello, TON!") 16 | .endCell(); 17 | 18 | const transferNftBody = beginCell() 19 | .storeUint(0x5fcc3d14, 32) // Opcode for NFT transfer 20 | .storeUint(0, 64) // query_id 21 | .storeAddress(destinationAddress) // new_owner 22 | .storeAddress(walletAddress) // response_destination for excesses 23 | .storeBit(0) // we do not have custom_payload 24 | .storeCoins(toNano("0.01")) // forward_payload 25 | .storeBit(1) // we store forward_payload as a reference 26 | .storeRef(forwardPayload) // store forward_payload as a reference 27 | .endCell(); 28 | 29 | const internalMessage = beginCell() 30 | .storeUint(0x18, 6) // bounce 31 | .storeAddress(nftAddress) 32 | .storeCoins(toNano("0.05")) 33 | .storeUint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1) // We store 1 that means we have body as a reference 34 | .storeRef(transferNftBody) 35 | .endCell(); 36 | 37 | const client = new TonClient({ 38 | endpoint: "https://toncenter.com/api/v2/jsonRPC", 39 | apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram 40 | }); 41 | 42 | const mnemonic = 'put your mnemonic'; // word1 word2 word3 43 | let getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract 44 | let seqno = getMethodResult.stack.readNumber(); // get seqno from response 45 | 46 | const mnemonicArray = mnemonic.split(' '); // get array from string 47 | const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic 48 | 49 | let toSign = beginCell() 50 | .storeUint(698983191, 32) // subwallet_id 51 | .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute 52 | .storeUint(seqno, 32) // store seqno 53 | // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) 54 | .storeUint(3, 8) 55 | .storeRef(internalMessage); 56 | 57 | let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature 58 | 59 | let body = beginCell() 60 | .storeBuffer(signature) // store signature 61 | .storeBuilder(toSign) // store our message 62 | .endCell(); 63 | 64 | let externalMessage = beginCell() 65 | .storeUint(0b10, 2) // ext_in_msg_info$10 66 | .storeUint(0, 2) // src -> addr_none 67 | .storeAddress(walletAddress) // Destination address 68 | .storeCoins(0) // Import Fee 69 | .storeBit(0) // No State Init 70 | .storeBit(1) // We store Message Body as a reference 71 | .storeRef(body) // Store Message Body as a reference 72 | .endCell(); 73 | 74 | client.sendFile(externalMessage.toBoc()); 75 | } 76 | 77 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 4/Sending multiple messages simultaneously.ts: -------------------------------------------------------------------------------- 1 | import { Address, Cell, beginCell, toNano } from "@ton/core"; 2 | import { mnemonicToWalletKey, sign } from "@ton/crypto"; 3 | import { TonClient } from "@ton/ton"; 4 | 5 | 6 | async function main() { 7 | const internalMessagesAmount = ["0.01", "0.02", "0.03", "0.04"]; 8 | const internalMessagesComment = [ 9 | "Hello, TON! #1", 10 | "Hello, TON! #2", 11 | "", // Let's leave the third message without comment 12 | "Hello, TON! #4" 13 | ] 14 | const destinationAddresses = [ 15 | "Put any address that belongs to you", 16 | "Put any address that belongs to you", 17 | "Put any address that belongs to you", 18 | "Put any address that belongs to you" 19 | ] // All 4 addresses can be the same 20 | 21 | let internalMessages:Cell[] = []; // array for our internal messages 22 | 23 | for (let index = 0; index < internalMessagesAmount.length; index++) { 24 | const amount = internalMessagesAmount[index]; 25 | 26 | let internalMessage = beginCell() 27 | .storeUint(0x18, 6) // bounce 28 | .storeAddress(Address.parse(destinationAddresses[index])) 29 | .storeCoins(toNano(amount)) 30 | .storeUint(0, 1 + 4 + 4 + 64 + 32 + 1); 31 | 32 | /* 33 | At this stage, it is not clear if we will have a message body. 34 | So put a bit only for stateInit, and if we have a comment, in means 35 | we have a body message. In that case, set the bit to 1 and store the 36 | body as a reference. 37 | */ 38 | 39 | if(internalMessagesComment[index] != "") { 40 | internalMessage.storeBit(1) // we store Message Body as a reference 41 | 42 | let internalMessageBody = beginCell() 43 | .storeUint(0, 32) 44 | .storeStringTail(internalMessagesComment[index]) 45 | .endCell(); 46 | 47 | internalMessage.storeRef(internalMessageBody); 48 | } 49 | else 50 | /* 51 | Since we do not have a message body, we indicate that 52 | the message body is in this message, but do not write it, 53 | which means it is absent. In that case, just set the bit to 0. 54 | */ 55 | internalMessage.storeBit(0); 56 | 57 | internalMessages.push(internalMessage.endCell()); 58 | } 59 | 60 | const walletAddress = Address.parse('put your wallet address'); 61 | const client = new TonClient({ 62 | endpoint: "https://toncenter.com/api/v2/jsonRPC", 63 | apiKey: "put your api key" // you can get an api key from @tonapibot bot in Telegram 64 | }); 65 | 66 | const mnemonic = 'put your mnemonic'; // word1 word2 word3 67 | let getMethodResult = await client.runMethod(walletAddress, "seqno"); // run "seqno" GET method from your wallet contract 68 | let seqno = getMethodResult.stack.readNumber(); // get seqno from response 69 | 70 | const mnemonicArray = mnemonic.split(' '); // get array from string 71 | const keyPair = await mnemonicToWalletKey(mnemonicArray); // get Secret and Public keys from mnemonic 72 | 73 | let toSign = beginCell() 74 | .storeUint(698983191, 32) // subwallet_id 75 | .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute 76 | .storeUint(seqno, 32); // store seqno 77 | // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) 78 | 79 | for (let index = 0; index < internalMessages.length; index++) { 80 | const internalMessage = internalMessages[index]; 81 | toSign.storeUint(3, 8) // store mode of our internal message 82 | toSign.storeRef(internalMessage) // store our internalMessage as a reference 83 | } 84 | 85 | let signature = sign(toSign.endCell().hash(), keyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature 86 | 87 | let body = beginCell() 88 | .storeBuffer(signature) // store signature 89 | .storeBuilder(toSign) // store our message 90 | .endCell(); 91 | 92 | let externalMessage = beginCell() 93 | .storeUint(0b10, 2) // ext_in_msg_info$10 94 | .storeUint(0, 2) // src -> addr_none 95 | .storeAddress(walletAddress) // Destination address 96 | .storeCoins(0) // Import Fee 97 | .storeBit(0) // No State Init 98 | .storeBit(1) // We store Message Body as a reference 99 | .storeRef(body) // Store Message Body as a reference 100 | .endCell(); 101 | 102 | client.sendFile(externalMessage.toBoc()); 103 | } 104 | 105 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 5/Deploying high-load wallet.ts: -------------------------------------------------------------------------------- 1 | import { compileFunc } from "@ton-community/func-js"; 2 | import { Address, Cell, beginCell, toNano } from "@ton/core"; 3 | import { mnemonicToWalletKey, sign } from "@ton/crypto"; 4 | import { TonClient } from "@ton/ton"; 5 | import fs from 'fs' 6 | 7 | async function main() { 8 | const result = await compileFunc({ 9 | targets: ['highload_wallet.fc'], // targets of your project 10 | sources: { 11 | 'stdlib.fc': fs.readFileSync('./src/stdlib.fc', { encoding: 'utf-8' }), 12 | 'highload_wallet.fc': fs.readFileSync('./src/highload_wallet.fc', { encoding: 'utf-8' }), 13 | } 14 | }); 15 | 16 | if (result.status === 'error') { 17 | console.error(result.message) 18 | return; 19 | } 20 | 21 | const codeCell = Cell.fromBoc(Buffer.from(result.codeBoc, 'base64'))[0]; 22 | 23 | // now we have base64 encoded BOC with compiled code in result.codeBoc 24 | console.log('Code BOC: ' + result.codeBoc); 25 | console.log('\nHash: ' + codeCell.hash().toString('base64')); // get the hash of cell and convert in to base64 encoded string 26 | 27 | const highloadMnemonicArray = 'put your mnemonic that you have generated and saved before'.split(' '); 28 | const highloadKeyPair = await mnemonicToWalletKey(highloadMnemonicArray); // extract private and public keys from mnemonic 29 | 30 | const dataCell = beginCell() 31 | .storeUint(698983191, 32) // Subwallet ID 32 | .storeUint(0, 64) // Last cleaned 33 | .storeBuffer(highloadKeyPair.publicKey) // Public Key 34 | .storeBit(0) // indicate that the dictionary is empty 35 | .endCell(); 36 | 37 | const stateInit = beginCell() 38 | .storeBit(0) // No split_depth 39 | .storeBit(0) // No special 40 | .storeBit(1) // We have code 41 | .storeRef(codeCell) 42 | .storeBit(1) // We have data 43 | .storeRef(dataCell) 44 | .storeBit(0) // No library 45 | .endCell(); 46 | 47 | const contractAddress = new Address(0, stateInit.hash()); // get the hash of stateInit to get the address of our smart contract in workchain with ID 0 48 | console.log(`Contract address: ${contractAddress.toString()}`); // Output contract address to console 49 | 50 | const internalMessageBody = beginCell() 51 | .storeUint(0, 32) 52 | .storeStringTail('Deploying...') 53 | .endCell(); 54 | 55 | const internalMessage = beginCell() 56 | .storeUint(0x10, 6) // no bounce 57 | .storeAddress(contractAddress) 58 | .storeCoins(toNano('0.01')) 59 | .storeUint(0, 1 + 4 + 4 + 64 + 32) 60 | .storeBit(1) // We have State Init 61 | .storeBit(1) // We store State Init as a reference 62 | .storeRef(stateInit) // Store State Init as a reference 63 | .storeBit(1) // We store Message Body as a reference 64 | .storeRef(internalMessageBody) // Store Message Body Init as a reference 65 | .endCell(); 66 | 67 | 68 | const client = new TonClient({ 69 | endpoint: 'https://toncenter.com/api/v2/jsonRPC', 70 | apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram 71 | }); 72 | 73 | const walletMnemonicArray = 'put your mnemonic'.split(' '); 74 | const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic 75 | const walletAddress = Address.parse('put your wallet address with which you will deploy'); 76 | const getMethodResult = await client.runMethod(walletAddress, 'seqno'); // run "seqno" GET method from your wallet contract 77 | const seqno = getMethodResult.stack.readNumber(); // get seqno from response 78 | 79 | // message for our wallet 80 | const toSign = beginCell() 81 | .storeUint(698983191, 32) // subwallet_id 82 | .storeUint(Math.floor(Date.now() / 1e3) + 60, 32) // Message expiration time, +60 = 1 minute 83 | .storeUint(seqno, 32) // store seqno 84 | // Do not forget that if we use Wallet V4, we need to add .storeUint(0, 8) 85 | .storeUint(3, 8) 86 | .storeRef(internalMessage); 87 | 88 | const signature = sign(toSign.endCell().hash(), walletKeyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature 89 | const body = beginCell() 90 | .storeBuffer(signature) // store signature 91 | .storeBuilder(toSign) // store our message 92 | .endCell(); 93 | 94 | const external = beginCell() 95 | .storeUint(0b10, 2) // indicate that it is an incoming external message 96 | .storeUint(0, 2) // src -> addr_none 97 | .storeAddress(walletAddress) 98 | .storeCoins(0) // Import fee 99 | .storeBit(0) // We do not have State Init 100 | .storeBit(1) // We store Message Body as a reference 101 | .storeRef(body) // Store Message Body as a reference 102 | .endCell(); 103 | 104 | client.sendFile(external.toBoc()); 105 | } 106 | 107 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 5/Sending transactions from high-load wallet.ts: -------------------------------------------------------------------------------- 1 | import { Address, Cell, Dictionary, beginCell, toNano } from "@ton/core"; 2 | import { mnemonicToWalletKey, sign } from "@ton/crypto"; 3 | import { TonClient } from "@ton/ton"; 4 | import * as crypto from 'crypto'; 5 | 6 | async function main() { 7 | let internalMessages:Cell[] = []; 8 | const walletAddress = Address.parse('put your wallet address from which you deployed high-load wallet'); 9 | 10 | for (let i = 0; i < 12; i++) { 11 | const internalMessageBody = beginCell() 12 | .storeUint(0, 32) 13 | .storeStringTail(`Hello, TON! #${i}`) 14 | .endCell(); 15 | 16 | const internalMessage = beginCell() 17 | .storeUint(0x18, 6) // bounce 18 | .storeAddress(walletAddress) 19 | .storeCoins(toNano('0.01')) 20 | .storeUint(0, 1 + 4 + 4 + 64 + 32) 21 | .storeBit(0) // We do not have State Init 22 | .storeBit(1) // We store Message Body as a reference 23 | .storeRef(internalMessageBody) // Store Message Body Init as a reference 24 | .endCell(); 25 | 26 | internalMessages.push(internalMessage); 27 | } 28 | 29 | const dictionary = Dictionary.empty(); // create an empty dictionary with the key as a number and the value as a cell 30 | for (let i = 0; i < internalMessages.length; i++) { 31 | const internalMessage = internalMessages[i]; // get our message from an array 32 | dictionary.set(i, internalMessage); // save the message in the dictionary 33 | } 34 | 35 | const queryID = crypto.randomBytes(4).readUint32BE(); // create a random uint32 number, 4 bytes = 32 bits 36 | const now = Math.floor(Date.now() / 1000); // get current timestamp 37 | const timeout = 120; // timeout for message expiration, 120 seconds = 2 minutes 38 | const finalQueryID = (BigInt(now + timeout) << 32n) + BigInt(queryID); // get our final query_id 39 | console.log(finalQueryID); // print query_id. With this query_id we can call GET method to check if our request has been processed 40 | 41 | const toSign = beginCell() 42 | .storeUint(698983191, 32) // subwallet_id 43 | .storeUint(finalQueryID, 64) 44 | // Here we create our own method that will save the 45 | // message mode and a reference to the message 46 | .storeDict(dictionary, Dictionary.Keys.Int(16), { 47 | serialize: (src, buidler) => { 48 | buidler.storeUint(3, 8); // save message mode, mode = 3 49 | buidler.storeRef(src); // save message as reference 50 | }, 51 | // We won't actually use this, but this method 52 | // will help to read our dictionary that we saved 53 | parse: (src) => { 54 | let cell = beginCell() 55 | .storeUint(src.loadUint(8), 8) 56 | .storeRef(src.loadRef()) 57 | .endCell(); 58 | return cell; 59 | } 60 | } 61 | ); 62 | 63 | const highloadMnemonicArray = 'put your high-load wallet mnemonic'.split(' '); 64 | const highloadKeyPair = await mnemonicToWalletKey(highloadMnemonicArray); // extract private and public keys from mnemonic 65 | const highloadWalletAddress = Address.parse('put your high-load wallet address'); 66 | 67 | const signature = sign(toSign.endCell().hash(), highloadKeyPair.secretKey); // get the hash of our message to wallet smart contract and sign it to get signature 68 | 69 | const body = beginCell() 70 | .storeBuffer(signature) // store signature 71 | .storeBuilder(toSign) // store our message 72 | .endCell(); 73 | 74 | const externalMessage = beginCell() 75 | .storeUint(0b10, 2) // indicate that it is an incoming external message 76 | .storeUint(0, 2) // src -> addr_none 77 | .storeAddress(highloadWalletAddress) 78 | .storeCoins(0) // Import fee 79 | .storeBit(0) // We do not have State Init 80 | .storeBit(1) // We store Message Body as a reference 81 | .storeRef(body) // Store Message Body as a reference 82 | .endCell(); 83 | 84 | // We do not need a key here as we will be sending 1 request per second 85 | const client = new TonClient({ 86 | endpoint: 'https://toncenter.com/api/v2/jsonRPC', 87 | // apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram 88 | }); 89 | 90 | client.sendFile(externalMessage.toBoc()); 91 | } 92 | 93 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 6/Deploying high-load wallet.ts: -------------------------------------------------------------------------------- 1 | import {Cell, toNano} from "@ton/core"; 2 | import { TonClient, WalletContractV3R2 } from "@ton/ton"; 3 | import { HighloadWalletV3 } from "./wrappers/HighloadWalletV3"; 4 | import { mnemonicToWalletKey } from "@ton/crypto"; 5 | 6 | async function main() { 7 | const HIGHLOAD_V3_CODE = Cell.fromBoc(Buffer.from('b5ee9c7241021001000228000114ff00f4a413f4bcf2c80b01020120020d02014803040078d020d74bc00101c060b0915be101d0d3030171b0915be0fa4030f828c705b39130e0d31f018210ae42e5a4ba9d8040d721d74cf82a01ed55fb04e030020120050a02027306070011adce76a2686b85ffc00201200809001aabb6ed44d0810122d721d70b3f0018aa3bed44d08307d721d70b1f0201200b0c001bb9a6eed44d0810162d721d70b15800e5b8bf2eda2edfb21ab09028409b0ed44d0810120d721f404f404d33fd315d1058e1bf82325a15210b99f326df82305aa0015a112b992306dde923033e2923033e25230800df40f6fa19ed021d721d70a00955f037fdb31e09130e259800df40f6fa19cd001d721d70a00937fdb31e0915be270801f6f2d48308d718d121f900ed44d0d3ffd31ff404f404d33fd315d1f82321a15220b98e12336df82324aa00a112b9926d32de58f82301de541675f910f2a106d0d31fd4d307d30cd309d33fd315d15168baf2a2515abaf2a6f8232aa15250bcf2a304f823bbf2a35304800df40f6fa199d024d721d70a00f2649130e20e01fe5309800df40f6fa18e13d05004d718d20001f264c858cf16cf8301cf168e1030c824cf40cf8384095005a1a514cf40e2f800c94039800df41704c8cbff13cb1ff40012f40012cb3f12cb15c9ed54f80f21d0d30001f265d3020171b0925f03e0fa4001d70b01c000f2a5fa4031fa0031f401fa0031fa00318060d721d300010f0020f265d2000193d431d19130e272b1fb00b585bf03', 'hex'))[0]; 8 | 9 | const client = new TonClient({ 10 | endpoint: 'https://toncenter.com/api/v2/jsonRPC', 11 | apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram 12 | }); 13 | 14 | const walletMnemonicArray = 'put your mnemonic'.split(' '); 15 | const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic 16 | const wallet = client.open(HighloadWalletV3.createFromConfig({ 17 | publicKey: walletKeyPair.publicKey, 18 | subwalletId: 0x10ad, 19 | timeout: 60 * 60, // 1 hour 20 | }, HIGHLOAD_V3_CODE)); 21 | console.log(`Wallet address: ${wallet.address.toString()}`); 22 | 23 | const deployerWalletMnemonicArray = 'put your mnemonic'.split(' '); 24 | const deployerWalletKeyPair = await mnemonicToWalletKey(deployerWalletMnemonicArray); // extract private and public keys from mnemonic 25 | const deployerWallet = client.open(WalletContractV3R2.create({ 26 | publicKey: deployerWalletKeyPair.publicKey, 27 | workchain: 0 28 | })); 29 | console.log(`Deployer wallet address: ${deployerWallet.address.toString()}`); 30 | 31 | await wallet.sendDeploy(deployerWallet.sender(deployerWalletKeyPair.secretKey), toNano(0.05)); 32 | } 33 | 34 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 6/Sending transactions from high-load wallet.ts: -------------------------------------------------------------------------------- 1 | import { Address, beginCell, internal, OutActionSendMsg, SendMode, toNano } from "@ton/core"; 2 | import { TonClient } from "@ton/ton"; 3 | import { HighloadWalletV3 } from "./wrappers/HighloadWalletV3"; 4 | import { mnemonicToWalletKey } from "@ton/crypto"; 5 | import { HighloadQueryId } from "./wrappers/HighloadQueryId"; 6 | 7 | async function main() { 8 | const client = new TonClient({ 9 | endpoint: 'https://toncenter.com/api/v2/jsonRPC', 10 | apiKey: 'put your api key' // you can get an api key from @tonapibot bot in Telegram 11 | }); 12 | 13 | const walletMnemonicArray = 'put your mnemonic'.split(' '); 14 | const walletKeyPair = await mnemonicToWalletKey(walletMnemonicArray); // extract private and public keys from mnemonic 15 | const wallet = client.open(HighloadWalletV3.createFromAddress(Address.parse('put your high-load wallet address'))); 16 | console.log(`Wallet address: ${wallet.address.toString()}`); 17 | 18 | const queryHandler = HighloadQueryId.fromShiftAndBitNumber(0n, 0n); 19 | 20 | const actions: OutActionSendMsg[] = []; 21 | actions.push({ 22 | type: 'sendMsg', 23 | mode: SendMode.CARRY_ALL_REMAINING_BALANCE, 24 | outMsg: internal({ 25 | to: Address.parse('put address of deployer wallet'), 26 | value: toNano(0), 27 | body: beginCell() 28 | .storeUint(0, 32) 29 | .storeStringTail('Hello, TON!') 30 | .endCell() 31 | }) 32 | }); 33 | const subwalletId = 0x10ad; 34 | const timeout = 60 * 60; // must be same as in the contract 35 | const internalMessageValue = toNano(0.01); // in real case it is recommended to set the value to 1 TON 36 | const createdAt = Math.floor(Date.now() / 1000) - 60; // LiteServers have some delay in time 37 | await wallet.sendBatch( 38 | walletKeyPair.secretKey, 39 | actions, 40 | subwalletId, 41 | queryHandler, 42 | timeout, 43 | internalMessageValue, 44 | SendMode.PAY_GAS_SEPARATELY, 45 | createdAt 46 | ); 47 | queryHandler.getNext(); 48 | } 49 | 50 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 6/wrappers/HighloadQueryId.ts: -------------------------------------------------------------------------------- 1 | const BIT_NUMBER_SIZE = 10n; // 10 bit 2 | const SHIFT_SIZE = 13n; // 13 bit 3 | const MAX_BIT_NUMBER = 1022n; 4 | const MAX_SHIFT = 8191n; // 2^13 = 8192 5 | 6 | export class HighloadQueryId { 7 | private shift: bigint; // [0 .. 8191] 8 | private bitnumber: bigint; // [0 .. 1022] 9 | 10 | constructor() { 11 | this.shift = 0n; 12 | this.bitnumber = 0n; 13 | } 14 | 15 | static fromShiftAndBitNumber(shift: bigint, bitnumber: bigint): HighloadQueryId { 16 | const q = new HighloadQueryId(); 17 | q.shift = shift; 18 | if (q.shift < 0) throw new Error('invalid shift'); 19 | if (q.shift > MAX_SHIFT) throw new Error('invalid shift'); 20 | q.bitnumber = bitnumber; 21 | if (q.bitnumber < 0) throw new Error('invalid bitnumber'); 22 | if (q.bitnumber > MAX_BIT_NUMBER) throw new Error('invalid bitnumber'); 23 | return q; 24 | } 25 | 26 | 27 | getNext() { 28 | let newBitnumber = this.bitnumber + 1n; 29 | let newShift = this.shift; 30 | 31 | if (newShift === MAX_SHIFT && newBitnumber > (MAX_BIT_NUMBER - 1n)) { 32 | throw new Error('Overload'); // NOTE: we left one queryId for emergency withdraw 33 | } 34 | 35 | if (newBitnumber > MAX_BIT_NUMBER) { 36 | newBitnumber = 0n; 37 | newShift += 1n; 38 | if (newShift > MAX_SHIFT) { 39 | throw new Error('Overload') 40 | } 41 | } 42 | 43 | return HighloadQueryId.fromShiftAndBitNumber(newShift, newBitnumber); 44 | } 45 | 46 | hasNext() { 47 | const isEnd = this.bitnumber >= (MAX_BIT_NUMBER - 1n) && this.shift === MAX_SHIFT; // NOTE: we left one queryId for emergency withdraw; 48 | return !isEnd; 49 | } 50 | 51 | getShift(): bigint { 52 | return this.shift; 53 | } 54 | 55 | getBitNumber(): bigint { 56 | return this.bitnumber; 57 | } 58 | 59 | getQueryId(): bigint { 60 | return (this.shift << BIT_NUMBER_SIZE) + this.bitnumber; 61 | } 62 | 63 | static fromQueryId(queryId: bigint): HighloadQueryId { 64 | const shift = queryId >> BIT_NUMBER_SIZE; 65 | const bitnumber = queryId & 1023n; 66 | return this.fromShiftAndBitNumber(shift, bitnumber); 67 | } 68 | 69 | static fromSeqno(i: bigint): HighloadQueryId { 70 | const shift = i / 1023n; 71 | const bitnumber = i % 1023n; 72 | return this.fromShiftAndBitNumber(shift, bitnumber); 73 | } 74 | 75 | /** 76 | * @return {bigint} [0 .. 8380415] 77 | */ 78 | toSeqno(): bigint { 79 | return this.bitnumber + this.shift * 1023n; 80 | } 81 | } -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 6/wrappers/HighloadWalletV3.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Address, 3 | beginCell, 4 | Cell, 5 | Contract, 6 | contractAddress, 7 | ContractProvider, 8 | internal as internal_relaxed, 9 | MessageRelaxed, 10 | OutAction, 11 | OutActionSendMsg, 12 | Sender, 13 | SendMode, 14 | storeMessageRelaxed, 15 | storeOutList, 16 | toNano 17 | } from '@ton/core'; 18 | // import { hex as CodeHex } from '../build/HighloadWalletV3.compiled.json'; 19 | import {sign} from "@ton/crypto"; 20 | import {OP} from "./const"; 21 | import {HighloadQueryId} from "./HighloadQueryId"; 22 | 23 | // export const HighloadWalletV3Code = Cell.fromBoc(Buffer.from(CodeHex, "hex"))[0] 24 | 25 | export type HighloadWalletV3Config = { 26 | publicKey: Buffer, 27 | subwalletId: number, 28 | timeout: number 29 | }; 30 | 31 | 32 | export const TIMESTAMP_SIZE = 64; 33 | export const TIMEOUT_SIZE = 22; 34 | 35 | export function highloadWalletV3ConfigToCell(config: HighloadWalletV3Config): Cell { 36 | return beginCell() 37 | .storeBuffer(config.publicKey) 38 | .storeUint(config.subwalletId, 32) 39 | .storeUint(0, 1 + 1 + TIMESTAMP_SIZE) 40 | .storeUint(config.timeout, TIMEOUT_SIZE) 41 | .endCell(); 42 | } 43 | 44 | export class HighloadWalletV3 implements Contract { 45 | 46 | constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) { 47 | } 48 | 49 | static createFromAddress(address: Address) { 50 | return new HighloadWalletV3(address); 51 | } 52 | 53 | static createFromConfig(config: HighloadWalletV3Config, code: Cell, workchain = 0) { 54 | const data = highloadWalletV3ConfigToCell(config); 55 | const init = {code, data}; 56 | return new HighloadWalletV3(contractAddress(workchain, init), init); 57 | } 58 | 59 | async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { 60 | await provider.internal(via, { 61 | value, 62 | bounce: false, 63 | sendMode: SendMode.PAY_GAS_SEPARATELY, 64 | body: beginCell().endCell(), 65 | }); 66 | } 67 | 68 | async sendExternalMessage( 69 | provider: ContractProvider, 70 | secretKey: Buffer, 71 | opts: { 72 | message: MessageRelaxed | Cell, 73 | mode: number, 74 | query_id: bigint | HighloadQueryId, 75 | createdAt: number, 76 | subwalletId: number, 77 | timeout: number, 78 | } 79 | ) { 80 | let messageCell: Cell; 81 | 82 | if (opts.message instanceof Cell) { 83 | messageCell = opts.message 84 | } else { 85 | const messageBuilder = beginCell(); 86 | messageBuilder.store(storeMessageRelaxed(opts.message)) 87 | messageCell = messageBuilder.endCell(); 88 | } 89 | 90 | const queryId = (opts.query_id instanceof HighloadQueryId) ? opts.query_id.getQueryId() : opts.query_id; 91 | 92 | const messageInner = beginCell() 93 | .storeUint(opts.subwalletId, 32) 94 | .storeRef(messageCell) 95 | .storeUint(opts.mode, 8) 96 | .storeUint(queryId, 23) 97 | .storeUint(opts.createdAt, TIMESTAMP_SIZE) 98 | .storeUint(opts.timeout, TIMEOUT_SIZE) 99 | .endCell(); 100 | 101 | await provider.external( 102 | beginCell() 103 | .storeBuffer(sign(messageInner.hash(), secretKey)) 104 | .storeRef(messageInner) 105 | .endCell() 106 | ); 107 | } 108 | 109 | async sendBatch(provider: ContractProvider, secretKey: Buffer, messages: OutActionSendMsg[], subwallet: number, query_id: HighloadQueryId, timeout: number, value: bigint, sendMode: SendMode, createdAt?: number) { 110 | if (createdAt == undefined) { 111 | createdAt = Math.floor(Date.now() / 1000) - 60; 112 | } 113 | return await this.sendExternalMessage(provider, secretKey, { 114 | message: this.packActions(messages, value, query_id), 115 | mode: sendMode, 116 | query_id: query_id, 117 | createdAt: createdAt, 118 | subwalletId: subwallet, 119 | timeout: timeout 120 | }); 121 | } 122 | 123 | static createInternalTransferBody(opts: { 124 | actions: OutAction[] | Cell, 125 | queryId: HighloadQueryId, 126 | }) { 127 | let actionsCell: Cell; 128 | if (opts.actions instanceof Cell) { 129 | actionsCell = opts.actions; 130 | } else { 131 | if (opts.actions.length > 254) { 132 | throw TypeError("Max allowed action count is 254. Use packActions instead."); 133 | } 134 | const actionsBuilder = beginCell(); 135 | storeOutList(opts.actions)(actionsBuilder); 136 | actionsCell = actionsBuilder.endCell(); 137 | } 138 | return beginCell().storeUint(OP.InternalTransfer, 32) 139 | .storeUint(opts.queryId.getQueryId(), 64) 140 | .storeRef(actionsCell) 141 | .endCell(); 142 | 143 | 144 | } 145 | 146 | createInternalTransfer(opts: { 147 | actions: OutAction[] | Cell 148 | queryId: HighloadQueryId, 149 | value: bigint 150 | }) { 151 | 152 | return internal_relaxed({ 153 | to: this.address, 154 | value: opts.value, 155 | body: HighloadWalletV3.createInternalTransferBody(opts) 156 | }); 157 | /*beginCell() 158 | .storeUint(0x10, 6) 159 | .storeAddress(this.address) 160 | .storeCoins(opts.value) 161 | .storeUint(0, 107) 162 | .storeSlice(body.asSlice()) 163 | .endCell(); 164 | */ 165 | } 166 | 167 | packActions(messages: OutAction[], value: bigint = toNano('1'), query_id: HighloadQueryId) { 168 | let batch: OutAction[]; 169 | if (messages.length > 254) { 170 | batch = messages.slice(0, 253); 171 | batch.push({ 172 | type: 'sendMsg', 173 | mode: value > 0n ? SendMode.PAY_GAS_SEPARATELY : SendMode.CARRY_ALL_REMAINING_BALANCE, 174 | outMsg: this.packActions(messages.slice(253), value, query_id) 175 | }); 176 | } else { 177 | batch = messages; 178 | } 179 | return this.createInternalTransfer({ 180 | actions: batch, 181 | queryId: query_id, 182 | value 183 | }); 184 | } 185 | 186 | 187 | async getPublicKey(provider: ContractProvider): Promise { 188 | const res = (await provider.get('get_public_key', [])).stack; 189 | const pubKeyU = res.readBigNumber(); 190 | return Buffer.from(pubKeyU.toString(16).padStart(32 * 2, '0'), 'hex'); 191 | } 192 | 193 | async getSubwalletId(provider: ContractProvider): Promise { 194 | const res = (await provider.get('get_subwallet_id', [])).stack; 195 | return res.readNumber(); 196 | } 197 | 198 | async getTimeout(provider: ContractProvider): Promise { 199 | const res = (await provider.get('get_timeout', [])).stack; 200 | return res.readNumber(); 201 | } 202 | 203 | async getLastCleaned(provider: ContractProvider): Promise { 204 | const res = (await provider.get('get_last_clean_time', [])).stack; 205 | return res.readNumber(); 206 | } 207 | 208 | async getProcessed(provider: ContractProvider, queryId: HighloadQueryId, needClean = true): Promise { 209 | const res = (await provider.get('processed?', [{'type': 'int', 'value': queryId.getQueryId()}, { 210 | 'type': 'int', 211 | 'value': needClean ? -1n : 0n 212 | }])).stack; 213 | return res.readBoolean(); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/Chapter 6/wrappers/const.ts: -------------------------------------------------------------------------------- 1 | export const SUBWALLET_ID = 239; 2 | 3 | export const DEFAULT_TIMEOUT = 128; 4 | 5 | export enum OP { 6 | InternalTransfer = 0xae42e5a4 7 | } 8 | export abstract class Errors { 9 | static invalid_signature = 33; 10 | static invalid_subwallet = 34; 11 | static invalid_creation_time = 35; 12 | static already_executed = 36; 13 | } 14 | 15 | export const maxKeyCount = (1 << 13); //That is max key count not max key value 16 | export const maxShift = maxKeyCount - 1; 17 | export const maxQueryCount = maxKeyCount * 1023; // Therefore value count 18 | export const maxQueryId = (maxShift << 10) + 1022; -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/highload_wallet.fc: -------------------------------------------------------------------------------- 1 | ;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges) 2 | ;; accepts orders for up to 254 internal messages (transfers) in one external message 3 | ;; this version does not use seqno for replay protection; instead, it remembers all recent query_ids 4 | ;; in this way several external messages with different query_id can be sent in parallel 5 | 6 | #include "stdlib.fc"; 7 | 8 | () recv_internal(slice in_msg) impure { 9 | ;; do nothing for internal messages 10 | } 11 | 12 | () recv_external(slice in_msg) impure { 13 | var signature = in_msg~load_bits(512); 14 | var cs = in_msg; 15 | var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); 16 | var bound = (now() << 32); 17 | throw_if(35, query_id < bound); 18 | var ds = get_data().begin_parse(); 19 | var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); 20 | ds.end_parse(); 21 | (_, var found?) = old_queries.udict_get?(64, query_id); 22 | throw_if(32, found?); 23 | throw_unless(34, subwallet_id == stored_subwallet); 24 | throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); 25 | var dict = cs~load_dict(); 26 | cs.end_parse(); 27 | accept_message(); 28 | int i = -1; 29 | do { 30 | (i, var cs, var f) = dict.idict_get_next?(16, i); 31 | if (f) { 32 | var mode = cs~load_uint(8); 33 | send_raw_message(cs~load_ref(), mode); 34 | } 35 | } until (~ f); 36 | bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago 37 | old_queries~udict_set_builder(64, query_id, begin_cell()); 38 | var queries = old_queries; 39 | do { 40 | var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); 41 | f~touch(); 42 | if (f) { 43 | f = (i < bound); 44 | } 45 | if (f) { 46 | old_queries = old_queries'; 47 | last_cleaned = i; 48 | } 49 | } until (~ f); 50 | set_data(begin_cell() 51 | .store_uint(stored_subwallet, 32) 52 | .store_uint(last_cleaned, 64) 53 | .store_uint(public_key, 256) 54 | .store_dict(old_queries) 55 | .end_cell()); 56 | } 57 | 58 | ;; Get methods 59 | 60 | ;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten) 61 | int processed?(int query_id) method_id { 62 | var ds = get_data().begin_parse(); 63 | var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); 64 | ds.end_parse(); 65 | (_, var found) = old_queries.udict_get?(64, query_id); 66 | return found ? true : - (query_id <= last_cleaned); 67 | } 68 | 69 | int get_public_key() method_id { 70 | var cs = get_data().begin_parse(); 71 | cs~load_uint(32 + 64); 72 | return cs.preload_uint(256); 73 | } -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/index.ts: -------------------------------------------------------------------------------- 1 | async function main() { 2 | console.log("Hello, TON!"); 3 | } 4 | 5 | main().finally(() => console.log("Exiting...")); -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/stdlib.fc: -------------------------------------------------------------------------------- 1 | ;; Standard library for funC 2 | ;; 3 | 4 | {- 5 | # Tuple manipulation primitives 6 | The names and the types are mostly self-explaining. 7 | See [polymorhism with forall](https://ton.org/docs/#/func/functions?id=polymorphism-with-forall) 8 | for more info on the polymorphic functions. 9 | 10 | Note that currently values of atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`) 11 | and vise versa. 12 | -} 13 | 14 | {- 15 | # Lisp-style lists 16 | 17 | Lists can be represented as nested 2-elements tuples. 18 | Empty list is conventionally represented as TVM `null` value (it can be obtained by calling [null()]). 19 | For example, tuple `(1, (2, (3, null)))` represents list `[1, 2, 3]`. Elements of a list can be of different types. 20 | -} 21 | 22 | ;;; Adds an element to the beginning of lisp-style list. 23 | forall X -> tuple cons(X head, tuple tail) asm "CONS"; 24 | 25 | ;;; Extracts the head and the tail of lisp-style list. 26 | forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; 27 | 28 | ;;; Extracts the tail and the head of lisp-style list. 29 | forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; 30 | 31 | ;;; Returns the head of lisp-style list. 32 | forall X -> X car(tuple list) asm "CAR"; 33 | 34 | ;;; Returns the tail of lisp-style list. 35 | tuple cdr(tuple list) asm "CDR"; 36 | 37 | ;;; Creates tuple with zero elements. 38 | tuple empty_tuple() asm "NIL"; 39 | 40 | ;;; Appends a value `x` to a `Tuple t = (x1, ..., xn)`, but only if the resulting `Tuple t' = (x1, ..., xn, x)` 41 | ;;; is of length at most 255. Otherwise throws a type check exception. 42 | forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; 43 | forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; 44 | 45 | ;;; Creates a tuple of length one with given argument as element. 46 | forall X -> [X] single(X x) asm "SINGLE"; 47 | 48 | ;;; Unpacks a tuple of length one 49 | forall X -> X unsingle([X] t) asm "UNSINGLE"; 50 | 51 | ;;; Creates a tuple of length two with given arguments as elements. 52 | forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; 53 | 54 | ;;; Unpacks a tuple of length two 55 | forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; 56 | 57 | ;;; Creates a tuple of length three with given arguments as elements. 58 | forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; 59 | 60 | ;;; Unpacks a tuple of length three 61 | forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; 62 | 63 | ;;; Creates a tuple of length four with given arguments as elements. 64 | forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; 65 | 66 | ;;; Unpacks a tuple of length four 67 | forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; 68 | 69 | ;;; Returns the first element of a tuple (with unknown element types). 70 | forall X -> X first(tuple t) asm "FIRST"; 71 | 72 | ;;; Returns the second element of a tuple (with unknown element types). 73 | forall X -> X second(tuple t) asm "SECOND"; 74 | 75 | ;;; Returns the third element of a tuple (with unknown element types). 76 | forall X -> X third(tuple t) asm "THIRD"; 77 | 78 | ;;; Returns the fourth element of a tuple (with unknown element types). 79 | forall X -> X fourth(tuple t) asm "3 INDEX"; 80 | 81 | ;;; Returns the first element of a pair tuple. 82 | forall X, Y -> X pair_first([X, Y] p) asm "FIRST"; 83 | 84 | ;;; Returns the second element of a pair tuple. 85 | forall X, Y -> Y pair_second([X, Y] p) asm "SECOND"; 86 | 87 | ;;; Returns the first element of a triple tuple. 88 | forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST"; 89 | 90 | ;;; Returns the second element of a triple tuple. 91 | forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND"; 92 | 93 | ;;; Returns the third element of a triple tuple. 94 | forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; 95 | 96 | 97 | ;;; Push null element (casted to given type) 98 | ;;; By the TVM type `Null` FunC represents absence of a value of some atomic type. 99 | ;;; So `null` can actually have any atomic type. 100 | forall X -> X null() asm "PUSHNULL"; 101 | 102 | ;;; Moves a variable [x] to the top of the stack 103 | forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP"; 104 | 105 | 106 | 107 | ;;; Returns the current Unix time as an Integer 108 | int now() asm "NOW"; 109 | 110 | ;;; Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`. 111 | ;;; If necessary, it can be parsed further using primitives such as [parse_std_addr]. 112 | slice my_address() asm "MYADDR"; 113 | 114 | ;;; Returns the balance of the smart contract as a tuple consisting of an int 115 | ;;; (balance in nanotoncoins) and a `cell` 116 | ;;; (a dictionary with 32-bit keys representing the balance of "extra currencies") 117 | ;;; at the start of Computation Phase. 118 | ;;; Note that RAW primitives such as [send_raw_message] do not update this field. 119 | [int, cell] get_balance() asm "BALANCE"; 120 | 121 | ;;; Returns the logical time of the current transaction. 122 | int cur_lt() asm "LTIME"; 123 | 124 | ;;; Returns the starting logical time of the current block. 125 | int block_lt() asm "BLOCKLT"; 126 | 127 | ;;; Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`. 128 | ;;; Useful for signing and checking signatures of arbitrary entities represented by a tree of cells. 129 | int cell_hash(cell c) asm "HASHCU"; 130 | 131 | ;;; Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`. 132 | ;;; The result is the same as if an ordinary cell containing only data and references from `s` had been created 133 | ;;; and its hash computed by [cell_hash]. 134 | int slice_hash(slice s) asm "HASHSU"; 135 | 136 | ;;; Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight, 137 | ;;; throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. 138 | int string_hash(slice s) asm "SHA256U"; 139 | 140 | {- 141 | # Signature checks 142 | -} 143 | 144 | ;;; Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) 145 | ;;; using [public_key] (also represented by a 256-bit unsigned integer). 146 | ;;; The signature must contain at least 512 data bits; only the first 512 bits are used. 147 | ;;; The result is `−1` if the signature is valid, `0` otherwise. 148 | ;;; Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`. 149 | ;;; That is, if [hash] is computed as the hash of some data, these data are hashed twice, 150 | ;;; the second hashing occurring inside `CHKSIGNS`. 151 | int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; 152 | 153 | ;;; Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `public_key`, 154 | ;;; similarly to [check_signature]. 155 | ;;; If the bit length of [data] is not divisible by eight, throws a cell underflow exception. 156 | ;;; The verification of Ed25519 signatures is the standard one, 157 | ;;; with sha256 used to reduce [data] to the 256-bit number that is actually signed. 158 | int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; 159 | 160 | {--- 161 | # Computation of boc size 162 | The primitives below may be useful for computing storage fees of user-provided data. 163 | -} 164 | 165 | ;;; Returns `(x, y, z, -1)` or `(null, null, null, 0)`. 166 | ;;; Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z` 167 | ;;; in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account 168 | ;;; the identification of equal cells. 169 | ;;; The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG, 170 | ;;; with a hash table of visited cell hashes used to prevent visits of already-visited cells. 171 | ;;; The total count of visited cells `x` cannot exceed non-negative [max_cells]; 172 | ;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and 173 | ;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. 174 | (int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; 175 | 176 | ;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`. 177 | ;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; 178 | ;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`. 179 | (int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; 180 | 181 | ;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure. 182 | (int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; 183 | 184 | ;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. 185 | (int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; 186 | 187 | ;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) 188 | ;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; 189 | 190 | {-- 191 | # Debug primitives 192 | Only works for local TVM execution with debug level verbosity 193 | -} 194 | ;;; Dumps the stack (at most the top 255 values) and shows the total stack depth. 195 | () dump_stack() impure asm "DUMPSTK"; 196 | 197 | {- 198 | # Persistent storage save and load 199 | -} 200 | 201 | ;;; Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. 202 | cell get_data() asm "c4 PUSH"; 203 | 204 | ;;; Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive. 205 | () set_data(cell c) impure asm "c4 POP"; 206 | 207 | {- 208 | # Continuation primitives 209 | -} 210 | ;;; Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. 211 | ;;; The primitive returns the current value of `c3`. 212 | cont get_c3() impure asm "c3 PUSH"; 213 | 214 | ;;; Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time. 215 | ;;; Note that after execution of this primitive the current code 216 | ;;; (and the stack of recursive function calls) won't change, 217 | ;;; but any other function call will use a function from the new code. 218 | () set_c3(cont c) impure asm "c3 POP"; 219 | 220 | ;;; Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist. 221 | cont bless(slice s) impure asm "BLESS"; 222 | 223 | {--- 224 | # Gas related primitives 225 | -} 226 | 227 | ;;; Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero, 228 | ;;; decreasing the value of `gr` by `gc` in the process. 229 | ;;; In other words, the current smart contract agrees to buy some gas to finish the current transaction. 230 | ;;; This action is required to process external messages, which bring no value (hence no gas) with themselves. 231 | ;;; 232 | ;;; For more details check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept). 233 | () accept_message() impure asm "ACCEPT"; 234 | 235 | ;;; Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero. 236 | ;;; If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`, 237 | ;;; an (unhandled) out of gas exception is thrown before setting new gas limits. 238 | ;;; Notice that [set_gas_limit] with an argument `limit ≥ 2^63 − 1` is equivalent to [accept_message]. 239 | () set_gas_limit(int limit) impure asm "SETGASLIMIT"; 240 | 241 | ;;; Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”) 242 | ;;; so that the current execution is considered “successful” with the saved values even if an exception 243 | ;;; in Computation Phase is thrown later. 244 | () commit() impure asm "COMMIT"; 245 | 246 | ;;; Not implemented 247 | ;;() buy_gas(int gram) impure asm "BUYGAS"; 248 | 249 | ;;; Computes the amount of gas that can be bought for `amount` nanoTONs, 250 | ;;; and sets `gl` accordingly in the same way as [set_gas_limit]. 251 | () buy_gas(int amount) impure asm "BUYGAS"; 252 | 253 | ;;; Computes the minimum of two integers [x] and [y]. 254 | int min(int x, int y) asm "MIN"; 255 | 256 | ;;; Computes the maximum of two integers [x] and [y]. 257 | int max(int x, int y) asm "MAX"; 258 | 259 | ;;; Sorts two integers. 260 | (int, int) minmax(int x, int y) asm "MINMAX"; 261 | 262 | ;;; Computes the absolute value of an integer [x]. 263 | int abs(int x) asm "ABS"; 264 | 265 | {- 266 | # Slice primitives 267 | 268 | It is said that a primitive _loads_ some data, 269 | if it returns the data and the remainder of the slice 270 | (so it can also be used as [modifying method](https://ton.org/docs/#/func/statements?id=modifying-methods)). 271 | 272 | It is said that a primitive _preloads_ some data, if it returns only the data 273 | (it can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods)). 274 | 275 | Unless otherwise stated, loading and preloading primitives read the data from a prefix of the slice. 276 | -} 277 | 278 | 279 | ;;; Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell, 280 | ;;; or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2) 281 | ;;; which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards. 282 | slice begin_parse(cell c) asm "CTOS"; 283 | 284 | ;;; Checks if [s] is empty. If not, throws an exception. 285 | () end_parse(slice s) impure asm "ENDS"; 286 | 287 | ;;; Loads the first reference from the slice. 288 | (slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; 289 | 290 | ;;; Preloads the first reference from the slice. 291 | cell preload_ref(slice s) asm "PLDREF"; 292 | 293 | {- Functions below are commented because are implemented on compilator level for optimisation -} 294 | 295 | ;;; Loads a signed [len]-bit integer from a slice [s]. 296 | ;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; 297 | 298 | ;;; Loads an unsigned [len]-bit integer from a slice [s]. 299 | ;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; 300 | 301 | ;;; Preloads a signed [len]-bit integer from a slice [s]. 302 | ;; int preload_int(slice s, int len) asm "PLDIX"; 303 | 304 | ;;; Preloads an unsigned [len]-bit integer from a slice [s]. 305 | ;; int preload_uint(slice s, int len) asm "PLDUX"; 306 | 307 | ;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. 308 | ;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; 309 | 310 | ;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. 311 | ;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; 312 | 313 | ;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^120 - 1`). 314 | (slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS"; 315 | (slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS"; 316 | 317 | ;;; Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s]. 318 | slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; 319 | (slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST"; 320 | 321 | ;;; Returns the first `0 ≤ len ≤ 1023` bits of `slice` [s]. 322 | slice first_bits(slice s, int len) asm "SDCUTFIRST"; 323 | 324 | ;;; Returns all but the last `0 ≤ len ≤ 1023` bits of `slice` [s]. 325 | slice skip_last_bits(slice s, int len) asm "SDSKIPLAST"; 326 | (slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST"; 327 | 328 | ;;; Returns the last `0 ≤ len ≤ 1023` bits of `slice` [s]. 329 | slice slice_last(slice s, int len) asm "SDCUTLAST"; 330 | 331 | ;;; Loads a dictionary `D` (HashMapE) from `slice` [s]. 332 | ;;; (returns `null` if `nothing` constructor is used). 333 | (slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; 334 | 335 | ;;; Preloads a dictionary `D` from `slice` [s]. 336 | cell preload_dict(slice s) asm "PLDDICT"; 337 | 338 | ;;; Loads a dictionary as [load_dict], but returns only the remainder of the slice. 339 | slice skip_dict(slice s) asm "SKIPDICT"; 340 | 341 | ;;; Loads (Maybe ^Cell) from `slice` [s]. 342 | ;;; In other words loads 1 bit and if it is true 343 | ;;; loads first ref and return it with slice remainder 344 | ;;; otherwise returns `null` and slice remainder 345 | (slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF"; 346 | 347 | ;;; Preloads (Maybe ^Cell) from `slice` [s]. 348 | cell preload_maybe_ref(slice s) asm "PLDOPTREF"; 349 | 350 | 351 | ;;; Returns the depth of `cell` [c]. 352 | ;;; If [c] has no references, then return `0`; 353 | ;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [c]. 354 | ;;; If [c] is a `null` instead of a cell, returns zero. 355 | int cell_depth(cell c) asm "CDEPTH"; 356 | 357 | 358 | {- 359 | # Slice size primitives 360 | -} 361 | 362 | ;;; Returns the number of references in `slice` [s]. 363 | int slice_refs(slice s) asm "SREFS"; 364 | 365 | ;;; Returns the number of data bits in `slice` [s]. 366 | int slice_bits(slice s) asm "SBITS"; 367 | 368 | ;;; Returns both the number of data bits and the number of references in `slice` [s]. 369 | (int, int) slice_bits_refs(slice s) asm "SBITREFS"; 370 | 371 | ;;; Checks whether a `slice` [s] is empty (i.e., contains no bits of data and no cell references). 372 | int slice_empty?(slice s) asm "SEMPTY"; 373 | 374 | ;;; Checks whether `slice` [s] has no bits of data. 375 | int slice_data_empty?(slice s) asm "SDEMPTY"; 376 | 377 | ;;; Checks whether `slice` [s] has no references. 378 | int slice_refs_empty?(slice s) asm "SREMPTY"; 379 | 380 | ;;; Returns the depth of `slice` [s]. 381 | ;;; If [s] has no references, then returns `0`; 382 | ;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [s]. 383 | int slice_depth(slice s) asm "SDEPTH"; 384 | 385 | {- 386 | # Builder size primitives 387 | -} 388 | 389 | ;;; Returns the number of cell references already stored in `builder` [b] 390 | int builder_refs(builder b) asm "BREFS"; 391 | 392 | ;;; Returns the number of data bits already stored in `builder` [b]. 393 | int builder_bits(builder b) asm "BBITS"; 394 | 395 | ;;; Returns the depth of `builder` [b]. 396 | ;;; If no cell references are stored in [b], then returns 0; 397 | ;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [b]. 398 | int builder_depth(builder b) asm "BDEPTH"; 399 | 400 | {- 401 | # Builder primitives 402 | It is said that a primitive _stores_ a value `x` into a builder `b` 403 | if it returns a modified version of the builder `b'` with the value `x` stored at the end of it. 404 | It can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods). 405 | 406 | All the primitives below first check whether there is enough space in the `builder`, 407 | and only then check the range of the value being serialized. 408 | -} 409 | 410 | ;;; Creates a new empty `builder`. 411 | builder begin_cell() asm "NEWC"; 412 | 413 | ;;; Converts a `builder` into an ordinary `cell`. 414 | cell end_cell(builder b) asm "ENDC"; 415 | 416 | ;;; Stores a reference to `cell` [c] into `builder` [b]. 417 | builder store_ref(builder b, cell c) asm(c b) "STREF"; 418 | 419 | ;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. 420 | ;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; 421 | 422 | ;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. 423 | ;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; 424 | 425 | 426 | ;;; Stores `slice` [s] into `builder` [b] 427 | builder store_slice(builder b, slice s) asm "STSLICER"; 428 | 429 | ;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. 430 | ;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, 431 | ;;; which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, 432 | ;;; followed by an `8l`-bit unsigned big-endian representation of [x]. 433 | ;;; If [x] does not belong to the supported range, a range check exception is thrown. 434 | ;;; 435 | ;;; Store amounts of TonCoins to the builder as VarUInteger 16 436 | builder store_grams(builder b, int x) asm "STGRAMS"; 437 | builder store_coins(builder b, int x) asm "STGRAMS"; 438 | 439 | ;;; Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b]. 440 | ;;; In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise. 441 | builder store_dict(builder b, cell c) asm(c b) "STDICT"; 442 | 443 | ;;; Stores (Maybe ^Cell) to builder: 444 | ;;; if cell is null store 1 zero bit 445 | ;;; otherwise store 1 true bit and ref to cell 446 | builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; 447 | 448 | 449 | {- 450 | # Address manipulation primitives 451 | The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme: 452 | ```TL-B 453 | addr_none$00 = MsgAddressExt; 454 | addr_extern$01 len:(## 8) external_address:(bits len) 455 | = MsgAddressExt; 456 | anycast_info$_ depth:(#<= 30) { depth >= 1 } 457 | rewrite_pfx:(bits depth) = Anycast; 458 | addr_std$10 anycast:(Maybe Anycast) 459 | workchain_id:int8 address:bits256 = MsgAddressInt; 460 | addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) 461 | workchain_id:int32 address:(bits addr_len) = MsgAddressInt; 462 | _ _:MsgAddressInt = MsgAddress; 463 | _ _:MsgAddressExt = MsgAddress; 464 | 465 | int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool 466 | src:MsgAddress dest:MsgAddressInt 467 | value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams 468 | created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; 469 | ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt 470 | created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; 471 | ``` 472 | A deserialized `MsgAddress` is represented by a tuple `t` as follows: 473 | 474 | - `addr_none` is represented by `t = (0)`, 475 | i.e., a tuple containing exactly one integer equal to zero. 476 | - `addr_extern` is represented by `t = (1, s)`, 477 | where slice `s` contains the field `external_address`. In other words, ` 478 | t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`. 479 | - `addr_std` is represented by `t = (2, u, x, s)`, 480 | where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present). 481 | Next, integer `x` is the `workchain_id`, and slice `s` contains the address. 482 | - `addr_var` is represented by `t = (3, u, x, s)`, 483 | where `u`, `x`, and `s` have the same meaning as for `addr_std`. 484 | -} 485 | 486 | ;;; Loads from slice [s] the only prefix that is a valid `MsgAddress`, 487 | ;;; and returns both this prefix `s'` and the remainder `s''` of [s] as slices. 488 | (slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR"; 489 | 490 | ;;; Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`. 491 | ;;; If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown. 492 | tuple parse_addr(slice s) asm "PARSEMSGADDR"; 493 | 494 | ;;; Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`), 495 | ;;; applies rewriting from the anycast (if present) to the same-length prefix of the address, 496 | ;;; and returns both the workchain and the 256-bit address as integers. 497 | ;;; If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`, 498 | ;;; throws a cell deserialization exception. 499 | (int, int) parse_std_addr(slice s) asm "REWRITESTDADDR"; 500 | 501 | ;;; A variant of [parse_std_addr] that returns the (rewritten) address as a slice [s], 502 | ;;; even if it is not exactly 256 bit long (represented by a `msg_addr_var`). 503 | (int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR"; 504 | 505 | {- 506 | # Dictionary primitives 507 | -} 508 | 509 | 510 | ;;; Sets the value associated with [key_len]-bit key signed index in dictionary [dict] to [value] (cell), 511 | ;;; and returns the resulting dictionary. 512 | cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; 513 | (cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; 514 | 515 | ;;; Sets the value associated with [key_len]-bit key unsigned index in dictionary [dict] to [value] (cell), 516 | ;;; and returns the resulting dictionary. 517 | cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; 518 | (cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; 519 | 520 | cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; 521 | (cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF" "NULLSWAPIFNOT"; 522 | (cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF" "NULLSWAPIFNOT"; 523 | (cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; 524 | (cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; 525 | (cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; 526 | (cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL"; 527 | (slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; 528 | (slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; 529 | (cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; 530 | (cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; 531 | (cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; 532 | (cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; 533 | cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; 534 | (cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; 535 | cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; 536 | (cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; 537 | cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; 538 | (cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; 539 | (cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD"; 540 | (cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE"; 541 | (cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD"; 542 | (cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE"; 543 | cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; 544 | (cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; 545 | cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; 546 | (cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; 547 | cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; 548 | (cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; 549 | (cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB"; 550 | (cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; 551 | (cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; 552 | (cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; 553 | (cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; 554 | (cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; 555 | (cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; 556 | (cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; 557 | (cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; 558 | (cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; 559 | (cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; 560 | (cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; 561 | (cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; 562 | (cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; 563 | (cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; 564 | (cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; 565 | (int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; 566 | (int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; 567 | (int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; 568 | (int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; 569 | (int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; 570 | (int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; 571 | (int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; 572 | (int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; 573 | (int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; 574 | (int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; 575 | (int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; 576 | (int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; 577 | (int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; 578 | (int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; 579 | (int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; 580 | (int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; 581 | 582 | ;;; Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL 583 | cell new_dict() asm "NEWDICT"; 584 | ;;; Checks whether a dictionary is empty. Equivalent to cell_null?. 585 | int dict_empty?(cell c) asm "DICTEMPTY"; 586 | 587 | 588 | {- Prefix dictionary primitives -} 589 | (slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; 590 | (cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET"; 591 | (cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL"; 592 | 593 | ;;; Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value. 594 | cell config_param(int x) asm "CONFIGOPTPARAM"; 595 | ;;; Checks whether c is a null. Note, that FunC also has polymorphic null? built-in. 596 | int cell_null?(cell c) asm "ISNULL"; 597 | 598 | ;;; Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15. 599 | () raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; 600 | ;;; Similar to raw_reserve, but also accepts a dictionary extra_amount (represented by a cell or null) with extra currencies. In this way currencies other than TonCoin can be reserved. 601 | () raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX"; 602 | ;;; Sends a raw message contained in msg, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have dummy value addr_none (to be automatically replaced with the current smart contract address), and ihr_fee, fwd_fee, created_lt and created_at fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter mode contains the flags. Currently mode = 0 is used for ordinary messages; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); mode' = mode + 1 means that the sender wants to pay transfer fees separately; mode' = mode + 2 means that any errors arising while processing this message during the action phase should be ignored. Finally, mode' = mode + 32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128. 603 | () send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG"; 604 | ;;; Creates an output action that would change this smart contract code to that given by cell new_code. Notice that this change will take effect only after the successful termination of the current run of the smart contract 605 | () set_code(cell new_code) impure asm "SETCODE"; 606 | 607 | ;;; Generates a new pseudo-random unsigned 256-bit integer x. The algorithm is as follows: if r is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its sha512(r) is computed; the first 32 bytes of this hash are stored as the new value r' of the random seed, and the remaining 32 bytes are returned as the next random value x. 608 | int random() impure asm "RANDU256"; 609 | ;;; Generates a new pseudo-random integer z in the range 0..range−1 (or range..−1, if range < 0). More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed. 610 | int rand(int range) impure asm "RAND"; 611 | ;;; Returns the current random seed as an unsigned 256-bit Integer. 612 | int get_seed() impure asm "RANDSEED"; 613 | ;;; Sets the random seed to unsigned 256-bit seed. 614 | () set_seed(int) impure asm "SETRAND"; 615 | ;;; Mixes unsigned 256-bit integer x into the random seed r by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed r, and the second with the big-endian representation of x. 616 | () randomize(int x) impure asm "ADDRAND"; 617 | ;;; Equivalent to randomize(cur_lt());. 618 | () randomize_lt() impure asm "LTIME" "ADDRAND"; 619 | 620 | ;;; Checks whether the data parts of two slices coinside 621 | int equal_slice_bits (slice a, slice b) asm "SDEQ"; 622 | 623 | ;;; Concatenates two builders 624 | builder store_builder(builder to, builder from) asm "STBR"; -------------------------------------------------------------------------------- /JavaScript - TypeScript/src/wallet_v3.fc: -------------------------------------------------------------------------------- 1 | #include "stdlib.fc"; 2 | 3 | () recv_internal(slice in_msg) impure { 4 | ;; do nothing for internal messages 5 | } 6 | 7 | () recv_external(slice in_msg) impure { 8 | var signature = in_msg~load_bits(512); 9 | var cs = in_msg; 10 | var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); 11 | throw_if(35, valid_until <= now()); 12 | var ds = get_data().begin_parse(); 13 | var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); 14 | ds.end_parse(); 15 | throw_unless(33, msg_seqno == stored_seqno); 16 | throw_unless(34, subwallet_id == stored_subwallet); 17 | throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); 18 | accept_message(); 19 | cs~touch(); 20 | while (cs.slice_refs()) { 21 | var mode = cs~load_uint(8); 22 | send_raw_message(cs~load_ref(), mode); 23 | } 24 | set_data(begin_cell() 25 | .store_uint(stored_seqno + 1, 32) 26 | .store_uint(stored_subwallet, 32) 27 | .store_uint(public_key, 256) 28 | .end_cell()); 29 | } 30 | 31 | ;; Get methods 32 | 33 | int seqno() method_id { 34 | return get_data().begin_parse().preload_uint(32); 35 | } 36 | 37 | int get_public_key() method_id { 38 | var cs = get_data().begin_parse(); 39 | cs~load_uint(64); 40 | return cs.preload_uint(256); 41 | } -------------------------------------------------------------------------------- /JavaScript - TypeScript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["es6"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "src", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | "resolveJsonModule": true, /* Enable importing .json files. */ 39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | "outDir": "build", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | // "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": false, /* Enable all strict type-checking options. */ 80 | "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /JavaScript - TypeScript/tsconfigs.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["es6"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "src", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | "resolveJsonModule": true, /* Enable importing .json files. */ 39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | "outDir": "build", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | // "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": false, /* Enable all strict type-checking options. */ 80 | "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 102 | }, 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wallet Tutorial 2 | 3 | The source code of the examples used in Wallet Tutorial. 4 | 5 | ## How to use 6 | 7 | For each chapter, examples are stored separately as independent code. 8 | 9 | ### JavaScript / Typescript 10 | 11 | Each code file's content in the `Chapter` folders can be copied into `index.ts` and run immediately by substituting the desired values in the fields where indicated. 12 | 13 | **IMPORTANT:** Do not forget about `npm install` command before starting. 14 | 15 | ### Golang 16 | 17 | Each code file's content in the `Chapter` folders can be copied into `main.go` and run immediately by substituting the desired values in the fields where indicated. 18 | 19 | **IMPORTANT:** Do not forget about `go get` command before starting. --------------------------------------------------------------------------------