diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index d932bed30e..e3867d6e1e 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -283,6 +283,10 @@ export default defineConfig({ { slug: 'ref/stdlib-content' }, { slug: 'ref/stdlib-deploy' }, { slug: 'ref/stdlib-dns' }, + { + slug: 'ref/stdlib-jetton-interface', + badge: { variant: 'tip', text: 'new' }, + }, { slug: 'ref/stdlib-ownable' }, { slug: 'ref/stdlib-stoppable' }, ], diff --git a/docs/src/content/docs/book/cells.mdx b/docs/src/content/docs/book/cells.mdx index 8eb7e87408..c3eafcb4b2 100644 --- a/docs/src/content/docs/book/cells.mdx +++ b/docs/src/content/docs/book/cells.mdx @@ -198,8 +198,8 @@ To give a real-world example, imagine that you need to notice and react to inbou ```tact /remaining/ message(0x7362d09c) JettonTransferNotification { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains - queryId: Int as uint64 = 0; + // Setting it to 0 means we don't mark messages to trace requests + queryId: Int as uint64; // Amount of Jettons transferred amount: Int as coins; diff --git a/docs/src/content/docs/book/learn-tact-in-y-minutes.mdx b/docs/src/content/docs/book/learn-tact-in-y-minutes.mdx index c05cd3c45e..f372f54583 100644 --- a/docs/src/content/docs/book/learn-tact-in-y-minutes.mdx +++ b/docs/src/content/docs/book/learn-tact-in-y-minutes.mdx @@ -1228,11 +1228,12 @@ For more, refer to the following resources: #### JettonWallet ```tact +/// This library provides primary and auxiliary structures +/// and utilities for working with the Jetton standard. +import "@stdlib/jetton-interface"; + /// Child contract per each holder of N amount of given Jetton (token) contract JettonWallet( - /// Balance in Jettons. - balance: Int as coins, - /// Address of the user's wallet which owns this JettonWallet, and messages /// from whom should be recognized and fully processed. owner: Address, @@ -1240,6 +1241,9 @@ contract JettonWallet( /// Address of the main minting contract, /// which deployed this Jetton wallet for the specific user's wallet. master: Address, + + /// Balance in Jettons. + balance: Int as coins, ) { /// Registers a binary receiver of the JettonTransfer message body. /// Transfers Jettons from the current owner to the target user's JettonWallet. @@ -1271,11 +1275,11 @@ contract JettonWallet( // Transfer Jetton from the current owner to the target user's JettonWallet. // If that wallet does not exist, it is deployed on-chain in the same transfer. - deploy(DeployParameters{ + deploy(DeployParameters { value: 0, mode: SendRemainingValue, bounce: true, - body: JettonTransferInternal{ + body: JettonTransferInternal { queryId: msg.queryId, amount: msg.amount, sender: self.owner, @@ -1285,7 +1289,7 @@ contract JettonWallet( }.toCell(), // Notice that we do not need to explicitly specify the Address, // because it will be computed on the fly from the initial package. - init: initOf JettonWallet(0, msg.destination, self.master), + init: initOf JettonWallet(msg.destination, self.master, 0), }); } @@ -1297,7 +1301,7 @@ contract JettonWallet( // This message should come only from JettonMinter, // or from other JettonWallet. - let wallet: StateInit = initOf JettonWallet(0, msg.sender, self.master); + let wallet: StateInit = initOf JettonWallet(msg.sender, self.master, 0); if (sender() != contractAddress(wallet)) { require(self.master == sender(), "Incorrect sender"); } @@ -1311,12 +1315,12 @@ contract JettonWallet( if (msg.forwardTonAmount > 0) { let fwdFee: Int = ctx.readForwardFee(); msgValue -= msg.forwardTonAmount + fwdFee; - message(MessageParameters{ + message(MessageParameters { to: self.owner, value: msg.forwardTonAmount, mode: SendPayGasSeparately, bounce: false, - body: JettonNotification{ + body: JettonTransferNotification { queryId: msg.queryId, amount: msg.amount, sender: msg.sender, @@ -1331,12 +1335,12 @@ contract JettonWallet( // And forward excesses (cashback) to the original sender. if (msg.responseDestination != null && msgValue > 0) { - message(MessageParameters{ + message(MessageParameters { to: msg.responseDestination!!, value: msgValue, mode: SendRemainingBalance + SendIgnoreErrors, bounce: false, - body: JettonExcesses{ queryId: msg.queryId }.toCell(), + body: JettonExcesses { queryId: msg.queryId }.toCell(), }); } } @@ -1361,12 +1365,12 @@ contract JettonWallet( // Send a message to the JettonMinter to reduce the total supply // of the Jettons. That is, to burn some. - message(MessageParameters{ + message(MessageParameters { to: self.master, value: 0, mode: SendRemainingValue, bounce: true, - body: JettonBurnNotification{ + body: JettonBurnNotification { queryId: msg.queryId, amount: msg.amount, sender: self.owner, @@ -1378,80 +1382,25 @@ contract JettonWallet( /// Registers a bounced binary receiver of messages /// with the JettonTransferInternal opcode. /// It handles such outgoing messages that bounced back to this contract. - bounced(msg: bounced) { self.balance += msg.amount; } + bounced(msg: bounced) { self.balance += msg.amount } /// Registers a bounced binary receiver of messages /// with the JettonBurnNotification opcode. /// It handles such outgoing messages that bounced back to this contract. - bounced(msg: bounced) { self.balance += msg.amount; } + bounced(msg: bounced) { self.balance += msg.amount } /// An off-chain getter function which returns useful data about this wallet. get fun get_wallet_data(): JettonWalletData { - return JettonWalletData{ + return JettonWalletData { balance: self.balance, owner: self.owner, - master: self.master, + minter: self.master, code: myCode(), }; } } -// -// Helper structs, message structs and constants, -// which would otherwise be imported from another file -// - -struct JettonWalletData { - balance: Int; - owner: Address; - master: Address; - code: Cell; -} - -message(0xf8a7ea5) JettonTransfer { - queryId: Int as uint64; - amount: Int as coins; - destination: Address; - responseDestination: Address?; - customPayload: Cell?; - forwardTonAmount: Int as coins; - forwardPayload: Slice as remaining; -} - -message(0x178d4519) JettonTransferInternal { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - responseDestination: Address?; - forwardTonAmount: Int as coins; - forwardPayload: Slice as remaining; -} - -message(0x7362d09c) JettonNotification { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - forwardPayload: Slice as remaining; -} - -message(0x595f07bc) JettonBurn { - queryId: Int as uint64; - amount: Int as coins; - responseDestination: Address?; - customPayload: Cell?; -} - -message(0x7bdd97de) JettonBurnNotification { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - responseDestination: Address?; -} - -message(0xd53276db) JettonExcesses { - queryId: Int as uint64; -} - +// Helper constants, which would otherwise be imported from another file const GAS_FOR_BURN: Int = 6000; const GAS_FOR_TRANSFER: Int = 8000; const MIN_TONCOIN_FOR_STORAGE: Int = ton("0.01"); diff --git a/docs/src/content/docs/cookbook/dexes/dedust.mdx b/docs/src/content/docs/cookbook/dexes/dedust.mdx index f22bb11ab4..4309e6ea16 100644 --- a/docs/src/content/docs/cookbook/dexes/dedust.mdx +++ b/docs/src/content/docs/cookbook/dexes/dedust.mdx @@ -80,7 +80,7 @@ The guides below use the [Jetton vault](https://docs.dedust.io/docs/concepts#vau /// https://docs.dedust.io/reference/tlb-schemes#message-swap message(0xea06185d) NativeSwap { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains + // Defaults to 0, which means we don't mark messages to trace requests queryId: Int as uint64 = 0; // Toncoin amount for the swap @@ -156,7 +156,7 @@ fun swapJetton(targetJettonWalletAddress: Address) { value: ton("0.3"), body: JettonTransfer{ // Unique identifier used to trace transactions across multiple contracts. - // Set to 0, which means we don't mark messages to trace their chains. + // Set to 0, which means we don't mark messages to trace requests. queryId: 0, // Jetton amount for the swap. amount: 10, // NOTE: change to your amount @@ -260,7 +260,7 @@ message(0x40e108d6) JettonDepositLiquidity { /// https://docs.dedust.io/reference/tlb-schemes#message-deposit_liquidity message(0xd55e4686) NativeDepositLiquidity { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means messages are not marked to trace their chains + // Defaults to 0, which means messages are not marked to trace requests queryId: Int as uint64 = 0; // Toncoin amount for the deposit diff --git a/docs/src/content/docs/cookbook/dexes/stonfi.mdx b/docs/src/content/docs/cookbook/dexes/stonfi.mdx index 6eb3e521c0..053f619f89 100644 --- a/docs/src/content/docs/cookbook/dexes/stonfi.mdx +++ b/docs/src/content/docs/cookbook/dexes/stonfi.mdx @@ -318,7 +318,7 @@ To swap Toncoin to Jetton, STON.fi requires the use of a so-called proxy Toncoin /// https://github.com/ston-fi/sdk/blob/786ece758794bd5c575db8b38f5e5de19f43f0d1/packages/sdk/src/contracts/pTON/v2_1/PtonV2_1.ts message(0x01f3835d) ProxyToncoinTransfer { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains + // Defaults to 0, which means we don't mark messages to trace requests queryId: Int as uint64 = 0; // Toncoin amount for the swap diff --git a/docs/src/content/docs/cookbook/jettons.mdx b/docs/src/content/docs/cookbook/jettons.mdx index 564f7bedfe..997330f217 100644 --- a/docs/src/content/docs/cookbook/jettons.mdx +++ b/docs/src/content/docs/cookbook/jettons.mdx @@ -7,14 +7,31 @@ This page lists common examples of working with [Fungible Tokens (Jettons)](http Jettons are token standards on the TON Blockchain, designed to create fungible tokens (similar to ERC-20 on Ethereum) using a decentralized approach. They are implemented as a pair of smart contracts, typically consisting of two core components: -* Jetton Master Contract (Jetton master) -* Jetton Wallet Contract (Jetton wallet) +* Jetton Master (or Minter), which acts like a parent contract +* Jetton Wallet, which is a child contract managed by the Jetton Minter and created for each user's wallet. These contracts interact with each other to manage token supply, distribution, transfers, and other operations related to the Jetton. +Read more about Jetton standards: + +* [TEP-74: Fungible tokens (Jettons) standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md). +* [TEP-64: Token Data Standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md) +* [TEP-89: Discoverable Jetton Wallets](https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md) + +:::tip + + Since Tact 1.6.6, primary and auxiliary structures for working with Jetton standards are available in the `@stdlib/jetton-interface` library: + + ```tact + // This would include everything from @stdlib/jetton-interface + import "@stdlib/jetton-interface"; + ``` + +::: + ## Jetton Master Contract -The Jetton Master Contract serves as the central entity for a given Jetton. It maintains critical information about the Jetton itself. Key responsibilities and data stored in the Jetton Master Contract include: +The Jetton Master (or Minter) Contract serves as the central entity for a given Jetton. It maintains critical information about the Jetton itself. Key responsibilities and data stored in the Jetton Master Contract include: * Jetton metadata: Information such as the token's name, symbol, total supply, and decimals. @@ -44,17 +61,17 @@ The transfer notification message has the following structure: ```tact message(0x7362d09c) JettonTransferNotification { - // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains - queryId: Int as uint64 = 0; + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; - // Amount of Jettons transferred + /// Amount of transferred Jettons in elementary units. amount: Int as coins; - // Address of the sender of the Jettons + /// Address of the previous owner of the transferred jettons. sender: Address; - // Optional custom payload + /// Optional custom data that was attached by the `sender`. forwardPayload: Slice as remaining; } ``` @@ -89,12 +106,7 @@ Since notifications originate from your contract's Jetton wallet, the function [ ::: ```tact -struct JettonWalletData { - balance: Int as coins; - ownerAddress: Address; - jettonMasterAddress: Address; - jettonWalletCode: Cell; -} +import "@stdlib/jetton-interface"; fun calculateJettonWalletAddress( ownerAddress: Address, @@ -102,28 +114,25 @@ fun calculateJettonWalletAddress( jettonWalletCode: Cell ): Address { - let initData = JettonWalletData{ + // Notice that the data layout of Jettons isn't fixed and can change. + // For an example of a different JettonWalletData, + // see the "USDT Jetton operations" section below. + let initData = JettonWalletData { balance: 0, - ownerAddress, - jettonMasterAddress, - jettonWalletCode, + owner: ownerAddress, + minter: jettonMasterAddress, + code: jettonWalletCode, }; - return contractAddress(StateInit{ + return contractAddress(StateInit { code: jettonWalletCode, data: initData.toCell(), }); } -message(0x7362d09c) JettonTransferNotification { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - forwardPayload: Slice as remaining; -} -contract Example { - myJettonWalletAddress: Address; +contract ExampleJettonReceiver { + myJettonWalletAddress: Address?; myJettonAmount: Int as coins = 0; init(jettonWalletCode: Cell, jettonMasterAddress: Address) { @@ -140,10 +149,11 @@ contract Example { receive(msg: JettonTransferNotification) { require( - sender() == self.myJettonWalletAddress, + sender() == self.myJettonWalletAddress!!, "Notification not from your jetton wallet!", ); + // handle the transfer self.myJettonAmount += msg.amount; // Forward excesses @@ -159,39 +169,27 @@ A Jetton transfer is the process of sending a specified amount of Jettons from o To send a Jetton transfer, use the [`send(){:tact}`](/book/send) function. ```tact -message(0xf8a7ea5) JettonTransfer { - queryId: Int as uint64; - amount: Int as coins; - destination: Address; - responseDestination: Address?; - customPayload: Cell? = null; - forwardTonAmount: Int as coins; - forwardPayload: Slice as remaining; -} +import "@stdlib/jetton-interface"; const JettonTransferGas: Int = ton("0.05"); -struct JettonWalletData { - balance: Int as coins; - ownerAddress: Address; - jettonMasterAddress: Address; - jettonWalletCode: Cell; -} - fun calculateJettonWalletAddress( ownerAddress: Address, jettonMasterAddress: Address, - jettonWalletCode: Cell, + jettonWalletCode: Cell ): Address { - let initData = JettonWalletData{ + // Notice that the data layout of Jettons isn't fixed and can change. + // For an example of a different JettonWalletData, + // see the "USDT Jetton operations" section below. + let initData = JettonWalletData { balance: 0, - ownerAddress, - jettonMasterAddress, - jettonWalletCode, + owner: ownerAddress, + minter: jettonMasterAddress, + code: jettonWalletCode, }; - return contractAddress(StateInit{ + return contractAddress(StateInit { code: jettonWalletCode, data: initData.toCell(), }); @@ -201,8 +199,8 @@ message Withdraw { amount: Int as coins; } -contract Example { - myJettonWalletAddress: Address; +contract ExampleJettonSender { + myJettonWalletAddress: Address?; myJettonAmount: Int as coins = 0; init(jettonWalletCode: Cell, jettonMasterAddress: Address) { @@ -223,8 +221,8 @@ contract Example { "Not enough funds to withdraw" ); - send(SendParameters{ - to: self.myJettonWalletAddress, + send(SendParameters { + to: self.myJettonWalletAddress!!, value: JettonTransferGas, body: JettonTransfer{ // Unique identifier used to trace transactions across multiple contracts @@ -256,39 +254,33 @@ contract Example { ### Burning Jetton -Jetton burning is the process of permanently removing a specified amount of Jettons from circulation, with no possibility of recovery. +Jetton burning permanently removes a specified amount of Jettons from circulation, with no possibility of recovery. -```tact -message(0x595f07bc) JettonBurn { - queryId: Int as uint64; - amount: Int as coins; - responseDestination: Address?; - customPayload: Cell? = null; -} +The process is different in Jettons with or without a governance mechanism, e.g., the [stablecoin](https://github.com/ton-blockchain/stablecoin-contract): the former can burn tokens in their Jetton Wallets, but the latter only allows burning from within the Jetton Masters (Minters). -const JettonBurnGas: Int = ton("0.05"); +The following example is given for the non-governance Jettons: -struct JettonWalletData { - balance: Int as coins; - ownerAddress: Address; - jettonMasterAddress: Address; - jettonWalletCode: Cell; -} +```tact +import "@stdlib/jetton-interface"; +const JettonBurnGas: Int = ton("0.05"); fun calculateJettonWalletAddress( ownerAddress: Address, jettonMasterAddress: Address, - jettonWalletCode: Cell, + jettonWalletCode: Cell ): Address { - let initData = JettonWalletData{ + // Notice that the data layout of Jettons isn't fixed and can change. + // For an example of a different JettonWalletData, + // see the "USDT Jetton operations" section below. + let initData = JettonWalletData { balance: 0, - ownerAddress, - jettonMasterAddress, - jettonWalletCode, + owner: ownerAddress, + minter: jettonMasterAddress, + code: jettonWalletCode, }; - return contractAddress(StateInit{ + return contractAddress(StateInit { code: jettonWalletCode, data: initData.toCell(), }); @@ -298,8 +290,8 @@ message ThrowAway { amount: Int as coins; } -contract Example { - myJettonWalletAddress: Address; +contract ExampleJettonBurner { + myJettonWalletAddress: Address?; myJettonAmount: Int as coins = 0; init(jettonWalletCode: Cell, jettonMasterAddress: Address) { @@ -321,9 +313,9 @@ contract Example { ); send(SendParameters{ - to: self.myJettonWalletAddress, + to: self.myJettonWalletAddress!!, value: JettonBurnGas, - body: JettonBurn{ + body: JettonBurn { // Unique identifier used to trace transactions across multiple contracts queryId: 42, // Jetton amount you want to burn @@ -331,7 +323,7 @@ contract Example { // Destination address for a confirmation notice of a successful burn // and the remainder of the incoming message value responseDestination: sender(), - // Can be used for custom logic of Jettons; + // Can be used for custom logic of Jettons; // if unused, it can be set to null customPayload: null, }.toCell(), diff --git a/docs/src/content/docs/ref/standard-libraries.mdx b/docs/src/content/docs/ref/standard-libraries.mdx index c99db7fecd..87e3c1e42c 100644 --- a/docs/src/content/docs/ref/standard-libraries.mdx +++ b/docs/src/content/docs/ref/standard-libraries.mdx @@ -23,6 +23,7 @@ Library | Description [`@stdlib/content`][2] | Encoding off-chain link [strings][p] into a [`Cell{:tact}`][cell]. | [`createOffchainContent(){:tact}`][coff] [`@stdlib/deploy`][3] | Unified mechanism for deployments. | **Deprecated**: [`Deployable{:tact}`][dep], [`FactoryDeployable{:tact}`][fcd] [`@stdlib/dns`][4] | Resolution of [DNS][dns] names. | [`DNSResolver{:tact}`][dnsr], [`dnsInternalVerify(){:tact}`][dnsi] +[`@stdlib/jetton-interface`][7] | Structures for working with the [Jetton standard][jt]. | [`JettonMinterData{:tact}`][jmd], [`JettonWalletData(){:tact}`][jwd] [`@stdlib/ownable`][5] | Traits for ownership management. | [`Ownable{:tact}`][own], [`OwnableTransferable{:tact}`][ownt] [`@stdlib/stoppable`][6] | Traits that allow contracts to be stopped. Requires [@stdlib/ownable][5]. | [`Stoppable{:tact}`][stp], [`Resumable{:tact}`][res] @@ -49,6 +50,11 @@ Library | Description [stp]: /ref/stdlib-stoppable#stoppable [res]: /ref/stdlib-stoppable#resumable +[7]: /ref/stdlib-jetton-interface +[jt]: /cookbook/jettons +[jmd]: /ref/stdlib-jetton-interface#jettonminterdata +[jwd]: /ref/stdlib-jetton-interface#jettonwalletdata + [p]: /book/types#primitive-types [cell]: /book/cells#cells [dns]: https://docs.ton.org/participate/web3/dns diff --git a/docs/src/content/docs/ref/stdlib-jetton-interface.mdx b/docs/src/content/docs/ref/stdlib-jetton-interface.mdx new file mode 100644 index 0000000000..eaf89e0032 --- /dev/null +++ b/docs/src/content/docs/ref/stdlib-jetton-interface.mdx @@ -0,0 +1,698 @@ +--- +title: "@stdlib/jetton-interface" +description: "Provides primary and auxiliary structures for working with the TON Jetton standard" +--- + +import { Badge } from '@astrojs/starlight/components'; + +

+ +The `@stdlib/jetton-interface` library provides primary and auxiliary structures and utilities for working with [Jetton standards](#sources). However, support of the [stablecoin contract and standard][stablecoin] is out of the scope of this library. + +Jettons are fungible tokens similar to ERC-20 tokens on Ethereum but implemented specifically for the TON Blockchain. They use an actor model of contract communication and a parent-child-like contract sharding, where a Jetton Master (or Minter) acts like a parent contract, and Jetton Wallets are child contracts created for each user's wallet. + +To use this library, import `@stdlib/jetton-interface`: + +```tact +import "@stdlib/jetton-interface"; +``` + +:::note + + See how to apply this library in your contracts: + * [Common examples of working with Jettons in the Cookbook](/cookbook/jettons). + * [An official standalone defi-cookbook repository with common cases, Tact examples and tests](https://github.com/tact-lang/defi-cookbook/tree/main/jettons). + + Notice that support of the [stablecoin contract and standard][stablecoin] is out of the scope of this library. + +::: + +## Structs + +### JettonMinterData + +```tact +struct JettonMinterData { + /// Total supply of this Jetton Minter — the amount of tokens. + totalSupply: Int as coins; + + /// Whether the tokens are still mintable. + mintable: Bool; + + /// Owner of the Jetton Minter — the regular TON wallet address. + owner: Address; + + /// Jetton content metadata, such as its name, description, symbol, etc. + content: Cell; + + /// Code of the child Jetton Wallet contract that Jetton Minter would + /// deploy for each user's TON wallet. + jettonWalletCode: Cell; +} +``` + +Describes the data returned by the Jetton Minter getter function [`get_jetton_data()`](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#get-methods-1), which is defined in [TEP-74][tep-74] and should be present in every Jetton Minter implementation. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + totalSupply: Int as coins, + owner: Address, + jettonContent: Cell, + mintable: Bool, +) { + // ... + get fun get_jetton_data(): JettonMinterData { + return JettonMinterData { + totalSupply: self.totalSupply, + mintable: self.mintable, + adminAddress: self.owner, + jettonContent: self.jettonContent, + jettonWalletCode: codeOf JettonWallet, + }; + } + // ... +} +``` + +### JettonWalletData + +```tact +struct JettonWalletData { + /// Balance of the Jetton Wallet in tokens. + balance: Int as coins; + + /// Owner of the Jetton Wallet — the regular TON wallet address. + owner: Address; + + /// Address of the parent Jetton Minter contract. + minter: Address; + + /// Code of this Jetton Wallet contract. + code: Cell; +} +``` + +Describes the data returned by the Jetton Wallet getter function [`get_wallet_data()`](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#get-methods), which is defined in [TEP-74][tep-74] and should be present in every Jetton Wallet implementation. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonWallet( + owner: Address, + minter: Address, + balance: Int as coins, +) { + // ... + get fun get_wallet_data(): JettonWalletData { + return JettonWalletData { + owner: self.owner, + minter: self.minter, + balance: self.balance, + code: myCode(), + }; + } + // ... +} +``` + +## Messages + +### JettonTransfer + +```tact +message(0xf8a7ea5) JettonTransfer { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Amount of tokens to be transferred. + amount: Int as coins; + + /// A new owner of the transferred tokens — the regular TON wallet address. + destination: Address; + + /// Address where to send a response confirming a successful transfer + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; + + /// Optional custom data received from another Jetton Wallet. + customPayload: Cell?; + + /// The amount of nanotons to be sent to the `destination` address. + forwardTonAmount: Int as coins; + + /// Optional custom data to be sent to the `destination` address. + forwardPayload: Slice as remaining; // (Either Cell ^Cell) +} +``` + +Represents a message body defined in [TEP-74][tep-74]: it is sent to one Jetton Wallet contract to initialize a [transfer of tokens to another Jetton Wallet](#jettontransferinternal). + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonWallet( + owner: Address, + balance: Int as coins, + // ... +) { + receive(msg: JettonTransfer) { + forceBasechain(msg.destination); + require(sender() == self.owner, "Incorrect sender"); + + self.balance -= msg.amount; + require(self.balance >= 0, "Incorrect balance after send"); + + // ...further checks and actions... + } + + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +### JettonTransferInternal + +```tact +message(0x178d4519) JettonTransferInternal { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Amount of transferred tokens. + amount: Int as coins; + + /// Previous owner of the transferred tokens — the regular TON wallet address. + sender: Address; + + /// Address where to send a response confirming a successful transfer + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; + + /// The amount of nanotons to be sent to the new owner's address. + forwardTonAmount: Int as coins; + + /// Optional custom data to be sent to the new owner's address. + forwardPayload: Slice as remaining; // (Either Cell ^Cell) +} +``` + +Represents a message body defined in [TEP-74][tep-74]: it is sent and received by the Jetton Wallet contract to make a transfer or process an incoming one, respectively. + +Usage examples: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonWallet( + owner: Address, + minter: Address, + balance: Int as coins, +) { + receive(msg: JettonTransfer) { + forceBasechain(msg.destination); + require(sender() == self.owner, "Incorrect sender"); + + // ...other intermediate checks... + + deploy(DeployParameters { + value: 0, + mode: SendRemainingValue, + bounce: true, + body: JettonTransferInternal { + queryId: msg.queryId, + amount: msg.amount, + sender: self.owner, + responseDestination: msg.responseDestination, + forwardTonAmount: msg.forwardTonAmount, + forwardPayload: msg.forwardPayload, + }.toCell(), + init: initOf JettonWallet(0, msg.destination, self.minter), + }); + } + + receive(msg: JettonTransferInternal) { + self.balance += msg.amount; + + // This message should come only from master, or from other JettonWallet + let wallet: StateInit = initOf JettonWallet(0, msg.sender, self.minter); + if (!wallet.hasSameBasechainAddress(sender())) { + require(self.minter == sender(), "Incorrect sender"); + } + + // ...further actions... + } + + bounced(msg: bounced) { + self.balance += msg.amount; + } + + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +### JettonTransferNotification + +```tact +message(0x7362d09c) JettonTransferNotification { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Amount of transferred tokens. + amount: Int as coins; + + /// Previous owner of the transferred tokens — the regular TON wallet address. + sender: Address; + + /// Optional custom data to be sent to the new owner's address. + forwardPayload: Slice as remaining; // (Either Cell ^Cell) +} +``` + +Represents a message body defined in [TEP-74][tep-74]: it is sent from the Jetton Wallet contract to the `self.owner` address to notify about the [incoming transfer of tokens from their previous owner](#jettontransferinternal) — the `sender`. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonWallet( + owner: Address, + // ... +) { + // ... + receive(msg: JettonTransferInternal) { + // ...prior checks... + + let msgValue: Int = ctx.value; + if (msg.forwardTonAmount > 0) { + let fwdFee: Int = ctx.readForwardFee(); + msgValue -= msg.forwardTonAmount + fwdFee; + message(MessageParameters { + to: self.owner, + value: msg.forwardTonAmount, + mode: SendPayGasSeparately, + bounce: false, + body: JettonTransferNotification { + queryId: msg.queryId, + amount: msg.amount, + sender: msg.sender, + forwardPayload: msg.forwardPayload, + }.toCell(), + }); + } + + // ...further actions... + } + // ... +} +``` + +### JettonBurn + +```tact +message(0x595f07bc) JettonBurn { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Amount of burned tokens. + amount: Int as coins; + + /// Address where to send a response confirming a successful burn + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; + + /// Optional custom data received from another Jetton Wallet. + customPayload: Cell?; +} +``` + +Represents a message body defined in [TEP-74][tep-74]: it is sent to the Jetton Wallet contract from its TON wallet owner to decrease its balance by a certain `amount`. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonWallet( + owner: Address, + balance: Int as coins, + // ... +) { + // ... + receive(msg: JettonBurn) { + require(sender() == self.owner, "Incorrect sender"); + self.balance -= msg.amount; + require(self.balance >= 0, "Incorrect balance after send"); + // ...further actions... + } + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +### JettonBurnNotification + +```tact +message(0x7bdd97de) JettonBurnNotification { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Amount of burned tokens. + amount: Int as coins; + + /// Previous owner of the burned tokens — the regular TON wallet address. + sender: Address; + + /// Address where to send a response confirming a successful burn + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; +} +``` + +Represents a message body defined in [TEP-74][tep-74]: it is sent from the Jetton Wallet contract to the Jetton Minter to notify about the successful burn and reduce the total supply of tokens. + +Usage examples: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + // ... +) { + // ... + receive(msg: JettonBurnNotification) { + let sender = parseStdAddress(sender().asSlice()); + let wallet = getJettonBasechainWalletByOwner(msg.sender); + + require( + sender.workchain == 0 && sender.address == wallet.hash!!, + "Unauthorized burn", + ); + // ...further actions... + } + // ... +} + +contract JettonWallet( + balance: Int as coins, + // ... +) { + // ... + bounced(msg: bounced) { + self.balance += msg.amount; + } + // ... +} +``` + +### JettonExcesses + +```tact +message(0xd53276db) JettonExcesses { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; +} +``` + +Represents a message body defined in [TEP-74][tep-74]: it is sent from either Jetton Minter or Jetton Wallet to refund the remaining Toncoin from the incoming message. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + // ... +) { + // ... + receive(msg: JettonBurnNotification) { + // ... + if (msg.responseDestination != null) { + message(MessageParameters { + to: msg.responseDestination!!, + body: JettonExcesses { queryId: msg.queryId }.toCell(), + value: 0, + bounce: false, // do not bounce + mode: SendRemainingValue | SendIgnoreErrors, + }); + } + } + // ... +} +``` + +### ProvideWalletAddress + +```tact +message(0x2c76b973) ProvideWalletAddress { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Owner of the requested Jetton Wallet — the regular TON wallet address. + ownerAddress: Address; + + /// Whether the `ownerAddress` should be included in the response. + includeAddress: Bool; +} +``` + +Represents a message body defined in [TEP-89][tep-89]: it is sent to the Jetton Minter contract to request the Jetton Wallet address for the `ownerAddress`. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + // ... +) { + // ... + receive(msg: ProvideWalletAddress) { + let ownerWorkchain = parseStdAddress(msg.ownerAddress.asSlice()).workchain; + // ...further actions... + } + // ... +} +``` + +### TakeWalletAddress + +```tact +message(0xd1735400) TakeWalletAddress { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Address of the requested Jetton Wallet. + walletAddress: Address; + + /// Owner of the requested Jetton Wallet — the regular TON wallet address. + ownerAddress: Cell?; +} +``` + +Represents a message body defined in [TEP-89][tep-89]: it is sent from the Jetton Minter contract in response to a [request for the Jetton Wallet address](#providewalletaddress) for the `ownerAddress`. + +### JettonUpdateContent + +```tact +message(4) JettonUpdateContent { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// New Jetton content metadata, such as its name, description, symbol, etc. + newContent: Cell; +} +``` + +Represents a message body often used in Jetton implementations: it is sent to the Jetton Minter contract to update the Jetton content metadata, which is defined in [TEP-64][tep-64]. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + owner: Address, + jettonContent: Cell, + // ... +) { + // ... + receive(msg: JettonUpdateContent) { + require(sender() == self.owner, "Incorrect sender"); + self.jettonContent = msg.content; + } + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +### Mint + +```tact +message(0x642b7d07) Mint { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// Recipient of the minted tokens — the regular TON wallet address. + receiver: Address; + + /// The amount of nanotons to be sent to the `receiver` during the transfer. + tonAmount: Int as coins; + + /// If the Jetton Wallet for the given `receiver` does not exist, this message + /// will be used for its deployment and minting. Otherwise, only mints the tokens + /// for the `receiver` using their corresponding Jetton Wallet. + mintMessage: JettonTransferInternal; +} +``` + +Represents a message body often used in Jetton implementations: it is sent to the Jetton Minter contract to mint `mintMessage.amount` of tokens for the `receiver`. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + owner: Address, + mintable: Bool, + // ... +) { + // ... + receive(msg: Mint) { + require(sender() == self.owner, "Incorrect sender"); + require(self.mintable, "Mint is closed"); + // ...further actions... + } + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +:::note + + The [stablecoin][stablecoin] standard and contracts and some other Jetton implementations might use a different [opcode for this message](/book/structs-and-messages#message-opcodes), namely `0x642b7d07{:tact}`. + +::: + +### CloseMinting + +```tact +message(22) CloseMinting {} +``` + +Represents a message body often used in Jetton implementations: it is sent to the Jetton Minter contract to disable the ability to [mint](#mint) new tokens. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + owner: Address, + mintable: Bool, + // ... +) { + // ... + receive(msg: CloseMinting) { + require(sender() == self.owner, "Incorrect sender"); + self.mintable = false; + cashback(sender()); + } + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +### ChangeOwner + +```tact +message(3) ChangeOwner { + /// Unique identifier used to trace transactions across multiple contracts. + queryId: Int as uint64; + + /// New owner of the contract. + newOwner: Address; +} +``` + +Represents a message body often used in Jetton implementations: it is sent to the Jetton Minter contract to change the owner to `newOwner`. + +Usage example: + +```tact +import "@stdlib/jetton-interface"; + +contract JettonMinter( + owner: Address, + // ... +) { + // ... + receive(msg: ChangeOwner) { + require(sender() == self.owner, "Incorrect sender"); + self.owner = msg.newOwner; + } + // ... +} +``` + +:::note + + Always verify the sender of the message before taking any further actions. + +::: + +## Sources + +* [TEP-74: Fungible tokens (Jettons) standard][tep-74]. +* [TEP-64: Token Data Standard][tep-64]. +* [TEP-89: Discoverable Jetton Wallets][tep-89]. +* [`@stdlib/jetton-interface` Tact source](https://github.com/tact-lang/tact/blob/main/stdlib/libs/jetton.tact). + +[jetton-minter]: https://github.com/tact-lang/jetton/blob/4267da0e015edc8e65c846663641432c34e1fe53/src/contracts/base/jetton-minter.tact +[jetton-wallet]: https://github.com/tact-lang/jetton/blob/4267da0e015edc8e65c846663641432c34e1fe53/src/contracts/base/jetton-wallet.tact + +[tep-74]: https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md +[tep-64]: https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md +[tep-89]: https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md + +[stablecoin]: https://github.com/ton-blockchain/stablecoin-contract diff --git a/docs/src/content/docs/zh-cn/cookbook/dexes/dedust.mdx b/docs/src/content/docs/zh-cn/cookbook/dexes/dedust.mdx index 983e7098fc..199db0e5e5 100644 --- a/docs/src/content/docs/zh-cn/cookbook/dexes/dedust.mdx +++ b/docs/src/content/docs/zh-cn/cookbook/dexes/dedust.mdx @@ -80,7 +80,7 @@ struct SwapParams { /// https://docs.dedust.io/reference/tlb-schemes#message-swap message(0xea06185d) NativeSwap { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains + // Defaults to 0, which means we don't mark messages to trace requests queryId: Int as uint64 = 0; // Toncoin amount for the swap @@ -156,7 +156,7 @@ fun swapJetton(targetJettonWalletAddress: Address) { value: ton("0.3"), body: JettonTransfer{ // Unique identifier used to trace transactions across multiple contracts - // Set to 0, which means we don't mark messages to trace their chains + // Set to 0, which means we don't mark messages to trace requests queryId: 0, // Jetton amount for the swap amount: 10, // NOTE: change to yours @@ -260,7 +260,7 @@ message(0x40e108d6) JettonDepositLiquidity { /// https://docs.dedust.io/reference/tlb-schemes#message-deposit_liquidity message(0xd55e4686) NativeDepositLiquidity { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains + // Defaults to 0, which means we don't mark messages to trace requests queryId: Int as uint64 = 0; // Toncoin amount for the deposit diff --git a/docs/src/content/docs/zh-cn/cookbook/dexes/stonfi.mdx b/docs/src/content/docs/zh-cn/cookbook/dexes/stonfi.mdx index ccc8f1f6fc..25af446f47 100644 --- a/docs/src/content/docs/zh-cn/cookbook/dexes/stonfi.mdx +++ b/docs/src/content/docs/zh-cn/cookbook/dexes/stonfi.mdx @@ -318,7 +318,7 @@ message(0xf8a7ea5) JettonTransfer { /// https://github.com/ston-fi/sdk/blob/786ece758794bd5c575db8b38f5e5de19f43f0d1/packages/sdk/src/contracts/pTON/v2_1/PtonV2_1.ts message(0x01f3835d) ProxyToncoinTransfer { // Unique identifier used to trace transactions across multiple contracts - // Defaults to 0, which means we don't mark messages to trace their chains + // Defaults to 0, which means we don't mark messages to trace requests queryId: Int as uint64 = 0; // Toncoin amount for the swap diff --git a/src/benchmarks/contracts/escrow.tact b/src/benchmarks/contracts/escrow.tact index 1dc545bed9..9640d6a1e6 100644 --- a/src/benchmarks/contracts/escrow.tact +++ b/src/benchmarks/contracts/escrow.tact @@ -1,13 +1,13 @@ -import "./messages"; +import "@stdlib/jetton-interface"; -inline fun calculateJettonWalletAddress(owner: Address, master: Address, code: Cell): Address { - let initData = JettonWalletData{balance: 0, owner, master, code}; - return contractAddress(StateInit{code: code, data: initData.toCell()}); +inline fun calculateJettonWalletAddress(owner: Address, minter: Address, code: Cell): Address { + let initData = JettonWalletData { balance: 0, owner, minter, code }; + return contractAddress(StateInit { code: code, data: initData.toCell() }); } -inline fun calculateJettonBasechainWalletAddress(owner: Address, master: Address, code: Cell): BasechainAddress { - let initData = JettonWalletData{balance: 0, owner, master, code}; - return contractBasechainAddress(StateInit{code: code, data: initData.toCell()}); +inline fun calculateJettonBasechainWalletAddress(owner: Address, minter: Address, code: Cell): BasechainAddress { + let initData = JettonWalletData { balance: 0, owner, minter, code }; + return contractBasechainAddress(StateInit { code: code, data: initData.toCell() }); } message(0x9eacde91) UpdateJettonWalletCode { @@ -34,7 +34,7 @@ struct EscrowData { buyerAddress: Address?; // null if not funded, set after funding } -contract Escrow ( +contract Escrow( id: Int as uint32, sellerAddress: Address, guarantorAddress: Address, @@ -45,38 +45,37 @@ contract Escrow ( assetAddress: Address?, jettonWalletCode: Cell?, ) { - const GUARANTOR_PERCENT_CONST: Int = 100000; const GUARANTOR_PERCENT_MAX: Int = 90000; const JETTON_TRANSFER_GAS: Int = ton("0.05"); const TON_TRANSFER_GAS: Int = ton("0.015"); - + fun sendJettons(receiver: Address, amount: Int, mode: Int) { - let escrowJettonWalletAddress = calculateJettonWalletAddress(myAddress(), + let escrowJettonWalletAddress = calculateJettonWalletAddress( + myAddress(), self.assetAddress!!, - self.jettonWalletCode!! - ); - message(MessageParameters{ - to: escrowJettonWalletAddress, - value: self.JETTON_TRANSFER_GAS, - body: JettonTransfer{ - queryId: 0, - amount: amount, - destination: receiver, - responseDestination: receiver, - forwardTonAmount: ton("0.01"), - forwardPayload: emptySlice(), - customPayload: null - }.toCell(), - mode: mode - } + self.jettonWalletCode!!, ); + message(MessageParameters { + to: escrowJettonWalletAddress, + value: self.JETTON_TRANSFER_GAS, + body: JettonTransfer { + queryId: 0, + amount: amount, + destination: receiver, + responseDestination: receiver, + forwardTonAmount: ton("0.01"), + forwardPayload: emptySlice(), + customPayload: null, + }.toCell(), + mode: mode, + }); } receive(_: Funding) { throwIf(400, self.isFunded); throwUnless(400, self.assetAddress == null); - + let ctx: Context = context(); throwUnless(401, ctx.value == self.dealAmount); @@ -92,13 +91,14 @@ contract Escrow ( self.jettonWalletCode = msg.newJettonWalletCode; } - receive(msg: JettonNotification) { + receive(msg: JettonTransferNotification) { throwUnless(400, !self.isFunded); throwUnless(400, self.assetAddress != null); - let escrowJettonWalletAddress = calculateJettonBasechainWalletAddress(myAddress(), + let escrowJettonWalletAddress = calculateJettonBasechainWalletAddress( + myAddress(), self.assetAddress!!, - self.jettonWalletCode!! + self.jettonWalletCode!!, ); let sender = parseStdAddress(sender().asSlice()); @@ -122,18 +122,16 @@ contract Escrow ( if (self.assetAddress == null) { throwUnless(404, ctx.value > (2 * self.TON_TRANSFER_GAS)); - message(MessageParameters{ - to: self.sellerAddress, - value: self.dealAmount - royaltyAmount, - mode: SendPayFwdFeesSeparately - } - ); - message(MessageParameters{ - to: self.guarantorAddress, - value: royaltyAmount, - mode: SendRemainingBalance | SendDestroyIfZero // send all remaining and destroy escrow - } - ); + message(MessageParameters { + to: self.sellerAddress, + value: self.dealAmount - royaltyAmount, + mode: SendPayFwdFeesSeparately, + }); + message(MessageParameters { + to: self.guarantorAddress, + value: royaltyAmount, + mode: SendRemainingBalance | SendDestroyIfZero, // send all remaining and destroy escrow + }); } else { throwUnless(404, ctx.value > (2 * self.JETTON_TRANSFER_GAS)); self.sendJettons(self.sellerAddress, self.dealAmount - royaltyAmount, SendPayFwdFeesSeparately); @@ -149,12 +147,11 @@ contract Escrow ( // it's up to business logic to decide if guarantor gets royalty in case of cancel // in this implementation we don't pay royalty in case of cancel if (self.assetAddress == null) { - message(MessageParameters{ - to: self.buyerAddress!!, - value: self.dealAmount, - mode: SendRemainingBalance | SendDestroyIfZero // send all remaining and destroy escrow - } - ); + message(MessageParameters { + to: self.buyerAddress!!, + value: self.dealAmount, + mode: SendRemainingBalance | SendDestroyIfZero, // send all remaining and destroy escrow + }); } else { self.sendJettons(self.buyerAddress!!, self.dealAmount, SendRemainingBalance | SendDestroyIfZero); } @@ -162,11 +159,11 @@ contract Escrow ( // on-chain data access, could be useful to guarantor contract receive(_: ProvideEscrowData) { - message(MessageParameters{ + message(MessageParameters { bounce: false, value: 0, to: sender(), - body: TakeEscrowData{ + body: TakeEscrowData { escrowData: EscrowData { id: self.id, sellerAddress: self.sellerAddress, @@ -176,10 +173,10 @@ contract Escrow ( isFunded: self.isFunded, assetAddress: self.assetAddress, jettonWalletCode: self.jettonWalletCode, - buyerAddress: self.buyerAddress - } + buyerAddress: self.buyerAddress, + }, }.toCell(), - mode: SendRemainingValue + mode: SendRemainingValue, }); } diff --git a/src/benchmarks/contracts/jetton-minter-discoverable.tact b/src/benchmarks/contracts/jetton-minter-discoverable.tact index 00a60a9fc3..d733e59a14 100644 --- a/src/benchmarks/contracts/jetton-minter-discoverable.tact +++ b/src/benchmarks/contracts/jetton-minter-discoverable.tact @@ -1,19 +1,12 @@ // https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md +import "@stdlib/jetton-interface"; import "./jetton-wallet"; import "./messages"; const ProvideAddressGasConsumption: Int = ton("0.01"); const Workchain: Int = 0; -struct JettonMasterState { - totalSupply: Int as coins; - mintable: Bool; - adminAddress: Address; - jettonContent: Cell; - jettonWalletCode: Cell; -} - contract JettonMinter( totalSupply: Int as coins, owner: Address, @@ -29,8 +22,8 @@ contract JettonMinter( if (msg.responseDestination.isNotNone()) { message(MessageParameters { - to: msg.responseDestination, - body: JettonExcesses{ queryId: msg.queryId }.toCell(), + to: msg.responseDestination!!, + body: JettonExcesses { queryId: msg.queryId }.toCell(), value: 0, bounce: false, mode: SendRemainingValue | SendIgnoreErrors, // ignore errors, because supply has already been updated @@ -61,19 +54,19 @@ contract JettonMinter( receive(msg: JettonUpdateContent) { throwUnless(73, sender() == self.owner); - self.jettonContent = msg.content; + self.jettonContent = msg.newContent; } receive(msg: Mint) { throwUnless(73, sender() == self.owner); self.totalSupply += msg.mintMessage.amount; - deploy(DeployParameters{ + deploy(DeployParameters { value: 0, bounce: true, mode: SendRemainingValue, body: msg.mintMessage.toCell(), - init: getJettonWalletInit(msg.receiver) + init: getJettonWalletInit(msg.receiver), }); } @@ -84,14 +77,14 @@ contract JettonMinter( receive(_: Slice) { throw(0xffff) } - get fun get_jetton_data(): JettonMasterState { - return JettonMasterState { + get fun get_jetton_data(): JettonMinterData { + return JettonMinterData { totalSupply: self.totalSupply, mintable: true, - adminAddress: self.owner, - jettonContent: self.jettonContent, + owner: self.owner, + content: self.jettonContent, jettonWalletCode: codeOf JettonWallet, - } + }; } get fun get_wallet_address(ownerAddress: Address): Address { @@ -102,8 +95,7 @@ contract JettonMinter( asm fun emptyAddress(): Address { b{00} PUSHSLICE } inline fun makeTakeWalletAddressMsg(targetJettonWallet: BasechainAddress, msg: ProvideWalletAddress): Cell { - return - beginCell() + return beginCell() .storeUint(TakeWalletAddressOpcode, 32) .storeUint(msg.queryId, 64) .storeBasechainAddress(targetJettonWallet) diff --git a/src/benchmarks/contracts/jetton-minter-notcoin.tact b/src/benchmarks/contracts/jetton-minter-notcoin.tact index 792b7a68ff..9df2248593 100644 --- a/src/benchmarks/contracts/jetton-minter-notcoin.tact +++ b/src/benchmarks/contracts/jetton-minter-notcoin.tact @@ -1,16 +1,9 @@ +import "@stdlib/jetton-interface"; import "./messages"; import "./jetton-wallet-notcoin"; const Workchain: Int = 0; -struct JettonMasterState { - totalSupply: Int as coins; - mintable: Bool; - adminAddress: Address; - jettonContent: Cell; - jettonWalletCode: Cell; -} - contract JettonMinterNotcoin( totalSupply: Int as coins, owner: Address, @@ -27,8 +20,8 @@ contract JettonMinterNotcoin( if (msg.responseDestination.isNotNone()) { message(MessageParameters { - to: msg.responseDestination, - body: JettonExcesses{ queryId: msg.queryId }.toCell(), + to: msg.responseDestination!!, + body: JettonExcesses { queryId: msg.queryId }.toCell(), value: 0, bounce: false, mode: SendRemainingValue | SendIgnoreErrors, // ignore errors, because supply has already been updated @@ -39,9 +32,8 @@ contract JettonMinterNotcoin( receive(msg: ProvideWalletAddress) { let ownerWorkchain: Int = parseStdAddress(msg.ownerAddress.asSlice()).workchain; - let targetJettonWallet: BasechainAddress = - (ownerWorkchain == Workchain) ? - contractBasechainAddress(initOf JettonWalletNotcoin(0, msg.ownerAddress, myAddress())) + let targetJettonWallet: BasechainAddress = (ownerWorkchain == Workchain) + ? contractBasechainAddress(initOf JettonWalletNotcoin(msg.ownerAddress, myAddress(), 0)) : emptyBasechainAddress(); message(MessageParameters { @@ -56,22 +48,22 @@ contract JettonMinterNotcoin( let ctx = context(); throwUnless(73, ctx.sender == self.owner); self.totalSupply += msg.mintMessage.amount; - + forceBasechain(msg.receiver); checkAmountIsEnoughToTransfer(ctx.value, msg.mintMessage.forwardTonAmount, ctx.readForwardFee()); - deploy(DeployParameters{ + deploy(DeployParameters { value: 0, bounce: true, mode: SendRemainingValue, body: msg.mintMessage.toCell(), - init: getJettonWalletInit(msg.receiver) + init: getJettonWalletInit(msg.receiver), }); } receive(msg: JettonUpdateContent) { throwUnless(73, sender() == self.owner); - self.jettonContent = msg.content; + self.jettonContent = msg.newContent; } receive(msg: ChangeAdmin) { @@ -96,14 +88,14 @@ contract JettonMinterNotcoin( receive(_: Slice) { throw(0xffff) } - get fun get_jetton_data(): JettonMasterState { - return JettonMasterState { + get fun get_jetton_data(): JettonMinterData { + return JettonMinterData { totalSupply: self.totalSupply, mintable: true, - adminAddress: self.owner, - jettonContent: self.jettonContent, + owner: self.owner, + content: self.jettonContent, jettonWalletCode: codeOf JettonWalletNotcoin, - } + }; } get fun get_wallet_address(ownerAddress: Address): Address { @@ -114,8 +106,7 @@ contract JettonMinterNotcoin( asm fun emptyAddress(): Address { b{00} PUSHSLICE } inline fun makeTakeWalletAddressMsg(targetJettonWallet: BasechainAddress, msg: ProvideWalletAddress): Cell { - return - beginCell() + return beginCell() .storeUint(TakeWalletAddressOpcode, 32) .storeUint(msg.queryId, 64) .storeBasechainAddress(targetJettonWallet) @@ -124,7 +115,7 @@ inline fun makeTakeWalletAddressMsg(targetJettonWallet: BasechainAddress, msg: P } inline fun getJettonWalletInit(address: Address): StateInit { - return initOf JettonWalletNotcoin(0, address, myAddress()); + return initOf JettonWalletNotcoin(address, myAddress(), 0); } inline fun getJettonWalletByOwner(jettonWalletOwner: Address): Address { diff --git a/src/benchmarks/contracts/jetton-wallet-notcoin.tact b/src/benchmarks/contracts/jetton-wallet-notcoin.tact index 316ac58036..74100caf4b 100644 --- a/src/benchmarks/contracts/jetton-wallet-notcoin.tact +++ b/src/benchmarks/contracts/jetton-wallet-notcoin.tact @@ -1,4 +1,4 @@ -import "./messages"; +import "@stdlib/jetton-interface"; // gas const SEND_TRANSFER_GAS_CONSUMPTION: Int = 10065; @@ -12,7 +12,7 @@ const MIN_STORAGE_DURATION: Int = 5 * 365 * 24 * 3600; // 5 years const JETTON_WALLET_BITS: Int = 1033; const JETTON_WALLET_CELLS: Int = 3; -const JETTON_WALLET_INIT_STATE_BITS: Int = 931; +const JETTON_WALLET_INIT_STATE_BITS: Int = 931; const JETTON_WALLET_INIT_STATE_CELLS: Int = 3; const BURN_NOTIFICATION_BITS: Int = 754; @@ -24,10 +24,10 @@ inline fun checkAmountIsEnoughToTransfer(msgValue: Int, forwardTonAmount: Int, f throwUnless( 705, msgValue > fwdCount * fwdFee + - getSimpleForwardFee(JETTON_WALLET_INIT_STATE_CELLS, JETTON_WALLET_INIT_STATE_BITS, false) + - getComputeFee(SEND_TRANSFER_GAS_CONSUMPTION, false) + - getComputeFee(RECEIVE_TRANSFER_GAS_CONSUMPTION, false) + - getStorageFee(JETTON_WALLET_CELLS, JETTON_WALLET_BITS, MIN_STORAGE_DURATION, false) + getSimpleForwardFee(JETTON_WALLET_INIT_STATE_CELLS, JETTON_WALLET_INIT_STATE_BITS, false) + + getComputeFee(SEND_TRANSFER_GAS_CONSUMPTION, false) + + getComputeFee(RECEIVE_TRANSFER_GAS_CONSUMPTION, false) + + getStorageFee(JETTON_WALLET_CELLS, JETTON_WALLET_BITS, MIN_STORAGE_DURATION, false), ); } @@ -37,14 +37,14 @@ inline fun checkAmountIsEnoughToBurn(msgValue: Int) { msgValue > getForwardFee(BURN_NOTIFICATION_CELLS, BURN_NOTIFICATION_BITS, false) + getComputeFee(SEND_BURN_GAS_CONSUMPTION, false) + - getComputeFee(RECEIVE_BURN_GAS_CONSUMPTION, false) + getComputeFee(RECEIVE_BURN_GAS_CONSUMPTION, false), ); } contract JettonWalletNotcoin( - balance: Int as coins, owner: Address, - master: Address, + minter: Address, + balance: Int as coins, ) { receive(msg: JettonTransfer) { forceBasechain(msg.destination); @@ -57,19 +57,19 @@ contract JettonWalletNotcoin( let ctx = context(); checkAmountIsEnoughToTransfer(ctx.value, msg.forwardTonAmount, ctx.readForwardFee()); - deploy(DeployParameters{ + deploy(DeployParameters { value: 0, mode: SendRemainingValue, bounce: true, - body: JettonTransferInternal{ + body: JettonTransferInternal { queryId: msg.queryId, amount: msg.amount, sender: self.owner, responseDestination: msg.responseDestination, forwardTonAmount: msg.forwardTonAmount, - forwardPayload: msg.forwardPayload + forwardPayload: msg.forwardPayload, }.toCell(), - init: initOf JettonWalletNotcoin(0, msg.destination, self.master), + init: initOf JettonWalletNotcoin(msg.destination, self.minter, 0), }); } @@ -77,18 +77,18 @@ contract JettonWalletNotcoin( self.balance += msg.amount; // This message should come only from master, or from other JettonWallet - let wallet: StateInit = initOf JettonWalletNotcoin(0, msg.sender, self.master); + let wallet: StateInit = initOf JettonWalletNotcoin(msg.sender, self.minter, 0); if (!wallet.hasSameBasechainAddress(sender())) { - throwUnless(707, self.master == sender()); + throwUnless(707, self.minter == sender()); } if (msg.forwardTonAmount > 0) { - message(MessageParameters{ + message(MessageParameters { to: self.owner, value: msg.forwardTonAmount, mode: SendPayFwdFeesSeparately, bounce: false, - body: JettonNotification{ // 0x7362d09c -- Remind the new Owner + body: JettonTransferNotification { // 0x7362d09c -- Remind the new Owner queryId: msg.queryId, amount: msg.amount, sender: msg.sender, @@ -102,15 +102,15 @@ contract JettonWalletNotcoin( let leaveOnBalance: Int = myBalance() - context().value + myStorageDue(); nativeReserve( max(leaveOnBalance, getStorageFee(JETTON_WALLET_CELLS, JETTON_WALLET_BITS, MIN_STORAGE_DURATION, false)), - 2 + 2, ); - message(MessageParameters{ + message(MessageParameters { to: msg.responseDestination!!, value: 0, mode: SendRemainingBalance | SendIgnoreErrors, bounce: false, - body: JettonExcesses{ queryId: msg.queryId }.toCell(), + body: JettonExcesses { queryId: msg.queryId }.toCell(), }); } } @@ -123,12 +123,12 @@ contract JettonWalletNotcoin( checkAmountIsEnoughToBurn(context().value); - message(MessageParameters{ - to: self.master, + message(MessageParameters { + to: self.minter, value: 0, mode: SendRemainingValue, bounce: true, - body: JettonBurnNotification{ + body: JettonBurnNotification { queryId: msg.queryId, amount: msg.amount, sender: self.owner, @@ -148,11 +148,11 @@ contract JettonWalletNotcoin( } get fun get_wallet_data(): JettonWalletData { - return JettonWalletData{ + return JettonWalletData { balance: self.balance, owner: self.owner, - master: self.master, - code: myCode() + minter: self.minter, + code: myCode(), }; } } diff --git a/src/benchmarks/contracts/jetton-wallet.tact b/src/benchmarks/contracts/jetton-wallet.tact index ded7775f22..c3a642d420 100644 --- a/src/benchmarks/contracts/jetton-wallet.tact +++ b/src/benchmarks/contracts/jetton-wallet.tact @@ -1,4 +1,4 @@ -import "./messages"; +import "@stdlib/jetton-interface"; contract JettonWallet( owner: Address, @@ -23,22 +23,21 @@ contract JettonWallet( let fwdCount = 1 + sign(msg.forwardTonAmount); // msg.forwardTonAmount is coins, so it's not negative throwUnless(709, ctx.value > - msg.forwardTonAmount + - fwdCount * ctx.readForwardFee() + - (2 * self.gasConsumption + self.minTonsForStorage) - ); + msg.forwardTonAmount + + fwdCount * ctx.readForwardFee() + + (2 * self.gasConsumption + self.minTonsForStorage)); - deploy(DeployParameters{ + deploy(DeployParameters { value: 0, mode: SendRemainingValue, bounce: true, - body: JettonTransferInternal{ + body: JettonTransferInternal { queryId: msg.queryId, amount: msg.amount, sender: self.owner, responseDestination: msg.responseDestination, forwardTonAmount: msg.forwardTonAmount, - forwardPayload: msg.forwardPayload + forwardPayload: msg.forwardPayload, }.toCell(), init: initOf JettonWallet(msg.destination, self.master, 0), }); @@ -62,12 +61,12 @@ contract JettonWallet( if (msg.forwardTonAmount > 0) { let fwdFee: Int = ctx.readForwardFee(); msgValue -= msg.forwardTonAmount + fwdFee; - message(MessageParameters{ + message(MessageParameters { to: self.owner, value: msg.forwardTonAmount, mode: SendPayFwdFeesSeparately, bounce: false, - body: JettonNotification{ // 0x7362d09c -- Remind the new Owner + body: JettonTransferNotification { // 0x7362d09c -- Remind the new Owner queryId: msg.queryId, amount: msg.amount, sender: msg.sender, @@ -78,12 +77,12 @@ contract JettonWallet( // 0xd53276db -- Cashback to the original Sender if (msg.responseDestination != null && msgValue > 0) { - message(MessageParameters{ + message(MessageParameters { to: msg.responseDestination!!, value: msgValue, mode: SendIgnoreErrors, bounce: false, - body: JettonExcesses{ queryId: msg.queryId }.toCell(), + body: JettonExcesses { queryId: msg.queryId }.toCell(), }); } } @@ -98,12 +97,12 @@ contract JettonWallet( let fwdFee: Int = ctx.readForwardFee(); throwUnless(707, ctx.value > (fwdFee + 2 * self.gasConsumption)); - message(MessageParameters{ + message(MessageParameters { to: self.master, value: 0, mode: SendRemainingValue, bounce: true, - body: JettonBurnNotification{ + body: JettonBurnNotification { queryId: msg.queryId, amount: msg.amount, sender: self.owner, @@ -123,11 +122,11 @@ contract JettonWallet( } get fun get_wallet_data(): JettonWalletData { - return JettonWalletData{ + return JettonWalletData { balance: self.balance, owner: self.owner, - master: self.master, - code: myCode() + minter: self.master, + code: myCode(), }; } } diff --git a/src/benchmarks/contracts/messages.tact b/src/benchmarks/contracts/messages.tact index deb09d9a34..b3c43913b1 100644 --- a/src/benchmarks/contracts/messages.tact +++ b/src/benchmarks/contracts/messages.tact @@ -1,95 +1,4 @@ -struct JettonData { - totalSupply: Int; - mintable: Bool; - owner: Address; - content: Cell; - jettonWalletCode: Cell; -} - -struct JettonWalletData { - balance: Int; - owner: Address; - master: Address; - code: Cell; -} - -struct MaybeAddress { - address: Address?; -} - -message(4) JettonUpdateContent { - queryId: Int as uint64; - content: Cell; -} - -message(0xf8a7ea5) JettonTransfer { - queryId: Int as uint64; - amount: Int as coins; - destination: Address; - responseDestination: Address?; - customPayload: Cell?; - forwardTonAmount: Int as coins; - forwardPayload: Slice as remaining; -} - -message(0x178d4519) JettonTransferInternal { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - responseDestination: Address?; - forwardTonAmount: Int as coins; - forwardPayload: Slice as remaining; -} - -message(0x7362d09c) JettonNotification { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - forwardPayload: Slice as remaining; -} - -message(0x595f07bc) JettonBurn { - queryId: Int as uint64; - amount: Int as coins; - responseDestination: Address; - customPayload: Cell?; -} - -message(0x7bdd97de) JettonBurnNotification { - queryId: Int as uint64; - amount: Int as coins; - sender: Address; - responseDestination: Address; -} - -message(0xd53276db) JettonExcesses { - queryId: Int as uint64; -} - -message(0x2c76b973) ProvideWalletAddress { - queryId: Int as uint64; - ownerAddress: Address; - includeAddress: Bool; -} - const TakeWalletAddressOpcode: Int = 0xd1735400; -message(TakeWalletAddressOpcode) TakeWalletAddress { - queryId: Int as uint64; - walletAddress: Address; - ownerAddress: Cell?; //It is Maybe ^Address, just encoded it like this -} - -message(21) Mint { - queryId: Int as uint64; - receiver: Address; - tonAmount: Int as coins; - mintMessage: JettonTransferInternal; -} - -message(3) ChangeOwner { - queryId: Int as uint64; - newOwner: Address; -} // notcoin message(0xd372158c) TopUp { diff --git a/src/benchmarks/escrow/results_code_size.json b/src/benchmarks/escrow/results_code_size.json index c78da5f5c9..90ce066836 100644 --- a/src/benchmarks/escrow/results_code_size.json +++ b/src/benchmarks/escrow/results_code_size.json @@ -63,6 +63,14 @@ "cells": "21", "bits": "11727" } + }, + { + "label": "1.6.5 with stdlib/jetton", + "pr": "https://github.com/tact-lang/tact/pull/2509", + "size": { + "cells": "21", + "bits": "11655" + } } ] } diff --git a/src/benchmarks/jetton/jetton.spec.ts b/src/benchmarks/jetton/jetton.spec.ts index aaa762f4d2..5ebc731822 100644 --- a/src/benchmarks/jetton/jetton.spec.ts +++ b/src/benchmarks/jetton/jetton.spec.ts @@ -1,12 +1,5 @@ import "@ton/test-utils"; -import { - Address, - beginCell, - Cell, - contractAddress, - SendMode, - toNano, -} from "@ton/core"; +import { Address, beginCell, Cell, toNano } from "@ton/core"; import type { SandboxContract, TreasuryContract } from "@ton/sandbox"; import { Blockchain } from "@ton/sandbox"; @@ -24,11 +17,10 @@ import { printBenchmarkTable, } from "@/benchmarks/utils/gas"; import benchmarkResults from "@/benchmarks/jetton/results_gas.json"; -import { join, resolve } from "path"; -import { readFileSync } from "fs"; -import { posixNormalize } from "@/utils/filePath"; +import { join } from "path"; import { type Step, writeLog } from "@/test/utils/write-vm-log"; import { + deployFuncJettonMinter, getJettonWalletRaw, sendBurnRaw, sendDiscoveryRaw, @@ -36,55 +28,6 @@ import { sendTransferRaw, } from "@/benchmarks/utils/jetton"; -const loadFunCJettonsBoc = () => { - const bocMinter = readFileSync( - posixNormalize( - resolve( - __dirname, - "../contracts/func/output/jetton-minter-discoverable.boc", - ), - ), - ); - - const bocWallet = readFileSync( - posixNormalize( - resolve(__dirname, "../contracts/func/output/jetton-wallet.boc"), - ), - ); - - return { bocMinter, bocWallet }; -}; - -const deployFuncJettonMinter = async ( - via: SandboxContract, -) => { - const jettonData = loadFunCJettonsBoc(); - const minterCell = Cell.fromBoc(jettonData.bocMinter)[0]!; - const walletCell = Cell.fromBoc(jettonData.bocWallet)[0]!; - - const stateInitMinter = beginCell() - .storeCoins(0) - .storeAddress(via.address) - .storeRef(beginCell().storeUint(1, 1).endCell()) // as salt - .storeRef(walletCell) - .endCell(); - - const init = { code: minterCell, data: stateInitMinter }; - - const minterAddress = contractAddress(0, init); - - return { - minterAddress, - result: await via.send({ - to: minterAddress, - value: toNano("0.1"), - init, - body: beginCell().endCell(), - sendMode: SendMode.PAY_GAS_SEPARATELY, - }), - }; -}; - describe("Jetton", () => { let blockchain: Blockchain; let jettonMinter: SandboxContract; @@ -131,7 +74,7 @@ describe("Jetton", () => { const msg: JettonUpdateContent = { $$type: "JettonUpdateContent", queryId: 0n, - content: new Cell(), + newContent: new Cell(), }; jettonMinter = blockchain.openContract( diff --git a/src/benchmarks/jetton/results_code_size.json b/src/benchmarks/jetton/results_code_size.json index 45d1570aa4..331cb04f1c 100644 --- a/src/benchmarks/jetton/results_code_size.json +++ b/src/benchmarks/jetton/results_code_size.json @@ -119,6 +119,16 @@ "wallet cells": "16", "wallet bits": "8211" } + }, + { + "label": "1.6.5 with stdlib/jetton", + "pr": "https://github.com/tact-lang/tact/pull/2509", + "size": { + "minter cells": "30", + "minter bits": "15008", + "wallet cells": "16", + "wallet bits": "8411" + } } ] } diff --git a/src/benchmarks/jetton/results_gas.json b/src/benchmarks/jetton/results_gas.json index 01c0d2aae9..7bb9a448a1 100644 --- a/src/benchmarks/jetton/results_gas.json +++ b/src/benchmarks/jetton/results_gas.json @@ -350,6 +350,15 @@ "burn": "11812", "discovery": "6161" } + }, + { + "label": "1.6.5 with stdlib/jetton", + "pr": "https://github.com/tact-lang/tact/pull/2509", + "gas": { + "transfer": "15143", + "burn": "12253", + "discovery": "6161" + } } ] } diff --git a/src/benchmarks/notcoin/notcoin.spec.ts b/src/benchmarks/notcoin/notcoin.spec.ts index e2f2743252..30cd7db059 100644 --- a/src/benchmarks/notcoin/notcoin.spec.ts +++ b/src/benchmarks/notcoin/notcoin.spec.ts @@ -141,7 +141,7 @@ describe("NotCoin", () => { const msg: JettonUpdateContent = { $$type: "JettonUpdateContent", queryId: 0n, - content: new Cell(), + newContent: new Cell(), }; jettonMinter = blockchain.openContract( diff --git a/src/benchmarks/notcoin/results_code_size.json b/src/benchmarks/notcoin/results_code_size.json index 2541b2f282..3d9fd68c95 100644 --- a/src/benchmarks/notcoin/results_code_size.json +++ b/src/benchmarks/notcoin/results_code_size.json @@ -59,6 +59,16 @@ "wallet cells": "14", "wallet bits": "8084" } + }, + { + "label": "1.6.5 with stdlib/jetton", + "pr": "https://github.com/tact-lang/tact/pull/2509", + "size": { + "minter cells": "31", + "minter bits": "16654", + "wallet cells": "15", + "wallet bits": "8404" + } } ] } diff --git a/src/benchmarks/notcoin/results_gas.json b/src/benchmarks/notcoin/results_gas.json index da407560f4..431f81ea90 100644 --- a/src/benchmarks/notcoin/results_gas.json +++ b/src/benchmarks/notcoin/results_gas.json @@ -53,6 +53,15 @@ "burn": "11919", "discovery": "5870" } + }, + { + "label": "1.6.5 with stdlib/jetton", + "pr": "https://github.com/tact-lang/tact/pull/2509", + "gas": { + "transfer": "15781", + "burn": "12342", + "discovery": "5870" + } } ] } diff --git a/src/benchmarks/utils/jetton.ts b/src/benchmarks/utils/jetton.ts index c91a6c4b95..9a9593d095 100644 --- a/src/benchmarks/utils/jetton.ts +++ b/src/benchmarks/utils/jetton.ts @@ -1,4 +1,5 @@ -import type { Address, Cell } from "@ton/core"; +import type { Address } from "@ton/core"; +import { Cell, contractAddress } from "@ton/core"; import { beginCell, Builder, SendMode, toNano } from "@ton/core"; import type { Blockchain, @@ -17,6 +18,58 @@ import { storeMint, storeProvideWalletAddress, } from "@/benchmarks/contracts/output/escrow_Escrow"; +import { readFileSync } from "fs"; +import { posixNormalize } from "@/utils/filePath"; +import { resolve } from "path"; + +const loadFunCJettonsBoc = () => { + const bocMinter = readFileSync( + posixNormalize( + resolve( + __dirname, + "../contracts/func/output/jetton-minter-discoverable.boc", + ), + ), + ); + + const bocWallet = readFileSync( + posixNormalize( + resolve(__dirname, "../contracts/func/output/jetton-wallet.boc"), + ), + ); + + return { bocMinter, bocWallet }; +}; + +export const deployFuncJettonMinter = async ( + via: SandboxContract, +) => { + const jettonData = loadFunCJettonsBoc(); + const minterCell = Cell.fromBoc(jettonData.bocMinter)[0]!; + const walletCell = Cell.fromBoc(jettonData.bocWallet)[0]!; + + const stateInitMinter = beginCell() + .storeCoins(0) + .storeAddress(via.address) + .storeRef(beginCell().storeUint(1, 1).endCell()) // as salt + .storeRef(walletCell) + .endCell(); + + const init = { code: minterCell, data: stateInitMinter }; + + const minterAddress = contractAddress(0, init); + + return { + minterAddress, + result: await via.send({ + to: minterAddress, + value: toNano("0.1"), + init, + body: beginCell().endCell(), + sendMode: SendMode.PAY_GAS_SEPARATELY, + }), + }; +}; export const sendDiscoveryRaw = async ( minterAddress: Address, diff --git a/src/stdlib/stdlib.ts b/src/stdlib/stdlib.ts index 52d67c23e0..6dfb8c0559 100644 --- a/src/stdlib/stdlib.ts +++ b/src/stdlib/stdlib.ts @@ -129,6 +129,251 @@ files["libs/dns.tact"] = "LnByZWZpeCArIGRlbHRhLCByZWNvcmQ6IHJlcy5yZWNvcmQgfTsKICAgIH0KCiAgICAvLy8gQ3JlYXRlcyBhIHN0cnVjdCBgRE5TUmVzb2x2ZVJlc3VsdGAgZnJvbSBz" + "dWJkb21haW4gYFNsaWNlYCBiaXRzLgogICAgdmlydHVhbCBmdW4gZG9SZXNvbHZlRE5TKHN1YmRvbWFpbjogU2xpY2UsIGNhdGVnb3J5OiBJbnQpOiBETlNSZXNvbHZl" + "UmVzdWx0IHsKICAgICAgICByZXR1cm4gRE5TUmVzb2x2ZVJlc3VsdCB7IHByZWZpeDogc3ViZG9tYWluLmJpdHMoKSwgcmVjb3JkOiBudWxsIH07CiAgICB9Cn0K"; +files["libs/jetton-interface.tact"] = + "Ly8vIFN0cnVjdC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS42LjYuCi8vLwovLy8gRGVzY3JpYmVzIHRoZSBkYXRhIHJldHVybmVkIGJ5IHRoZSBKZXR0b24gTWludGVy" + + "IGdldHRlciBmdW5jdGlvbiBgZ2V0X2pldHRvbl9kYXRhKClgLAovLy8gd2hpY2ggaXMgZGVmaW5lZCBpbiBURVAtNzQgYW5kIHNob3VsZCBiZSBwcmVzZW50IGluIGV2" + + "ZXJ5IEpldHRvbiBNaW50ZXIgaW1wbGVtZW50YXRpb24uCi8vLwovLy8gYGBgdGFjdAovLy8gaW1wb3J0ICJAc3RkbGliL2pldHRvbi1pbnRlcmZhY2UiOwovLy8KLy8v" + + "IGNvbnRyYWN0IEpldHRvbk1pbnRlcigKLy8vICAgICB0b3RhbFN1cHBseTogSW50IGFzIGNvaW5zLAovLy8gICAgIG93bmVyOiBBZGRyZXNzLAovLy8gICAgIGpldHRv" + + "bkNvbnRlbnQ6IENlbGwsCi8vLyAgICAgbWludGFibGU6IEJvb2wsCi8vLyApIHsKLy8vICAgICAvLyAuLi4KLy8vICAgICBnZXQgZnVuIGdldF9qZXR0b25fZGF0YSgp" + + "OiBKZXR0b25NaW50ZXJEYXRhIHsKLy8vICAgICAgICAgcmV0dXJuIEpldHRvbk1pbnRlckRhdGEgewovLy8gICAgICAgICAgICAgdG90YWxTdXBwbHk6IHNlbGYudG90" + + "YWxTdXBwbHksCi8vLyAgICAgICAgICAgICBtaW50YWJsZTogc2VsZi5taW50YWJsZSwKLy8vICAgICAgICAgICAgIGFkbWluQWRkcmVzczogc2VsZi5vd25lciwKLy8v" + + "ICAgICAgICAgICAgIGpldHRvbkNvbnRlbnQ6IHNlbGYuamV0dG9uQ29udGVudCwKLy8vICAgICAgICAgICAgIGpldHRvbldhbGxldENvZGU6IGNvZGVPZiBKZXR0b25X" + + "YWxsZXQsCi8vLyAgICAgICAgIH07Ci8vLyAgICAgfQovLy8gICAgIC8vIC4uLgovLy8gfQovLy8gYGBgCi8vLwovLy8gU2VlOgovLy8gKiBodHRwczovL2RvY3MudGFj" + + "dC1sYW5nLm9yZy9yZWYvc3RkbGliLWpldHRvbi1pbnRlcmZhY2UjamV0dG9ubWludGVyZGF0YQovLy8gKiBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJsb2NrY2hhaW4v" + + "VEVQcy9ibG9iL21hc3Rlci90ZXh0LzAwNzQtamV0dG9ucy1zdGFuZGFyZC5tZCNnZXQtbWV0aG9kcy0xCi8vLwpzdHJ1Y3QgSmV0dG9uTWludGVyRGF0YSB7CiAgICAv" + + "Ly8gVG90YWwgc3VwcGx5IG9mIHRoaXMgSmV0dG9uIE1pbnRlciDigJQgdGhlIGFtb3VudCBvZiB0b2tlbnMuCiAgICB0b3RhbFN1cHBseTogSW50IGFzIGNvaW5zOwoK" + + "ICAgIC8vLyBXaGV0aGVyIHRoZSB0b2tlbnMgYXJlIHN0aWxsIG1pbnRhYmxlLgogICAgbWludGFibGU6IEJvb2w7CgogICAgLy8vIE93bmVyIG9mIHRoZSBKZXR0b24g" + + "TWludGVyIOKAlCB0aGUgcmVndWxhciBUT04gd2FsbGV0IGFkZHJlc3MuCiAgICBvd25lcjogQWRkcmVzczsKCiAgICAvLy8gSmV0dG9uIGNvbnRlbnQgbWV0YWRhdGEs" + + "IHN1Y2ggYXMgaXRzIG5hbWUsIGRlc2NyaXB0aW9uLCBzeW1ib2wsIGV0Yy4KICAgIC8vLwogICAgLy8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL3Rvbi1ibG9ja2No" + + "YWluL1RFUHMvYmxvYi9tYXN0ZXIvdGV4dC8wMDY0LXRva2VuLWRhdGEtc3RhbmRhcmQubWQKICAgIC8vLwogICAgY29udGVudDogQ2VsbDsKCiAgICAvLy8gQ29kZSBv" + + "ZiB0aGUgY2hpbGQgSmV0dG9uIFdhbGxldCBjb250cmFjdCB0aGF0IEpldHRvbiBNaW50ZXIgd291bGQKICAgIC8vLyBkZXBsb3kgZm9yIGVhY2ggdXNlcidzIFRPTiB3" + + "YWxsZXQuCiAgICBqZXR0b25XYWxsZXRDb2RlOiBDZWxsOwp9CgovLy8gU3RydWN0LiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjYuNi4KLy8vCi8vLyBEZXNjcmliZXMg" + + "dGhlIGRhdGEgcmV0dXJuZWQgYnkgdGhlIEpldHRvbiBXYWxsZXQgZ2V0dGVyIGZ1bmN0aW9uIGBnZXRfd2FsbGV0X2RhdGEoKWAsCi8vLyB3aGljaCBpcyBkZWZpbmVk" + + "IGluIFRFUC03NCBhbmQgc2hvdWxkIGJlIHByZXNlbnQgaW4gZXZlcnkgSmV0dG9uIFdhbGxldCBpbXBsZW1lbnRhdGlvbi4KLy8vCi8vLyBgYGB0YWN0Ci8vLyBpbXBv" + + "cnQgIkBzdGRsaWIvamV0dG9uLWludGVyZmFjZSI7Ci8vLwovLy8gY29udHJhY3QgSmV0dG9uV2FsbGV0KAovLy8gICAgIG93bmVyOiBBZGRyZXNzLAovLy8gICAgIG1p" + + "bnRlcjogQWRkcmVzcywKLy8vICAgICBiYWxhbmNlOiBJbnQgYXMgY29pbnMsCi8vLyApIHsKLy8vICAgICAvLyAuLi4KLy8vICAgICBnZXQgZnVuIGdldF93YWxsZXRf" + + "ZGF0YSgpOiBKZXR0b25XYWxsZXREYXRhIHsKLy8vICAgICAgICAgcmV0dXJuIEpldHRvbldhbGxldERhdGEgewovLy8gICAgICAgICAgICAgb3duZXI6IHNlbGYub3du" + + "ZXIsCi8vLyAgICAgICAgICAgICBtaW50ZXI6IHNlbGYubWludGVyLAovLy8gICAgICAgICAgICAgYmFsYW5jZTogc2VsZi5iYWxhbmNlLAovLy8gICAgICAgICAgICAg" + + "Y29kZTogbXlDb2RlKCksCi8vLyAgICAgICAgIH07Ci8vLyAgICAgfQovLy8gICAgIC8vIC4uLgovLy8gfQovLy8gYGBgCi8vLwovLy8gU2VlOgovLy8gKiBodHRwczov" + + "L2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvc3RkbGliLWpldHRvbi1pbnRlcmZhY2UjamV0dG9ud2FsbGV0ZGF0YQovLy8gKiBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJs" + + "b2NrY2hhaW4vVEVQcy9ibG9iL21hc3Rlci90ZXh0LzAwNzQtamV0dG9ucy1zdGFuZGFyZC5tZCNnZXQtbWV0aG9kcwovLy8Kc3RydWN0IEpldHRvbldhbGxldERhdGEg" + + "ewogICAgLy8vIEJhbGFuY2Ugb2YgdGhlIEpldHRvbiBXYWxsZXQgaW4gdG9rZW5zLgogICAgYmFsYW5jZTogSW50IGFzIGNvaW5zOwoKICAgIC8vLyBPd25lciBvZiB0" + + "aGUgSmV0dG9uIFdhbGxldCDigJQgdGhlIHJlZ3VsYXIgVE9OIHdhbGxldCBhZGRyZXNzLgogICAgb3duZXI6IEFkZHJlc3M7CgogICAgLy8vIEFkZHJlc3Mgb2YgdGhl" + + "IHBhcmVudCBKZXR0b24gTWludGVyIGNvbnRyYWN0LgogICAgbWludGVyOiBBZGRyZXNzOwoKICAgIC8vLyBDb2RlIG9mIHRoaXMgSmV0dG9uIFdhbGxldCBjb250cmFj" + + "dC4KICAgIGNvZGU6IENlbGw7Cn0KCi8vLyBNZXNzYWdlIHN0cnVjdC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS42LjYuCi8vLwovLy8gUmVwcmVzZW50cyBhIG1lc3Nh" + + "Z2UgYm9keSBkZWZpbmVkIGluIFRFUC03NDogaXQgaXMgc2VudCB0byBvbmUgSmV0dG9uIFdhbGxldAovLy8gY29udHJhY3QgdG8gaW5pdGlhbGl6ZSBhIHRyYW5zZmVy" + + "IG9mIHRva2VucyB0byBhbm90aGVyIEpldHRvbiBXYWxsZXQuCi8vLwovLy8gTk9URTogQWx3YXlzIHZlcmlmeSB0aGUgc2VuZGVyIG9mIHRoZSBtZXNzYWdlIGJlZm9y" + + "ZSB0YWtpbmcgYW55IGZ1cnRoZXIgYWN0aW9ucy4KLy8vCi8vLyAjIyMjIFRMLUIgZnJvbSB0aGUgVEVQLTc0Ci8vLwovLy8gYGBgdGxiCi8vLyB0cmFuc2ZlciMwZjhh" + + "N2VhNQovLy8gICAgIHF1ZXJ5X2lkOnVpbnQ2NAovLy8gICAgIGFtb3VudDooVmFyVUludGVnZXIgMTYpCi8vLyAgICAgZGVzdGluYXRpb246TXNnQWRkcmVzcwovLy8g" + + "ICAgIHJlc3BvbnNlX2Rlc3RpbmF0aW9uOk1zZ0FkZHJlc3MKLy8vICAgICBjdXN0b21fcGF5bG9hZDooTWF5YmUgXkNlbGwpCi8vLyAgICAgZm9yd2FyZF90b25fYW1v" + + "dW50OihWYXJVSW50ZWdlciAxNikKLy8vICAgICBmb3J3YXJkX3BheWxvYWQ6KEVpdGhlciBDZWxsIF5DZWxsKQovLy8gICAgID0gSW50ZXJuYWxNc2dCb2R5OwovLy8g" + + "YGBgCi8vLwovLy8gIyMjIyBVc2FnZSBleGFtcGxlCi8vLwovLy8gYGBgdGFjdAovLy8gaW1wb3J0ICJAc3RkbGliL2pldHRvbi1pbnRlcmZhY2UiOwovLy8KLy8vIGNv" + + "bnRyYWN0IEpldHRvbldhbGxldCgKLy8vICAgICBvd25lcjogQWRkcmVzcywKLy8vICAgICBiYWxhbmNlOiBJbnQgYXMgY29pbnMsCi8vLyAgICAgLy8gLi4uCi8vLyAp" + + "IHsKLy8vICAgICByZWNlaXZlKG1zZzogSmV0dG9uVHJhbnNmZXIpIHsKLy8vICAgICAgICAgZm9yY2VCYXNlY2hhaW4obXNnLmRlc3RpbmF0aW9uKTsKLy8vICAgICAg" + + "ICAgcmVxdWlyZShzZW5kZXIoKSA9PSBzZWxmLm93bmVyLCAiSW5jb3JyZWN0IHNlbmRlciIpOwovLy8KLy8vICAgICAgICAgc2VsZi5iYWxhbmNlIC09IG1zZy5hbW91" + + "bnQ7Ci8vLyAgICAgICAgIHJlcXVpcmUoc2VsZi5iYWxhbmNlID49IDAsICJJbmNvcnJlY3QgYmFsYW5jZSBhZnRlciBzZW5kIik7Ci8vLwovLy8gICAgICAgICAvLyAu" + + "Li5mdXJ0aGVyIGNoZWNrcyBhbmQgYWN0aW9ucy4uLgovLy8gICAgIH0KLy8vCi8vLyAgICAgLy8gLi4uCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0" + + "dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9zdGRsaWItamV0dG9uLWludGVyZmFjZSNqZXR0b250cmFuc2ZlcgovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5n" + + "Lm9yZy9yZWYvc3RkbGliLWpldHRvbi1pbnRlcmZhY2UjamV0dG9udHJhbnNmZXJpbnRlcm5hbAovLy8gKiBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJsb2NrY2hhaW4v" + + "VEVQcy9ibG9iL21hc3Rlci90ZXh0LzAwNzQtamV0dG9ucy1zdGFuZGFyZC5tZCMxLXRyYW5zZmVyCi8vLwptZXNzYWdlKDB4ZjhhN2VhNSkgSmV0dG9uVHJhbnNmZXIg" + + "ewogICAgLy8vIFVuaXF1ZSBpZGVudGlmaWVyIHVzZWQgdG8gdHJhY2UgdHJhbnNhY3Rpb25zIGFjcm9zcyBtdWx0aXBsZSBjb250cmFjdHMuCiAgICAvLy8gU2V0dGlu" + + "ZyBpdCB0byAwIG1lYW5zIHdlIGRvbid0IG1hcmsgbWVzc2FnZXMgdG8gdHJhY2UgcmVxdWVzdHMuCiAgICBxdWVyeUlkOiBJbnQgYXMgdWludDY0OwoKICAgIC8vLyBB" + + "bW91bnQgb2YgdG9rZW5zIHRvIGJlIHRyYW5zZmVycmVkLgogICAgYW1vdW50OiBJbnQgYXMgY29pbnM7CgogICAgLy8vIEEgbmV3IG93bmVyIG9mIHRoZSB0cmFuc2Zl" + + "cnJlZCB0b2tlbnMg4oCUIHRoZSByZWd1bGFyIFRPTiB3YWxsZXQgYWRkcmVzcy4KICAgIGRlc3RpbmF0aW9uOiBBZGRyZXNzOwoKICAgIC8vLyBBZGRyZXNzIHdoZXJl" + + "IHRvIHNlbmQgYSByZXNwb25zZSBjb25maXJtaW5nIGEgc3VjY2Vzc2Z1bCB0cmFuc2ZlcgogICAgLy8vIGFuZCB0aGUgcmVtYWluaW5nIFRvbmNvaW4gZnJvbSB0aGUg" + + "aW5jb21pbmcgbWVzc2FnZS4KICAgIHJlc3BvbnNlRGVzdGluYXRpb246IEFkZHJlc3M/OwoKICAgIC8vLyBPcHRpb25hbCBjdXN0b20gZGF0YSByZWNlaXZlZCBmcm9t" + + "IGFub3RoZXIgSmV0dG9uIFdhbGxldC4KICAgIGN1c3RvbVBheWxvYWQ6IENlbGw/OwoKICAgIC8vLyBUaGUgYW1vdW50IG9mIG5hbm90b25zIHRvIGJlIHNlbnQgdG8g" + + "dGhlIGBkZXN0aW5hdGlvbmAgYWRkcmVzcy4KICAgIGZvcndhcmRUb25BbW91bnQ6IEludCBhcyBjb2luczsKCiAgICAvLy8gT3B0aW9uYWwgY3VzdG9tIGRhdGEgdG8g" + + "YmUgc2VudCB0byB0aGUgYGRlc3RpbmF0aW9uYCBhZGRyZXNzLgogICAgZm9yd2FyZFBheWxvYWQ6IFNsaWNlIGFzIHJlbWFpbmluZzsgLy8gKEVpdGhlciBDZWxsIF5D" + + "ZWxsKQp9CgovLy8gTWVzc2FnZSBzdHJ1Y3QuIEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNi42LgovLy8KLy8vIFJlcHJlc2VudHMgYSBtZXNzYWdlIGJvZHkgZGVmaW5l" + + "ZCBpbiBURVAtNzQ6IGl0IGlzIHNlbnQgYW5kIHJlY2VpdmVkIGJ5IHRoZQovLy8gSmV0dG9uIFdhbGxldCBjb250cmFjdCB0byBtYWtlIGEgdHJhbnNmZXIgb3IgcHJv" + + "Y2VzcyBhbiBpbmNvbWluZyBvbmUsIHJlc3BlY3RpdmVseS4KLy8vCi8vLyBOT1RFOiBBbHdheXMgdmVyaWZ5IHRoZSBzZW5kZXIgb2YgdGhlIG1lc3NhZ2UgYmVmb3Jl" + + "IHRha2luZyBhbnkgZnVydGhlciBhY3Rpb25zLgovLy8KLy8vICMjIyMgVEwtQiBmcm9tIHRoZSBURVAtNzQKLy8vCi8vLyBgYGB0bGIKLy8vIGludGVybmFsX3RyYW5z" + + "ZmVyIzE3OGQ0NTE5Ci8vLyAgICAgcXVlcnlfaWQ6dWludDY0Ci8vLyAgICBhIG1vdW50OihWYXJVSW50ZWdlciAxNikKLy8vICAgICBmcm9tOk1zZ0FkZHJlc3MKLy8v" + + "ICAgICByZXNwb25zZV9hZGRyZXNzOk1zZ0FkZHJlc3MKLy8vICAgICBmb3J3YXJkX3Rvbl9hbW91bnQ6KFZhclVJbnRlZ2VyIDE2KQovLy8gICAgIGZvcndhcmRfcGF5" + + "bG9hZDooRWl0aGVyIENlbGwgXkNlbGwpCi8vLyAgICAgPSBJbnRlcm5hbE1zZ0JvZHk7Ci8vLyBgYGAKLy8vCi8vLyAjIyMjIFVzYWdlIGV4YW1wbGVzCi8vLwovLy8g" + + "YGBgdGFjdAovLy8gaW1wb3J0ICJAc3RkbGliL2pldHRvbi1pbnRlcmZhY2UiOwovLy8KLy8vIGNvbnRyYWN0IEpldHRvbldhbGxldCgKLy8vICAgICBvd25lcjogQWRk" + + "cmVzcywKLy8vICAgICBtaW50ZXI6IEFkZHJlc3MsCi8vLyAgICAgYmFsYW5jZTogSW50IGFzIGNvaW5zLAovLy8gKSB7Ci8vLyAgICAgcmVjZWl2ZShtc2c6IEpldHRv" + + "blRyYW5zZmVyKSB7Ci8vLyAgICAgICAgIGZvcmNlQmFzZWNoYWluKG1zZy5kZXN0aW5hdGlvbik7Ci8vLyAgICAgICAgIHJlcXVpcmUoc2VuZGVyKCkgPT0gc2VsZi5v" + + "d25lciwgIkluY29ycmVjdCBzZW5kZXIiKTsKLy8vCi8vLyAgICAgICAgIC8vIC4uLm90aGVyIGludGVybWVkaWF0ZSBjaGVja3MuLi4KLy8vCi8vLyAgICAgICAgIGRl" + + "cGxveShEZXBsb3lQYXJhbWV0ZXJzIHsKLy8vICAgICAgICAgICAgIHZhbHVlOiAwLAovLy8gICAgICAgICAgICAgbW9kZTogU2VuZFJlbWFpbmluZ1ZhbHVlLAovLy8g" + + "ICAgICAgICAgICAgYm91bmNlOiB0cnVlLAovLy8gICAgICAgICAgICAgYm9keTogSmV0dG9uVHJhbnNmZXJJbnRlcm5hbCB7Ci8vLyAgICAgICAgICAgICAgICAgcXVl" + + "cnlJZDogbXNnLnF1ZXJ5SWQsCi8vLyAgICAgICAgICAgICAgICAgYW1vdW50OiBtc2cuYW1vdW50LAovLy8gICAgICAgICAgICAgICAgIHNlbmRlcjogc2VsZi5vd25l" + + "ciwKLy8vICAgICAgICAgICAgICAgICByZXNwb25zZURlc3RpbmF0aW9uOiBtc2cucmVzcG9uc2VEZXN0aW5hdGlvbiwKLy8vICAgICAgICAgICAgICAgICBmb3J3YXJk" + + "VG9uQW1vdW50OiBtc2cuZm9yd2FyZFRvbkFtb3VudCwKLy8vICAgICAgICAgICAgICAgICBmb3J3YXJkUGF5bG9hZDogbXNnLmZvcndhcmRQYXlsb2FkLAovLy8gICAg" + + "ICAgICAgICAgfS50b0NlbGwoKSwKLy8vICAgICAgICAgICAgIGluaXQ6IGluaXRPZiBKZXR0b25XYWxsZXQoMCwgbXNnLmRlc3RpbmF0aW9uLCBzZWxmLm1pbnRlciks" + + "Ci8vLyAgICAgICAgIH0pOwovLy8gICAgIH0KLy8vCi8vLyAgICAgcmVjZWl2ZShtc2c6IEpldHRvblRyYW5zZmVySW50ZXJuYWwpIHsKLy8vICAgICAgICAgc2VsZi5i" + + "YWxhbmNlICs9IG1zZy5hbW91bnQ7Ci8vLwovLy8gICAgICAgICAvLyBUaGlzIG1lc3NhZ2Ugc2hvdWxkIGNvbWUgb25seSBmcm9tIG1hc3Rlciwgb3IgZnJvbSBvdGhl" + + "ciBKZXR0b25XYWxsZXQKLy8vICAgICAgICAgbGV0IHdhbGxldDogU3RhdGVJbml0ID0gaW5pdE9mIEpldHRvbldhbGxldCgwLCBtc2cuc2VuZGVyLCBzZWxmLm1pbnRl" + + "cik7Ci8vLyAgICAgICAgIGlmICghd2FsbGV0Lmhhc1NhbWVCYXNlY2hhaW5BZGRyZXNzKHNlbmRlcigpKSkgewovLy8gICAgICAgICAgICAgcmVxdWlyZShzZWxmLm1p" + + "bnRlciA9PSBzZW5kZXIoKSwgIkluY29ycmVjdCBzZW5kZXIiKTsKLy8vICAgICAgICAgfQovLy8KLy8vICAgICAgICAgLy8gLi4uZnVydGhlciBhY3Rpb25zLi4uCi8v" + + "LyAgICAgfQovLy8KLy8vICAgICBib3VuY2VkKG1zZzogYm91bmNlZDxKZXR0b25UcmFuc2ZlckludGVybmFsPikgewovLy8gICAgICAgICBzZWxmLmJhbGFuY2UgKz0g" + + "bXNnLmFtb3VudDsKLy8vICAgICB9Ci8vLwovLy8gICAgIC8vIC4uLgovLy8gfQovLy8gYGBgCi8vLwovLy8gU2VlOgovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5n" + + "Lm9yZy9yZWYvc3RkbGliLWpldHRvbi1pbnRlcmZhY2UjamV0dG9udHJhbnNmZXJpbnRlcm5hbAovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvc3Rk" + + "bGliLWpldHRvbi1pbnRlcmZhY2UjamV0dG9udHJhbnNmZXIKLy8vICogaHR0cHM6Ly9naXRodWIuY29tL3Rvbi1ibG9ja2NoYWluL1RFUHMvYmxvYi9tYXN0ZXIvdGV4" + + "dC8wMDc0LWpldHRvbnMtc3RhbmRhcmQubWQjdGwtYi1zY2hlbWEKLy8vCm1lc3NhZ2UoMHgxNzhkNDUxOSkgSmV0dG9uVHJhbnNmZXJJbnRlcm5hbCB7CiAgICAvLy8g" + + "VW5pcXVlIGlkZW50aWZpZXIgdXNlZCB0byB0cmFjZSB0cmFuc2FjdGlvbnMgYWNyb3NzIG11bHRpcGxlIGNvbnRyYWN0cy4KICAgIC8vLyBTZXR0aW5nIGl0IHRvIDAg" + + "bWVhbnMgd2UgZG9uJ3QgbWFyayBtZXNzYWdlcyB0byB0cmFjZSByZXF1ZXN0cy4KICAgIHF1ZXJ5SWQ6IEludCBhcyB1aW50NjQ7CgogICAgLy8vIEFtb3VudCBvZiB0" + + "cmFuc2ZlcnJlZCB0b2tlbnMuCiAgICBhbW91bnQ6IEludCBhcyBjb2luczsKCiAgICAvLy8gUHJldmlvdXMgb3duZXIgb2YgdGhlIHRyYW5zZmVycmVkIHRva2VucyDi" + + "gJQgdGhlIHJlZ3VsYXIgVE9OIHdhbGxldCBhZGRyZXNzLgogICAgc2VuZGVyOiBBZGRyZXNzOwoKICAgIC8vLyBBZGRyZXNzIHdoZXJlIHRvIHNlbmQgYSByZXNwb25z" + + "ZSBjb25maXJtaW5nIGEgc3VjY2Vzc2Z1bCB0cmFuc2ZlcgogICAgLy8vIGFuZCB0aGUgcmVtYWluaW5nIFRvbmNvaW4gZnJvbSB0aGUgaW5jb21pbmcgbWVzc2FnZS4K" + + "ICAgIHJlc3BvbnNlRGVzdGluYXRpb246IEFkZHJlc3M/OwoKICAgIC8vLyBUaGUgYW1vdW50IG9mIG5hbm90b25zIHRvIGJlIHNlbnQgdG8gdGhlIG5ldyBvd25lcidz" + + "IGFkZHJlc3MuCiAgICBmb3J3YXJkVG9uQW1vdW50OiBJbnQgYXMgY29pbnM7CgogICAgLy8vIE9wdGlvbmFsIGN1c3RvbSBkYXRhIHRvIGJlIHNlbnQgdG8gdGhlIG5l" + + "dyBvd25lcidzIGFkZHJlc3MuCiAgICBmb3J3YXJkUGF5bG9hZDogU2xpY2UgYXMgcmVtYWluaW5nOyAvLyAoRWl0aGVyIENlbGwgXkNlbGwpCn0KCi8vLyBNZXNzYWdl" + + "IHN0cnVjdC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS42LjYuCi8vLwovLy8gUmVwcmVzZW50cyBhIG1lc3NhZ2UgYm9keSBkZWZpbmVkIGluIFRFUC03NDogaXQgaXMg" + + "c2VudCBmcm9tIHRoZSBKZXR0b24gV2FsbGV0Ci8vLyBjb250cmFjdCB0byB0aGUgYHNlbGYub3duZXJgIGFkZHJlc3MgdG8gbm90aWZ5IGFib3V0IHRoZSBpbmNvbWlu" + + "ZyB0cmFuc2ZlcgovLy8gb2YgdG9rZW5zIGZyb20gdGhlaXIgcHJldmlvdXMgb3duZXIg4oCUIHRoZSBgc2VuZGVyYC4KLy8vCi8vLyAjIyMjIFRMLUIgZnJvbSB0aGUg" + + "VEVQLTc0Ci8vLwovLy8gYGBgdGxiCi8vLyB0cmFuc2Zlcl9ub3RpZmljYXRpb24jNzM2MmQwOWMKLy8vICAgICBxdWVyeV9pZDp1aW50NjQKLy8vICAgICBhbW91bnQ6" + + "KFZhclVJbnRlZ2VyIDE2KQovLy8gICAgIHNlbmRlcjpNc2dBZGRyZXNzCi8vLyAgICAgZm9yd2FyZF9wYXlsb2FkOihFaXRoZXIgQ2VsbCBeQ2VsbCkKLy8vICAgICA9" + + "IEludGVybmFsTXNnQm9keTsKLy8vIGBgYAovLy8KLy8vICMjIyMgVXNhZ2UgZXhhbXBsZQovLy8KLy8vIGBgYHRhY3QKLy8vIGltcG9ydCAiQHN0ZGxpYi9qZXR0b24t" + + "aW50ZXJmYWNlIjsKLy8vCi8vLyBjb250cmFjdCBKZXR0b25XYWxsZXQoCi8vLyAgICAgb3duZXI6IEFkZHJlc3MsCi8vLyAgICAgLy8gLi4uCi8vLyApIHsKLy8vICAg" + + "ICAvLyAuLi4KLy8vICAgICByZWNlaXZlKG1zZzogSmV0dG9uVHJhbnNmZXJJbnRlcm5hbCkgewovLy8gICAgICAgICAvLyAuLi5wcmlvciBjaGVja3MuLi4KLy8vCi8v" + + "LyAgICAgICAgIGxldCBtc2dWYWx1ZTogSW50ID0gY3R4LnZhbHVlOwovLy8gICAgICAgICBpZiAobXNnLmZvcndhcmRUb25BbW91bnQgPiAwKSB7Ci8vLyAgICAgICAg" + + "ICAgICBsZXQgZndkRmVlOiBJbnQgPSBjdHgucmVhZEZvcndhcmRGZWUoKTsKLy8vICAgICAgICAgICAgIG1zZ1ZhbHVlIC09IG1zZy5mb3J3YXJkVG9uQW1vdW50ICsg" + + "ZndkRmVlOwovLy8gICAgICAgICAgICAgbWVzc2FnZShNZXNzYWdlUGFyYW1ldGVycyB7Ci8vLyAgICAgICAgICAgICAgICAgdG86IHNlbGYub3duZXIsCi8vLyAgICAg" + + "ICAgICAgICAgICAgdmFsdWU6IG1zZy5mb3J3YXJkVG9uQW1vdW50LAovLy8gICAgICAgICAgICAgICAgIG1vZGU6IFNlbmRQYXlHYXNTZXBhcmF0ZWx5LAovLy8gICAg" + + "ICAgICAgICAgICAgIGJvdW5jZTogZmFsc2UsCi8vLyAgICAgICAgICAgICAgICAgYm9keTogSmV0dG9uVHJhbnNmZXJOb3RpZmljYXRpb24gewovLy8gICAgICAgICAg" + + "ICAgICAgICAgICBxdWVyeUlkOiBtc2cucXVlcnlJZCwKLy8vICAgICAgICAgICAgICAgICAgICAgYW1vdW50OiBtc2cuYW1vdW50LAovLy8gICAgICAgICAgICAgICAg" + + "ICAgICBzZW5kZXI6IG1zZy5zZW5kZXIsCi8vLyAgICAgICAgICAgICAgICAgICAgIGZvcndhcmRQYXlsb2FkOiBtc2cuZm9yd2FyZFBheWxvYWQsCi8vLyAgICAgICAg" + + "ICAgICAgICAgfS50b0NlbGwoKSwKLy8vICAgICAgICAgICAgIH0pOwovLy8gICAgICAgICB9Ci8vLwovLy8gICAgICAgICAvLyAuLi5mdXJ0aGVyIGFjdGlvbnMuLi4K" + + "Ly8vICAgICB9Ci8vLyAgICAgLy8gLi4uCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9zdGRsaWIt" + + "amV0dG9uLWludGVyZmFjZSNqZXR0b250cmFuc2Zlcm5vdGlmaWNhdGlvbgovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvc3RkbGliLWpldHRvbi1p" + + "bnRlcmZhY2UjamV0dG9udHJhbnNmZXJpbnRlcm5hbAovLy8gKiBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJsb2NrY2hhaW4vVEVQcy9ibG9iL21hc3Rlci90ZXh0LzAw" + + "NzQtamV0dG9ucy1zdGFuZGFyZC5tZCMxLXRyYW5zZmVyCi8vLwptZXNzYWdlKDB4NzM2MmQwOWMpIEpldHRvblRyYW5zZmVyTm90aWZpY2F0aW9uIHsKICAgIC8vLyBV" + + "bmlxdWUgaWRlbnRpZmllciB1c2VkIHRvIHRyYWNlIHRyYW5zYWN0aW9ucyBhY3Jvc3MgbXVsdGlwbGUgY29udHJhY3RzLgogICAgLy8vIFNldHRpbmcgaXQgdG8gMCBt" + + "ZWFucyB3ZSBkb24ndCBtYXJrIG1lc3NhZ2VzIHRvIHRyYWNlIHJlcXVlc3RzLgogICAgcXVlcnlJZDogSW50IGFzIHVpbnQ2NDsKCiAgICAvLy8gQW1vdW50IG9mIHRy" + + "YW5zZmVycmVkIHRva2Vucy4KICAgIGFtb3VudDogSW50IGFzIGNvaW5zOwoKICAgIC8vLyBQcmV2aW91cyBvd25lciBvZiB0aGUgdHJhbnNmZXJyZWQgdG9rZW5zIOKA" + + "lCB0aGUgcmVndWxhciBUT04gd2FsbGV0IGFkZHJlc3MuCiAgICBzZW5kZXI6IEFkZHJlc3M7CgogICAgLy8vIE9wdGlvbmFsIGN1c3RvbSBkYXRhIHRvIGJlIHNlbnQg" + + "dG8gdGhlIG5ldyBvd25lcidzIGFkZHJlc3MuCiAgICBmb3J3YXJkUGF5bG9hZDogU2xpY2UgYXMgcmVtYWluaW5nOyAvLyAoRWl0aGVyIENlbGwgXkNlbGwpCn0KCi8v" + + "LyBNZXNzYWdlIHN0cnVjdC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS42LjYuCi8vLwovLy8gUmVwcmVzZW50cyBhIG1lc3NhZ2UgYm9keSBkZWZpbmVkIGluIFRFUC03" + + "NDogaXQgaXMgc2VudCB0byB0aGUgSmV0dG9uIFdhbGxldAovLy8gY29udHJhY3QgZnJvbSBpdHMgVE9OIHdhbGxldCBvd25lciB0byBkZWNyZWFzZSBpdHMgYmFsYW5j" + + "ZSBieSBhIGNlcnRhaW4gYGFtb3VudGAuCi8vLwovLy8gTk9URTogQWx3YXlzIHZlcmlmeSB0aGUgc2VuZGVyIG9mIHRoZSBtZXNzYWdlIGJlZm9yZSB0YWtpbmcgYW55" + + "IGZ1cnRoZXIgYWN0aW9ucy4KLy8vCi8vLyAjIyMjIFRMLUIgZnJvbSB0aGUgVEVQLTc0Ci8vLwovLy8gYGBgdGxiCi8vLyBidXJuIzU5NWYwN2JjCi8vLyAgICAgcXVl" + + "cnlfaWQ6dWludDY0Ci8vLyAgICAgYW1vdW50OihWYXJVSW50ZWdlciAxNikKLy8vICAgICByZXNwb25zZV9kZXN0aW5hdGlvbjpNc2dBZGRyZXNzCi8vLyAgICAgY3Vz" + + "dG9tX3BheWxvYWQ6KE1heWJlIF5DZWxsKQovLy8gICAgID0gSW50ZXJuYWxNc2dCb2R5OwovLy8gYGBgCi8vLwovLy8gIyMjIyBVc2FnZSBleGFtcGxlCi8vLwovLy8g" + + "YGBgdGFjdAovLy8gaW1wb3J0ICJAc3RkbGliL2pldHRvbi1pbnRlcmZhY2UiOwovLy8KLy8vIGNvbnRyYWN0IEpldHRvbldhbGxldCgKLy8vICAgICBvd25lcjogQWRk" + + "cmVzcywKLy8vICAgICBiYWxhbmNlOiBJbnQgYXMgY29pbnMsCi8vLyAgICAgLy8gLi4uCi8vLyApIHsKLy8vICAgICAvLyAuLi4KLy8vICAgICByZWNlaXZlKG1zZzog" + + "SmV0dG9uQnVybikgewovLy8gICAgICAgICByZXF1aXJlKHNlbmRlcigpID09IHNlbGYub3duZXIsICJJbmNvcnJlY3Qgc2VuZGVyIik7Ci8vLyAgICAgICAgIHNlbGYu" + + "YmFsYW5jZSAtPSBtc2cuYW1vdW50OwovLy8gICAgICAgICByZXF1aXJlKHNlbGYuYmFsYW5jZSA+PSAwLCAiSW5jb3JyZWN0IGJhbGFuY2UgYWZ0ZXIgc2VuZCIpOwov" + + "Ly8gICAgICAgICAvLyAuLi5mdXJ0aGVyIGFjdGlvbnMuLi4KLy8vICAgICB9Ci8vLyAgICAgLy8gLi4uCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0" + + "dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9zdGRsaWItamV0dG9uLWludGVyZmFjZSNqZXR0b25idXJuCi8vLyAqIGh0dHBzOi8vZ2l0aHViLmNvbS90b24tYmxv" + + "Y2tjaGFpbi9URVBzL2Jsb2IvbWFzdGVyL3RleHQvMDA3NC1qZXR0b25zLXN0YW5kYXJkLm1kIzItYnVybgovLy8KbWVzc2FnZSgweDU5NWYwN2JjKSBKZXR0b25CdXJu" + + "IHsKICAgIC8vLyBVbmlxdWUgaWRlbnRpZmllciB1c2VkIHRvIHRyYWNlIHRyYW5zYWN0aW9ucyBhY3Jvc3MgbXVsdGlwbGUgY29udHJhY3RzLgogICAgLy8vIFNldHRp" + + "bmcgaXQgdG8gMCBtZWFucyB3ZSBkb24ndCBtYXJrIG1lc3NhZ2VzIHRvIHRyYWNlIHJlcXVlc3RzLgogICAgcXVlcnlJZDogSW50IGFzIHVpbnQ2NDsKCiAgICAvLy8g" + + "QW1vdW50IG9mIGJ1cm5lZCB0b2tlbnMuCiAgICBhbW91bnQ6IEludCBhcyBjb2luczsKCiAgICAvLy8gQWRkcmVzcyB3aGVyZSB0byBzZW5kIGEgcmVzcG9uc2UgY29u" + + "ZmlybWluZyBhIHN1Y2Nlc3NmdWwgYnVybgogICAgLy8vIGFuZCB0aGUgcmVtYWluaW5nIFRvbmNvaW4gZnJvbSB0aGUgaW5jb21pbmcgbWVzc2FnZS4KICAgIHJlc3Bv" + + "bnNlRGVzdGluYXRpb246IEFkZHJlc3M/OwoKICAgIC8vLyBPcHRpb25hbCBjdXN0b20gZGF0YSByZWNlaXZlZCBmcm9tIGFub3RoZXIgSmV0dG9uIFdhbGxldC4KICAg" + + "IGN1c3RvbVBheWxvYWQ6IENlbGw/Owp9CgovLy8gTWVzc2FnZSBzdHJ1Y3QuIEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNi42LgovLy8KLy8vIFJlcHJlc2VudHMgYSBt" + + "ZXNzYWdlIGJvZHkgZGVmaW5lZCBpbiBURVAtNzQ6IGl0IGlzIHNlbnQgZnJvbSB0aGUgSmV0dG9uIFdhbGxldAovLy8gY29udHJhY3QgdG8gdGhlIEpldHRvbiBNaW50" + + "ZXIgdG8gbm90aWZ5IGFib3V0IHRoZSBzdWNjZXNzZnVsIGJ1cm4KLy8vIGFuZCByZWR1Y2UgdGhlIHRvdGFsIHN1cHBseSBvZiB0b2tlbnMuCi8vLwovLy8gIyMjIyBU" + + "TC1CIGZyb20gdGhlIFRFUC03NAovLy8KLy8vIGBgYHRsYgovLy8gYnVybl9ub3RpZmljYXRpb24jN2JkZDk3ZGUKLy8vICAgICBxdWVyeV9pZDp1aW50NjQKLy8vICAg" + + "ICBhbW91bnQ6KFZhclVJbnRlZ2VyIDE2KQovLy8gICAgIHNlbmRlcjpNc2dBZGRyZXNzCi8vLyAgICAgcmVzcG9uc2VfZGVzdGluYXRpb246TXNnQWRkcmVzcwovLy8g" + + "ICAgID0gSW50ZXJuYWxNc2dCb2R5OwovLy8gYGBgCi8vLwovLy8gIyMjIyBVc2FnZSBleGFtcGxlcwovLy8KLy8vIGBgYHRhY3QKLy8vIGltcG9ydCAiQHN0ZGxpYi9q" + + "ZXR0b24taW50ZXJmYWNlIjsKLy8vCi8vLyBjb250cmFjdCBKZXR0b25NaW50ZXIoCi8vLyAgICAgLy8gLi4uCi8vLyApIHsKLy8vICAgICAvLyAuLi4KLy8vICAgICBy" + + "ZWNlaXZlKG1zZzogSmV0dG9uQnVybk5vdGlmaWNhdGlvbikgewovLy8gICAgICAgICBsZXQgc2VuZGVyID0gcGFyc2VTdGRBZGRyZXNzKHNlbmRlcigpLmFzU2xpY2Uo" + + "KSk7Ci8vLyAgICAgICAgIGxldCB3YWxsZXQgPSBnZXRKZXR0b25CYXNlY2hhaW5XYWxsZXRCeU93bmVyKG1zZy5zZW5kZXIpOwovLy8KLy8vICAgICAgICAgcmVxdWly" + + "ZSgKLy8vICAgICAgICAgICAgIHNlbmRlci53b3JrY2hhaW4gPT0gMCAmJiBzZW5kZXIuYWRkcmVzcyA9PSB3YWxsZXQuaGFzaCEhLAovLy8gICAgICAgICAgICAgIlVu" + + "YXV0aG9yaXplZCBidXJuIiwKLy8vICAgICAgICAgKTsKLy8vICAgICAgICAgLy8gLi4uZnVydGhlciBhY3Rpb25zLi4uCi8vLyAgICAgfQovLy8gICAgIC8vIC4uLgov" + + "Ly8gfQovLy8KLy8vIGNvbnRyYWN0IEpldHRvbldhbGxldCgKLy8vICAgICBiYWxhbmNlOiBJbnQgYXMgY29pbnMsCi8vLyAgICAgLy8gLi4uCi8vLyApIHsKLy8vICAg" + + "ICAvLyAuLi4KLy8vICAgICBib3VuY2VkKG1zZzogYm91bmNlZDxKZXR0b25CdXJuTm90aWZpY2F0aW9uPikgewovLy8gICAgICAgICBzZWxmLmJhbGFuY2UgKz0gbXNn" + + "LmFtb3VudDsKLy8vICAgICB9Ci8vLyAgICAgLy8gLi4uCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3Jl" + + "Zi9zdGRsaWItamV0dG9uLWludGVyZmFjZSNqZXR0b25idXJubm90aWZpY2F0aW9uCi8vLyAqIGh0dHBzOi8vZ2l0aHViLmNvbS90b24tYmxvY2tjaGFpbi9URVBzL2Js" + + "b2IvbWFzdGVyL3RleHQvMDA3NC1qZXR0b25zLXN0YW5kYXJkLm1kI3RsLWItc2NoZW1hCi8vLwptZXNzYWdlKDB4N2JkZDk3ZGUpIEpldHRvbkJ1cm5Ob3RpZmljYXRp" + + "b24gewogICAgLy8vIFVuaXF1ZSBpZGVudGlmaWVyIHVzZWQgdG8gdHJhY2UgdHJhbnNhY3Rpb25zIGFjcm9zcyBtdWx0aXBsZSBjb250cmFjdHMuCiAgICAvLy8gU2V0" + + "dGluZyBpdCB0byAwIG1lYW5zIHdlIGRvbid0IG1hcmsgbWVzc2FnZXMgdG8gdHJhY2UgcmVxdWVzdHMuCiAgICBxdWVyeUlkOiBJbnQgYXMgdWludDY0OwoKICAgIC8v" + + "LyBBbW91bnQgb2YgYnVybmVkIHRva2Vucy4KICAgIGFtb3VudDogSW50IGFzIGNvaW5zOwoKICAgIC8vLyBQcmV2aW91cyBvd25lciBvZiB0aGUgYnVybmVkIHRva2Vu" + + "cyDigJQgdGhlIHJlZ3VsYXIgVE9OIHdhbGxldCBhZGRyZXNzLgogICAgc2VuZGVyOiBBZGRyZXNzOwoKICAgIC8vLyBBZGRyZXNzIHdoZXJlIHRvIHNlbmQgYSByZXNw" + + "b25zZSBjb25maXJtaW5nIGEgc3VjY2Vzc2Z1bCBidXJuCiAgICAvLy8gYW5kIHRoZSByZW1haW5pbmcgVG9uY29pbiBmcm9tIHRoZSBpbmNvbWluZyBtZXNzYWdlLgog" + + "ICAgcmVzcG9uc2VEZXN0aW5hdGlvbjogQWRkcmVzcz87Cn0KCi8vLyBNZXNzYWdlIHN0cnVjdC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS42LjYuCi8vLwovLy8gUmVw" + + "cmVzZW50cyBhIG1lc3NhZ2UgYm9keSBkZWZpbmVkIGluIFRFUC03NDogaXQgaXMgc2VudCBmcm9tIGVpdGhlciBKZXR0b24gTWludGVyCi8vLyBvciBKZXR0b24gV2Fs" + + "bGV0IHRvIHJlZnVuZCB0aGUgcmVtYWluaW5nIFRvbmNvaW4gZnJvbSB0aGUgaW5jb21pbmcgbWVzc2FnZS4KLy8vCi8vLyAjIyMjIFRMLUIgZnJvbSB0aGUgVEVQLTc0" + + "Ci8vLwovLy8gYGBgdGxiCi8vLyBleGNlc3NlcyNkNTMyNzZkYgovLy8gICAgIHF1ZXJ5X2lkOnVpbnQ2NAovLy8gICAgID0gSW50ZXJuYWxNc2dCb2R5OwovLy8gYGBg" + + "Ci8vLwovLy8gIyMjIyBVc2FnZSBleGFtcGxlCi8vLwovLy8gYGBgdGFjdAovLy8gaW1wb3J0ICJAc3RkbGliL2pldHRvbi1pbnRlcmZhY2UiOwovLy8KLy8vIGNvbnRy" + + "YWN0IEpldHRvbk1pbnRlcigKLy8vICAgICAvLyAuLi4KLy8vICkgewovLy8gICAgIC8vIC4uLgovLy8gICAgIHJlY2VpdmUobXNnOiBKZXR0b25CdXJuTm90aWZpY2F0" + + "aW9uKSB7Ci8vLyAgICAgICAgIC8vIC4uLgovLy8gICAgICAgICBpZiAobXNnLnJlc3BvbnNlRGVzdGluYXRpb24gIT0gbnVsbCkgewovLy8gICAgICAgICAgICAgbWVz" + + "c2FnZShNZXNzYWdlUGFyYW1ldGVycyB7Ci8vLyAgICAgICAgICAgICAgICAgdG86IG1zZy5yZXNwb25zZURlc3RpbmF0aW9uISEsCi8vLyAgICAgICAgICAgICAgICAg" + + "Ym9keTogSmV0dG9uRXhjZXNzZXMgeyBxdWVyeUlkOiBtc2cucXVlcnlJZCB9LnRvQ2VsbCgpLAovLy8gICAgICAgICAgICAgICAgIHZhbHVlOiAwLAovLy8gICAgICAg" + + "ICAgICAgICAgIGJvdW5jZTogZmFsc2UsIC8vIGRvIG5vdCBib3VuY2UKLy8vICAgICAgICAgICAgICAgICBtb2RlOiBTZW5kUmVtYWluaW5nVmFsdWUgfCBTZW5kSWdu" + + "b3JlRXJyb3JzLAovLy8gICAgICAgICAgICAgfSk7Ci8vLyAgICAgICAgIH0KLy8vICAgICB9Ci8vLyAgICAgLy8gLi4uCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6" + + "Ci8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9zdGRsaWItamV0dG9uLWludGVyZmFjZSNqZXR0b25leGNlc3NlcwovLy8gKiBodHRwczovL2dpdGh1" + + "Yi5jb20vdG9uLWJsb2NrY2hhaW4vVEVQcy9ibG9iL21hc3Rlci90ZXh0LzAwNzQtamV0dG9ucy1zdGFuZGFyZC5tZCMxLXRyYW5zZmVyCi8vLwptZXNzYWdlKDB4ZDUz" + + "Mjc2ZGIpIEpldHRvbkV4Y2Vzc2VzIHsKICAgIC8vLyBVbmlxdWUgaWRlbnRpZmllciB1c2VkIHRvIHRyYWNlIHRyYW5zYWN0aW9ucyBhY3Jvc3MgbXVsdGlwbGUgY29u" + + "dHJhY3RzLgogICAgLy8vIFNldHRpbmcgaXQgdG8gMCBtZWFucyB3ZSBkb24ndCBtYXJrIG1lc3NhZ2VzIHRvIHRyYWNlIHJlcXVlc3RzLgogICAgcXVlcnlJZDogSW50" + + "IGFzIHVpbnQ2NDsKfQoKLy8vIE1lc3NhZ2Ugc3RydWN0LiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjYuNi4KLy8vCi8vLyBSZXByZXNlbnRzIGEgbWVzc2FnZSBib2R5" + + "IGRlZmluZWQgaW4gVEVQLTg5OiBpdCBpcyBzZW50IHRvIHRoZSBKZXR0b24gTWludGVyCi8vLyBjb250cmFjdCB0byByZXF1ZXN0IHRoZSBKZXR0b24gV2FsbGV0IGFk" + + "ZHJlc3MgZm9yIHRoZSBgb3duZXJBZGRyZXNzYC4KLy8vCi8vLyAjIyMjIFRMLUIgZnJvbSB0aGUgVEVQLTg5Ci8vLwovLy8gYGBgdGxiCi8vLyBwcm92aWRlX3dhbGxl" + + "dF9hZGRyZXNzIzJjNzZiOTczCi8vLyAgICAgcXVlcnlfaWQ6dWludDY0Ci8vLyAgICAgb3duZXJfYWRkcmVzczpNc2dBZGRyZXNzCi8vLyAgICAgaW5jbHVkZV9hZGRy" + + "ZXNzOkJvb2wKLy8vICAgICA9IEludGVybmFsTXNnQm9keTsKLy8vIGBgYAovLy8KLy8vICMjIyMgVXNhZ2UgZXhhbXBsZQovLy8KLy8vIGBgYHRhY3QKLy8vIGltcG9y" + + "dCAiQHN0ZGxpYi9qZXR0b24taW50ZXJmYWNlIjsKLy8vCi8vLyBjb250cmFjdCBKZXR0b25NaW50ZXIoCi8vLyAgICAgLy8gLi4uCi8vLyApIHsKLy8vICAgICAvLyAu" + + "Li4KLy8vICAgICByZWNlaXZlKG1zZzogUHJvdmlkZVdhbGxldEFkZHJlc3MpIHsKLy8vICAgICAgICAgbGV0IG93bmVyV29ya2NoYWluID0gcGFyc2VTdGRBZGRyZXNz" + + "KG1zZy5vd25lckFkZHJlc3MuYXNTbGljZSgpKS53b3JrY2hhaW47Ci8vLyAgICAgICAgIC8vIC4uLmZ1cnRoZXIgYWN0aW9ucy4uLgovLy8gICAgIH0KLy8vICAgICAv" + + "LyAuLi4KLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL3N0ZGxpYi1qZXR0b24taW50ZXJmYWNlI3By" + + "b3ZpZGV3YWxsZXRhZGRyZXNzCi8vLyAqIGh0dHBzOi8vZ2l0aHViLmNvbS90b24tYmxvY2tjaGFpbi9URVBzL2Jsb2IvbWFzdGVyL3RleHQvMDA4OS1qZXR0b24td2Fs" + + "bGV0LWRpc2NvdmVyeS5tZCNzY2hlbWUKLy8vCm1lc3NhZ2UoMHgyYzc2Yjk3MykgUHJvdmlkZVdhbGxldEFkZHJlc3MgewogICAgLy8vIFVuaXF1ZSBpZGVudGlmaWVy" + + "IHVzZWQgdG8gdHJhY2UgdHJhbnNhY3Rpb25zIGFjcm9zcyBtdWx0aXBsZSBjb250cmFjdHMuCiAgICAvLy8gU2V0dGluZyBpdCB0byAwIG1lYW5zIHdlIGRvbid0IG1h" + + "cmsgbWVzc2FnZXMgdG8gdHJhY2UgcmVxdWVzdHMuCiAgICBxdWVyeUlkOiBJbnQgYXMgdWludDY0OwoKICAgIC8vLyBPd25lciBvZiB0aGUgcmVxdWVzdGVkIEpldHRv" + + "biBXYWxsZXQg4oCUIHRoZSByZWd1bGFyIFRPTiB3YWxsZXQgYWRkcmVzcy4KICAgIG93bmVyQWRkcmVzczogQWRkcmVzczsKCiAgICAvLy8gV2hldGhlciB0aGUgYG93" + + "bmVyQWRkcmVzc2Agc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSByZXNwb25zZS4KICAgIGluY2x1ZGVBZGRyZXNzOiBCb29sOwp9CgovLy8gTWVzc2FnZSBzdHJ1Y3Qu" + + "IEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNi42LgovLy8KLy8vIFJlcHJlc2VudHMgYSBtZXNzYWdlIGJvZHkgZGVmaW5lZCBpbiBURVAtODk6IGl0IGlzIHNlbnQgZnJv" + + "bSB0aGUgSmV0dG9uIE1pbnRlcgovLy8gY29udHJhY3QgaW4gcmVzcG9uc2UgdG8gYSByZXF1ZXN0IGZvciB0aGUgSmV0dG9uIFdhbGxldCBhZGRyZXNzCi8vLyBmb3Ig" + + "dGhlIGBvd25lckFkZHJlc3NgLgovLy8KLy8vICMjIyMgVEwtQiBmcm9tIHRoZSBURVAtODkKLy8vCi8vLyBgYGB0bGIKLy8vIHRha2Vfd2FsbGV0X2FkZHJlc3MjZDE3" + + "MzU0MDAKLy8vICAgICBxdWVyeV9pZDp1aW50NjQKLy8vICAgICB3YWxsZXRfYWRkcmVzczpNc2dBZGRyZXNzCi8vLyAgICAgb3duZXJfYWRkcmVzczooTWF5YmUgXk1z" + + "Z0FkZHJlc3MpCi8vLyAgICAgPSBJbnRlcm5hbE1zZ0JvZHk7Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9z" + + "dGRsaWItamV0dG9uLWludGVyZmFjZSN0YWtld2FsbGV0YWRkcmVzcwovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvc3RkbGliLWpldHRvbi1pbnRl" + + "cmZhY2UjcHJvdmlkZXdhbGxldGFkZHJlc3MKLy8vICogaHR0cHM6Ly9naXRodWIuY29tL3Rvbi1ibG9ja2NoYWluL1RFUHMvYmxvYi9tYXN0ZXIvdGV4dC8wMDg5LWpl" + + "dHRvbi13YWxsZXQtZGlzY292ZXJ5Lm1kI3NjaGVtZQovLy8KbWVzc2FnZSgweGQxNzM1NDAwKSBUYWtlV2FsbGV0QWRkcmVzcyB7CiAgICAvLy8gVW5pcXVlIGlkZW50" + + "aWZpZXIgdXNlZCB0byB0cmFjZSB0cmFuc2FjdGlvbnMgYWNyb3NzIG11bHRpcGxlIGNvbnRyYWN0cy4KICAgIC8vLyBTZXR0aW5nIGl0IHRvIDAgbWVhbnMgd2UgZG9u" + + "J3QgbWFyayBtZXNzYWdlcyB0byB0cmFjZSByZXF1ZXN0cy4KICAgIHF1ZXJ5SWQ6IEludCBhcyB1aW50NjQ7CgogICAgLy8vIEFkZHJlc3Mgb2YgdGhlIHJlcXVlc3Rl" + + "ZCBKZXR0b24gV2FsbGV0LgogICAgd2FsbGV0QWRkcmVzczogQWRkcmVzczsKCiAgICAvLy8gT3duZXIgb2YgdGhlIHJlcXVlc3RlZCBKZXR0b24gV2FsbGV0IOKAlCB0" + + "aGUgcmVndWxhciBUT04gd2FsbGV0IGFkZHJlc3MuCiAgICBvd25lckFkZHJlc3M6IENlbGw/Owp9CgovLy8gTWVzc2FnZSBzdHJ1Y3QuIEF2YWlsYWJsZSBzaW5jZSBU" + + "YWN0IDEuNi42LgovLy8KLy8vIFJlcHJlc2VudHMgYSBtZXNzYWdlIGJvZHkgb2Z0ZW4gdXNlZCBpbiBKZXR0b24gaW1wbGVtZW50YXRpb25zOiBpdCBpcyBzZW50Ci8v" + + "LyB0byB0aGUgSmV0dG9uIE1pbnRlciBjb250cmFjdCB0byB1cGRhdGUgdGhlIEpldHRvbiBjb250ZW50IG1ldGFkYXRhLAovLy8gd2hpY2ggaXMgZGVmaW5lZCBpbiBU" + + "RVAtNjQuCi8vLwovLy8gTk9URTogQWx3YXlzIHZlcmlmeSB0aGUgc2VuZGVyIG9mIHRoZSBtZXNzYWdlIGJlZm9yZSB0YWtpbmcgYW55IGZ1cnRoZXIgYWN0aW9ucy4K" + + "Ly8vCi8vLyBgYGB0YWN0Ci8vLyBpbXBvcnQgIkBzdGRsaWIvamV0dG9uLWludGVyZmFjZSI7Ci8vLwovLy8gY29udHJhY3QgSmV0dG9uTWludGVyKAovLy8gICAgIG93" + + "bmVyOiBBZGRyZXNzLAovLy8gICAgIGpldHRvbkNvbnRlbnQ6IENlbGwsCi8vLyAgICAgLy8gLi4uCi8vLyApIHsKLy8vICAgICAvLyAuLi4KLy8vICAgICByZWNlaXZl" + + "KG1zZzogSmV0dG9uVXBkYXRlQ29udGVudCkgewovLy8gICAgICAgICByZXF1aXJlKHNlbmRlcigpID09IHNlbGYub3duZXIsICJJbmNvcnJlY3Qgc2VuZGVyIik7Ci8v" + + "LyAgICAgICAgIHNlbGYuamV0dG9uQ29udGVudCA9IG1zZy5jb250ZW50OwovLy8gICAgIH0KLy8vICAgICAvLyAuLi4KLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNlZToK" + + "Ly8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL3N0ZGxpYi1qZXR0b24taW50ZXJmYWNlI2pldHRvbnVwZGF0ZWNvbnRlbnQKLy8vICogaHR0cHM6Ly9n" + + "aXRodWIuY29tL3Rvbi1ibG9ja2NoYWluL1RFUHMvYmxvYi9tYXN0ZXIvdGV4dC8wMDY0LXRva2VuLWRhdGEtc3RhbmRhcmQubWQKLy8vCm1lc3NhZ2UoNCkgSmV0dG9u" + + "VXBkYXRlQ29udGVudCB7CiAgICAvLy8gVW5pcXVlIGlkZW50aWZpZXIgdXNlZCB0byB0cmFjZSB0cmFuc2FjdGlvbnMgYWNyb3NzIG11bHRpcGxlIGNvbnRyYWN0cy4K" + + "ICAgIC8vLyBTZXR0aW5nIGl0IHRvIDAgbWVhbnMgd2UgZG9uJ3QgbWFyayBtZXNzYWdlcyB0byB0cmFjZSByZXF1ZXN0cy4KICAgIHF1ZXJ5SWQ6IEludCBhcyB1aW50" + + "NjQ7CgogICAgLy8vIE5ldyBKZXR0b24gY29udGVudCBtZXRhZGF0YSwgc3VjaCBhcyBpdHMgbmFtZSwgZGVzY3JpcHRpb24sIHN5bWJvbCwgZXRjLgogICAgLy8vCiAg" + + "ICAvLy8gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJsb2NrY2hhaW4vVEVQcy9ibG9iL21hc3Rlci90ZXh0LzAwNjQtdG9rZW4tZGF0YS1zdGFuZGFyZC5tZAog" + + "ICAgLy8vCiAgICBuZXdDb250ZW50OiBDZWxsOwp9CgovLy8gTWVzc2FnZSBzdHJ1Y3QuIEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNi42LgovLy8KLy8vIFJlcHJlc2Vu" + + "dHMgYSBtZXNzYWdlIGJvZHkgb2Z0ZW4gdXNlZCBpbiBKZXR0b24gaW1wbGVtZW50YXRpb25zOiBpdCBpcyBzZW50Ci8vLyB0byB0aGUgSmV0dG9uIE1pbnRlciBjb250" + + "cmFjdCB0byBtaW50IGBtaW50TWVzc2FnZS5hbW91bnRgIG9mIHRva2VucwovLy8gZm9yIHRoZSBgcmVjZWl2ZXJgLgovLy8KLy8vIE5PVEU6IEFsd2F5cyB2ZXJpZnkg" + + "dGhlIHNlbmRlciBvZiB0aGUgbWVzc2FnZSBiZWZvcmUgdGFraW5nIGFueSBmdXJ0aGVyIGFjdGlvbnMuCi8vLwovLy8gYGBgdGFjdAovLy8gaW1wb3J0ICJAc3RkbGli" + + "L2pldHRvbi1pbnRlcmZhY2UiOwovLy8KLy8vIGNvbnRyYWN0IEpldHRvbk1pbnRlcigKLy8vICAgICBvd25lcjogQWRkcmVzcywKLy8vICAgICBtaW50YWJsZTogQm9v" + + "bCwKLy8vICAgICAvLyAuLi4KLy8vICkgewovLy8gICAgIC8vIC4uLgovLy8gICAgIHJlY2VpdmUobXNnOiBNaW50KSB7Ci8vLyAgICAgICAgIHJlcXVpcmUoc2VuZGVy" + + "KCkgPT0gc2VsZi5vd25lciwgIkluY29ycmVjdCBzZW5kZXIiKTsKLy8vICAgICAgICAgcmVxdWlyZShzZWxmLm1pbnRhYmxlLCAiTWludCBpcyBjbG9zZWQiKTsKLy8v" + + "ICAgICAgICAgLy8gLi4uZnVydGhlciBhY3Rpb25zLi4uCi8vLyAgICAgfQovLy8gICAgIC8vIC4uLgovLy8gfQovLy8gYGBgCi8vLwovLy8gU2VlOiBodHRwczovL2Rv" + + "Y3MudGFjdC1sYW5nLm9yZy9yZWYvc3RkbGliLWpldHRvbi1pbnRlcmZhY2UjbWludAovLy8KbWVzc2FnZSgweDY0MmI3ZDA3KSBNaW50IHsKICAgIC8vLyBVbmlxdWUg" + + "aWRlbnRpZmllciB1c2VkIHRvIHRyYWNlIHRyYW5zYWN0aW9ucyBhY3Jvc3MgbXVsdGlwbGUgY29udHJhY3RzLgogICAgLy8vIFNldHRpbmcgaXQgdG8gMCBtZWFucyB3" + + "ZSBkb24ndCBtYXJrIG1lc3NhZ2VzIHRvIHRyYWNlIHJlcXVlc3RzLgogICAgcXVlcnlJZDogSW50IGFzIHVpbnQ2NDsKCiAgICAvLy8gUmVjaXBpZW50IG9mIHRoZSBt" + + "aW50ZWQgdG9rZW5zIOKAlCB0aGUgcmVndWxhciBUT04gd2FsbGV0IGFkZHJlc3MuCiAgICByZWNlaXZlcjogQWRkcmVzczsKCiAgICAvLy8gVGhlIGFtb3VudCBvZiBu" + + "YW5vdG9ucyB0byBiZSBzZW50IHRvIHRoZSBgcmVjZWl2ZXJgIGR1cmluZyB0aGUgdHJhbnNmZXIuCiAgICB0b25BbW91bnQ6IEludCBhcyBjb2luczsKCiAgICAvLy8g" + + "SWYgdGhlIEpldHRvbiBXYWxsZXQgZm9yIHRoZSBnaXZlbiBgcmVjZWl2ZXJgIGRvZXMgbm90IGV4aXN0LCB0aGlzIG1lc3NhZ2UKICAgIC8vLyB3aWxsIGJlIHVzZWQg" + + "Zm9yIGl0cyBkZXBsb3ltZW50IGFuZCBtaW50aW5nLiBPdGhlcndpc2UsIG9ubHkgbWludHMgdGhlIHRva2VucwogICAgLy8vIGZvciB0aGUgYHJlY2VpdmVyYCB1c2lu" + + "ZyB0aGVpciBjb3JyZXNwb25kaW5nIEpldHRvbiBXYWxsZXQuCiAgICBtaW50TWVzc2FnZTogSmV0dG9uVHJhbnNmZXJJbnRlcm5hbDsKfQoKLy8vIE1lc3NhZ2Ugc3Ry" + + "dWN0LiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjYuNi4KLy8vCi8vLyBSZXByZXNlbnRzIGEgbWVzc2FnZSBib2R5IG9mdGVuIHVzZWQgaW4gSmV0dG9uIGltcGxlbWVu" + + "dGF0aW9uczogaXQgaXMgc2VudAovLy8gdG8gdGhlIEpldHRvbiBNaW50ZXIgY29udHJhY3QgdG8gZGlzYWJsZSB0aGUgYWJpbGl0eSB0byBtaW50IG5ldyB0b2tlbnMu" + + "Ci8vLwovLy8gTk9URTogQWx3YXlzIHZlcmlmeSB0aGUgc2VuZGVyIG9mIHRoZSBtZXNzYWdlIGJlZm9yZSB0YWtpbmcgYW55IGZ1cnRoZXIgYWN0aW9ucy4KLy8vCi8v" + + "LyBgYGB0YWN0Ci8vLyBpbXBvcnQgIkBzdGRsaWIvamV0dG9uLWludGVyZmFjZSI7Ci8vLwovLy8gY29udHJhY3QgSmV0dG9uTWludGVyKAovLy8gICAgIG93bmVyOiBB" + + "ZGRyZXNzLAovLy8gICAgIG1pbnRhYmxlOiBCb29sLAovLy8gICAgIC8vIC4uLgovLy8gKSB7Ci8vLyAgICAgLy8gLi4uCi8vLyAgICAgcmVjZWl2ZShtc2c6IENsb3Nl" + + "TWludGluZykgewovLy8gICAgICAgICByZXF1aXJlKHNlbmRlcigpID09IHNlbGYub3duZXIsICJJbmNvcnJlY3Qgc2VuZGVyIik7Ci8vLyAgICAgICAgIHNlbGYubWlu" + + "dGFibGUgPSBmYWxzZTsKLy8vICAgICAgICAgY2FzaGJhY2soc2VuZGVyKCkpOwovLy8gICAgIH0KLy8vICAgICAvLyAuLi4KLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNl" + + "ZTogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL3N0ZGxpYi1qZXR0b24taW50ZXJmYWNlI2Nsb3NlbWludGluZwovLy8KbWVzc2FnZSgyMikgQ2xvc2VNaW50" + + "aW5nIHt9CgovLy8gTWVzc2FnZSBzdHJ1Y3QuIEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNi42LgovLy8KLy8vIFJlcHJlc2VudHMgYSBtZXNzYWdlIGJvZHkgb2Z0ZW4g" + + "dXNlZCBpbiBKZXR0b24gaW1wbGVtZW50YXRpb25zOiBpdCBpcyBzZW50Ci8vLyB0byB0aGUgSmV0dG9uIE1pbnRlciBjb250cmFjdCB0byBjaGFuZ2UgdGhlIG93bmVy" + + "IHRvIGBuZXdPd25lcmAuCi8vLwovLy8gTk9URTogQWx3YXlzIHZlcmlmeSB0aGUgc2VuZGVyIG9mIHRoZSBtZXNzYWdlIGJlZm9yZSB0YWtpbmcgYW55IGZ1cnRoZXIg" + + "YWN0aW9ucy4KLy8vCi8vLyBgYGB0YWN0Ci8vLyBpbXBvcnQgIkBzdGRsaWIvamV0dG9uLWludGVyZmFjZSI7Ci8vLwovLy8gY29udHJhY3QgSmV0dG9uTWludGVyKAov" + + "Ly8gICAgIG93bmVyOiBBZGRyZXNzLAovLy8gICAgIC8vIC4uLgovLy8gKSB7Ci8vLyAgICAgLy8gLi4uCi8vLyAgICAgcmVjZWl2ZShtc2c6IENoYW5nZU93bmVyKSB7" + + "Ci8vLyAgICAgICAgIHJlcXVpcmUoc2VuZGVyKCkgPT0gc2VsZi5vd25lciwgIkluY29ycmVjdCBzZW5kZXIiKTsKLy8vICAgICAgICAgc2VsZi5vd25lciA9IG1zZy5u" + + "ZXdPd25lcjsKLy8vICAgICB9Ci8vLyAgICAgLy8gLi4uCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6IGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9zdGRs" + + "aWItamV0dG9uLWludGVyZmFjZSNjaGFuZ2Vvd25lcgovLy8KbWVzc2FnZSgzKSBDaGFuZ2VPd25lciB7CiAgICAvLy8gVW5pcXVlIGlkZW50aWZpZXIgdXNlZCB0byB0" + + "cmFjZSB0cmFuc2FjdGlvbnMgYWNyb3NzIG11bHRpcGxlIGNvbnRyYWN0cy4KICAgIC8vLyBTZXR0aW5nIGl0IHRvIDAgbWVhbnMgd2UgZG9uJ3QgbWFyayBtZXNzYWdl" + + "cyB0byB0cmFjZSByZXF1ZXN0cy4KICAgIHF1ZXJ5SWQ6IEludCBhcyB1aW50NjQ7CgogICAgLy8vIE5ldyBvd25lciBvZiB0aGUgY29udHJhY3Qg4oCUIHRoZSByZWd1" + + "bGFyIFRPTiB3YWxsZXQgYWRkcmVzcy4KICAgIG5ld093bmVyOiBBZGRyZXNzOwp9Cg=="; files["libs/ownable.tact"] = "Ly8vIE1lc3NhZ2Ugc2VudCBieSB0aGUgb3duZXIgdG8gdHJhbnNmZXIgb3duZXJzaGlwIG9mIGEgY29udHJhY3QuCi8vLwovLy8gU2VlOiBodHRwczovL2RvY3MudGFj" + "dC1sYW5nLm9yZy9yZWYvc3RkbGliLW93bmFibGUjY2hhbmdlb3duZXIKLy8vCm1lc3NhZ2UoMHg4MTlkYmU5OSkgQ2hhbmdlT3duZXIgewogICAgLy8vIFF1ZXJ5IElE" + diff --git a/src/stdlib/stdlib/libs/jetton-interface.tact b/src/stdlib/stdlib/libs/jetton-interface.tact new file mode 100644 index 0000000000..9923cf8edf --- /dev/null +++ b/src/stdlib/stdlib/libs/jetton-interface.tact @@ -0,0 +1,752 @@ +/// Struct. Available since Tact 1.6.6. +/// +/// Describes the data returned by the Jetton Minter getter function `get_jetton_data()`, +/// which is defined in TEP-74 and should be present in every Jetton Minter implementation. +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// totalSupply: Int as coins, +/// owner: Address, +/// jettonContent: Cell, +/// mintable: Bool, +/// ) { +/// // ... +/// get fun get_jetton_data(): JettonMinterData { +/// return JettonMinterData { +/// totalSupply: self.totalSupply, +/// mintable: self.mintable, +/// adminAddress: self.owner, +/// jettonContent: self.jettonContent, +/// jettonWalletCode: codeOf JettonWallet, +/// }; +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettonminterdata +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#get-methods-1 +/// +struct JettonMinterData { + /// Total supply of this Jetton Minter — the amount of tokens. + totalSupply: Int as coins; + + /// Whether the tokens are still mintable. + mintable: Bool; + + /// Owner of the Jetton Minter — the regular TON wallet address. + owner: Address; + + /// Jetton content metadata, such as its name, description, symbol, etc. + /// + /// See: https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md + /// + content: Cell; + + /// Code of the child Jetton Wallet contract that Jetton Minter would + /// deploy for each user's TON wallet. + jettonWalletCode: Cell; +} + +/// Struct. Available since Tact 1.6.6. +/// +/// Describes the data returned by the Jetton Wallet getter function `get_wallet_data()`, +/// which is defined in TEP-74 and should be present in every Jetton Wallet implementation. +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonWallet( +/// owner: Address, +/// minter: Address, +/// balance: Int as coins, +/// ) { +/// // ... +/// get fun get_wallet_data(): JettonWalletData { +/// return JettonWalletData { +/// owner: self.owner, +/// minter: self.minter, +/// balance: self.balance, +/// code: myCode(), +/// }; +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettonwalletdata +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#get-methods +/// +struct JettonWalletData { + /// Balance of the Jetton Wallet in tokens. + balance: Int as coins; + + /// Owner of the Jetton Wallet — the regular TON wallet address. + owner: Address; + + /// Address of the parent Jetton Minter contract. + minter: Address; + + /// Code of this Jetton Wallet contract. + code: Cell; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-74: it is sent to one Jetton Wallet +/// contract to initialize a transfer of tokens to another Jetton Wallet. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// #### TL-B from the TEP-74 +/// +/// ```tlb +/// transfer#0f8a7ea5 +/// query_id:uint64 +/// amount:(VarUInteger 16) +/// destination:MsgAddress +/// response_destination:MsgAddress +/// custom_payload:(Maybe ^Cell) +/// forward_ton_amount:(VarUInteger 16) +/// forward_payload:(Either Cell ^Cell) +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage example +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonWallet( +/// owner: Address, +/// balance: Int as coins, +/// // ... +/// ) { +/// receive(msg: JettonTransfer) { +/// forceBasechain(msg.destination); +/// require(sender() == self.owner, "Incorrect sender"); +/// +/// self.balance -= msg.amount; +/// require(self.balance >= 0, "Incorrect balance after send"); +/// +/// // ...further checks and actions... +/// } +/// +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettontransfer +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettontransferinternal +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer +/// +message(0xf8a7ea5) JettonTransfer { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Amount of tokens to be transferred. + amount: Int as coins; + + /// A new owner of the transferred tokens — the regular TON wallet address. + destination: Address; + + /// Address where to send a response confirming a successful transfer + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; + + /// Optional custom data received from another Jetton Wallet. + customPayload: Cell?; + + /// The amount of nanotons to be sent to the `destination` address. + forwardTonAmount: Int as coins; + + /// Optional custom data to be sent to the `destination` address. + forwardPayload: Slice as remaining; // (Either Cell ^Cell) +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-74: it is sent and received by the +/// Jetton Wallet contract to make a transfer or process an incoming one, respectively. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// #### TL-B from the TEP-74 +/// +/// ```tlb +/// internal_transfer#178d4519 +/// query_id:uint64 +/// a mount:(VarUInteger 16) +/// from:MsgAddress +/// response_address:MsgAddress +/// forward_ton_amount:(VarUInteger 16) +/// forward_payload:(Either Cell ^Cell) +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage examples +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonWallet( +/// owner: Address, +/// minter: Address, +/// balance: Int as coins, +/// ) { +/// receive(msg: JettonTransfer) { +/// forceBasechain(msg.destination); +/// require(sender() == self.owner, "Incorrect sender"); +/// +/// // ...other intermediate checks... +/// +/// deploy(DeployParameters { +/// value: 0, +/// mode: SendRemainingValue, +/// bounce: true, +/// body: JettonTransferInternal { +/// queryId: msg.queryId, +/// amount: msg.amount, +/// sender: self.owner, +/// responseDestination: msg.responseDestination, +/// forwardTonAmount: msg.forwardTonAmount, +/// forwardPayload: msg.forwardPayload, +/// }.toCell(), +/// init: initOf JettonWallet(0, msg.destination, self.minter), +/// }); +/// } +/// +/// receive(msg: JettonTransferInternal) { +/// self.balance += msg.amount; +/// +/// // This message should come only from master, or from other JettonWallet +/// let wallet: StateInit = initOf JettonWallet(0, msg.sender, self.minter); +/// if (!wallet.hasSameBasechainAddress(sender())) { +/// require(self.minter == sender(), "Incorrect sender"); +/// } +/// +/// // ...further actions... +/// } +/// +/// bounced(msg: bounced) { +/// self.balance += msg.amount; +/// } +/// +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettontransferinternal +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettontransfer +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#tl-b-schema +/// +message(0x178d4519) JettonTransferInternal { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Amount of transferred tokens. + amount: Int as coins; + + /// Previous owner of the transferred tokens — the regular TON wallet address. + sender: Address; + + /// Address where to send a response confirming a successful transfer + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; + + /// The amount of nanotons to be sent to the new owner's address. + forwardTonAmount: Int as coins; + + /// Optional custom data to be sent to the new owner's address. + forwardPayload: Slice as remaining; // (Either Cell ^Cell) +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-74: it is sent from the Jetton Wallet +/// contract to the `self.owner` address to notify about the incoming transfer +/// of tokens from their previous owner — the `sender`. +/// +/// #### TL-B from the TEP-74 +/// +/// ```tlb +/// transfer_notification#7362d09c +/// query_id:uint64 +/// amount:(VarUInteger 16) +/// sender:MsgAddress +/// forward_payload:(Either Cell ^Cell) +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage example +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonWallet( +/// owner: Address, +/// // ... +/// ) { +/// // ... +/// receive(msg: JettonTransferInternal) { +/// // ...prior checks... +/// +/// let msgValue: Int = ctx.value; +/// if (msg.forwardTonAmount > 0) { +/// let fwdFee: Int = ctx.readForwardFee(); +/// msgValue -= msg.forwardTonAmount + fwdFee; +/// message(MessageParameters { +/// to: self.owner, +/// value: msg.forwardTonAmount, +/// mode: SendPayGasSeparately, +/// bounce: false, +/// body: JettonTransferNotification { +/// queryId: msg.queryId, +/// amount: msg.amount, +/// sender: msg.sender, +/// forwardPayload: msg.forwardPayload, +/// }.toCell(), +/// }); +/// } +/// +/// // ...further actions... +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettontransfernotification +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettontransferinternal +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer +/// +message(0x7362d09c) JettonTransferNotification { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Amount of transferred tokens. + amount: Int as coins; + + /// Previous owner of the transferred tokens — the regular TON wallet address. + sender: Address; + + /// Optional custom data to be sent to the new owner's address. + forwardPayload: Slice as remaining; // (Either Cell ^Cell) +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-74: it is sent to the Jetton Wallet +/// contract from its TON wallet owner to decrease its balance by a certain `amount`. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// #### TL-B from the TEP-74 +/// +/// ```tlb +/// burn#595f07bc +/// query_id:uint64 +/// amount:(VarUInteger 16) +/// response_destination:MsgAddress +/// custom_payload:(Maybe ^Cell) +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage example +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonWallet( +/// owner: Address, +/// balance: Int as coins, +/// // ... +/// ) { +/// // ... +/// receive(msg: JettonBurn) { +/// require(sender() == self.owner, "Incorrect sender"); +/// self.balance -= msg.amount; +/// require(self.balance >= 0, "Incorrect balance after send"); +/// // ...further actions... +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettonburn +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#2-burn +/// +message(0x595f07bc) JettonBurn { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Amount of burned tokens. + amount: Int as coins; + + /// Address where to send a response confirming a successful burn + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; + + /// Optional custom data received from another Jetton Wallet. + customPayload: Cell?; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-74: it is sent from the Jetton Wallet +/// contract to the Jetton Minter to notify about the successful burn +/// and reduce the total supply of tokens. +/// +/// #### TL-B from the TEP-74 +/// +/// ```tlb +/// burn_notification#7bdd97de +/// query_id:uint64 +/// amount:(VarUInteger 16) +/// sender:MsgAddress +/// response_destination:MsgAddress +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage examples +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// // ... +/// ) { +/// // ... +/// receive(msg: JettonBurnNotification) { +/// let sender = parseStdAddress(sender().asSlice()); +/// let wallet = getJettonBasechainWalletByOwner(msg.sender); +/// +/// require( +/// sender.workchain == 0 && sender.address == wallet.hash!!, +/// "Unauthorized burn", +/// ); +/// // ...further actions... +/// } +/// // ... +/// } +/// +/// contract JettonWallet( +/// balance: Int as coins, +/// // ... +/// ) { +/// // ... +/// bounced(msg: bounced) { +/// self.balance += msg.amount; +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettonburnnotification +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#tl-b-schema +/// +message(0x7bdd97de) JettonBurnNotification { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Amount of burned tokens. + amount: Int as coins; + + /// Previous owner of the burned tokens — the regular TON wallet address. + sender: Address; + + /// Address where to send a response confirming a successful burn + /// and the remaining Toncoin from the incoming message. + responseDestination: Address?; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-74: it is sent from either Jetton Minter +/// or Jetton Wallet to refund the remaining Toncoin from the incoming message. +/// +/// #### TL-B from the TEP-74 +/// +/// ```tlb +/// excesses#d53276db +/// query_id:uint64 +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage example +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// // ... +/// ) { +/// // ... +/// receive(msg: JettonBurnNotification) { +/// // ... +/// if (msg.responseDestination != null) { +/// message(MessageParameters { +/// to: msg.responseDestination!!, +/// body: JettonExcesses { queryId: msg.queryId }.toCell(), +/// value: 0, +/// bounce: false, // do not bounce +/// mode: SendRemainingValue | SendIgnoreErrors, +/// }); +/// } +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettonexcesses +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer +/// +message(0xd53276db) JettonExcesses { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-89: it is sent to the Jetton Minter +/// contract to request the Jetton Wallet address for the `ownerAddress`. +/// +/// #### TL-B from the TEP-89 +/// +/// ```tlb +/// provide_wallet_address#2c76b973 +/// query_id:uint64 +/// owner_address:MsgAddress +/// include_address:Bool +/// = InternalMsgBody; +/// ``` +/// +/// #### Usage example +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// // ... +/// ) { +/// // ... +/// receive(msg: ProvideWalletAddress) { +/// let ownerWorkchain = parseStdAddress(msg.ownerAddress.asSlice()).workchain; +/// // ...further actions... +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#providewalletaddress +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md#scheme +/// +message(0x2c76b973) ProvideWalletAddress { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Owner of the requested Jetton Wallet — the regular TON wallet address. + ownerAddress: Address; + + /// Whether the `ownerAddress` should be included in the response. + includeAddress: Bool; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body defined in TEP-89: it is sent from the Jetton Minter +/// contract in response to a request for the Jetton Wallet address +/// for the `ownerAddress`. +/// +/// #### TL-B from the TEP-89 +/// +/// ```tlb +/// take_wallet_address#d1735400 +/// query_id:uint64 +/// wallet_address:MsgAddress +/// owner_address:(Maybe ^MsgAddress) +/// = InternalMsgBody; +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#takewalletaddress +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#providewalletaddress +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md#scheme +/// +message(0xd1735400) TakeWalletAddress { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Address of the requested Jetton Wallet. + walletAddress: Address; + + /// Owner of the requested Jetton Wallet — the regular TON wallet address. + ownerAddress: Cell?; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body often used in Jetton implementations: it is sent +/// to the Jetton Minter contract to update the Jetton content metadata, +/// which is defined in TEP-64. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// owner: Address, +/// jettonContent: Cell, +/// // ... +/// ) { +/// // ... +/// receive(msg: JettonUpdateContent) { +/// require(sender() == self.owner, "Incorrect sender"); +/// self.jettonContent = msg.content; +/// } +/// // ... +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/stdlib-jetton-interface#jettonupdatecontent +/// * https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md +/// +message(4) JettonUpdateContent { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// New Jetton content metadata, such as its name, description, symbol, etc. + /// + /// See: https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md + /// + newContent: Cell; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body often used in Jetton implementations: it is sent +/// to the Jetton Minter contract to mint `mintMessage.amount` of tokens +/// for the `receiver`. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// owner: Address, +/// mintable: Bool, +/// // ... +/// ) { +/// // ... +/// receive(msg: Mint) { +/// require(sender() == self.owner, "Incorrect sender"); +/// require(self.mintable, "Mint is closed"); +/// // ...further actions... +/// } +/// // ... +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/stdlib-jetton-interface#mint +/// +message(0x642b7d07) Mint { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// Recipient of the minted tokens — the regular TON wallet address. + receiver: Address; + + /// The amount of nanotons to be sent to the `receiver` during the transfer. + tonAmount: Int as coins; + + /// If the Jetton Wallet for the given `receiver` does not exist, this message + /// will be used for its deployment and minting. Otherwise, only mints the tokens + /// for the `receiver` using their corresponding Jetton Wallet. + mintMessage: JettonTransferInternal; +} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body often used in Jetton implementations: it is sent +/// to the Jetton Minter contract to disable the ability to mint new tokens. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// owner: Address, +/// mintable: Bool, +/// // ... +/// ) { +/// // ... +/// receive(msg: CloseMinting) { +/// require(sender() == self.owner, "Incorrect sender"); +/// self.mintable = false; +/// cashback(sender()); +/// } +/// // ... +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/stdlib-jetton-interface#closeminting +/// +message(22) CloseMinting {} + +/// Message struct. Available since Tact 1.6.6. +/// +/// Represents a message body often used in Jetton implementations: it is sent +/// to the Jetton Minter contract to change the owner to `newOwner`. +/// +/// NOTE: Always verify the sender of the message before taking any further actions. +/// +/// ```tact +/// import "@stdlib/jetton-interface"; +/// +/// contract JettonMinter( +/// owner: Address, +/// // ... +/// ) { +/// // ... +/// receive(msg: ChangeOwner) { +/// require(sender() == self.owner, "Incorrect sender"); +/// self.owner = msg.newOwner; +/// } +/// // ... +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/stdlib-jetton-interface#changeowner +/// +message(3) ChangeOwner { + /// Unique identifier used to trace transactions across multiple contracts. + /// Setting it to 0 means we don't mark messages to trace requests. + queryId: Int as uint64; + + /// New owner of the contract — the regular TON wallet address. + newOwner: Address; +}