diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 9bdc67ca9..81c4eafe2 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -20,10 +20,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node.js 18 for backwards compat tests - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18 # without caching @@ -46,7 +46,7 @@ jobs: yarn config delete ignore-engines - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "yarn" diff --git a/CHANGELOG.md b/CHANGELOG.md index 221a9b688..99a91449f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921) - Docs: added Ston.fi cookbook: PR [#956](https://github.com/tact-lang/tact/pull/956) - Docs: added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) +- Docs: added security best practices: PR [#1070](https://github.com/tact-lang/tact/pull/1070) - Ability to specify a compile-time method ID expression for getters: PR [#922](https://github.com/tact-lang/tact/pull/922) and PR [#932](https://github.com/tact-lang/tact/pull/932) - Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#964](https://github.com/tact-lang/tact/pull/964), PR [#969](https://github.com/tact-lang/tact/pull/969) - Docs: automatic links to Web IDE from all code blocks: PR [#994](https://github.com/tact-lang/tact/pull/994) @@ -84,7 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Wider range of serialization options for integers — `uint1` through `uint256` and `int1` through `int257`: PR [#558](https://github.com/tact-lang/tact/pull/558), PR [#937](https://github.com/tact-lang/tact/pull/937) - The `deepEquals` method for the `Map` type: PR [#637](https://github.com/tact-lang/tact/pull/637), PR [#939](https://github.com/tact-lang/tact/pull/939) - `asm` bodies for module-level functions: PR [#769](https://github.com/tact-lang/tact/pull/769), PR [#825](https://github.com/tact-lang/tact/pull/825) -- Corresponding stdlib functions for new TVM instructions from 2023.07 and 2024.04 upgrades: PR [#331](https://github.com/tact-lang/tact/pull/331). Added the `storeBuilder` extension function and `gasConsumed`, `getComputeFee`, `getStorageFee`, `getForwardFee`, `getSimpleComputeFee`, `getSimpleForwardFee`, `getOriginalFwdFee`, `myStorageDue` functions. +- Corresponding stdlib functions for new TVM instructions from 2023.07 and 2024.04 upgrades: PR [#331](https://github.com/tact-lang/tact/pull/331), PR [#1062](https://github.com/tact-lang/tact/pull/1062). Added the `storeBuilder` extension function and `gasConsumed`, `getComputeFee`, `getStorageFee`, `getForwardFee`, `getSimpleComputeFee`, `getSimpleForwardFee`, `getOriginalFwdFee`, `myStorageDue` functions. - `slice`, `rawSlice`, `ascii` and `crc32` built-in functions: PR [#787](https://github.com/tact-lang/tact/pull/787), PR [#799](https://github.com/tact-lang/tact/pull/799), PR [#951](https://github.com/tact-lang/tact/pull/951) - `Builder.storeMaybeRef`, `parseStdAddress` and `parseVarAddress` stdlib functions: PR [#793](https://github.com/tact-lang/tact/pull/793), PR [#950](https://github.com/tact-lang/tact/pull/950) - The compiler development guide: PR [#833](https://github.com/tact-lang/tact/pull/833) diff --git a/cspell.json b/cspell.json index 45bdb11fa..b48885a05 100644 --- a/cspell.json +++ b/cspell.json @@ -47,6 +47,7 @@ "funs", "frontmatter", "Georgiy", + "getsimpleforwardfee", "gettest", "Héctor", "infixl", diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 5a7c09e94..b286f45c7 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -189,6 +189,7 @@ export default defineConfig({ { slug: 'book/masterchain' }, { slug: 'book/func' }, { slug: 'book/programmatic' }, + { slug: 'book/security-best-practices' }, ], }, { diff --git a/docs/src/content/docs/book/debug.mdx b/docs/src/content/docs/book/debug.mdx index a3305ec88..fde2420c9 100644 --- a/docs/src/content/docs/book/debug.mdx +++ b/docs/src/content/docs/book/debug.mdx @@ -693,7 +693,7 @@ If you're overwhelmed by the testing setup of [Blueprint][bp] or just want to te ```json filename="package.json" {3} { "scripts": { - "lab": "blueprint build 1>/dev/null && yarn test -t plays" + "lab": "blueprint build --all 1>/dev/null && yarn test -t plays" } } ``` @@ -703,7 +703,7 @@ If you're overwhelmed by the testing setup of [Blueprint][bp] or just want to te ```json filename="package.json" {3-4} { "scripts": { - "build": "blueprint build | out-null", + "build": "blueprint build --all | out-null", "lab": "yarn build && yarn test -t plays" } } diff --git a/docs/src/content/docs/book/security-best-practices.mdx b/docs/src/content/docs/book/security-best-practices.mdx new file mode 100644 index 000000000..acd2a2d7e --- /dev/null +++ b/docs/src/content/docs/book/security-best-practices.mdx @@ -0,0 +1,443 @@ +--- +title: Security best practices +description: "Several anti-patterns and potential attack vectors, as well as best practices, that Tact smart contract developers should be aware of" +--- + +[//]: # (✅❌) + +There are several anti-patterns and potential attack vectors that Tact smart contract developers should be aware of. These can affect the security, efficiency, and correctness of the contracts. Below we discuss the do's and don'ts specific to writing and maintaining secure Tact smart contracts. + +For a deeper understanding, refer to the following resources: + +* [Smart contracts guidelines in TON Docs](https://docs.ton.org/v3/guidelines/smart-contracts/guidelines) +* [Secure Smart Contract Programming in TON Docs](https://docs.ton.org/v3/guidelines/smart-contracts/security/secure-programming) +* [FunC Security Best Practices in GitHub repo](https://github.com/slowmist/Toncoin-Smart-Contract-Security-Best-Practices/blob/main/README.md) + +## Send sensitive data on-chain + +The entire smart contract computation is transparent, and if you had some confidential values at run-time, they could be retrieved with a simple emulation. + +##### Do's ✅ + +Do **not** send or store sensitive data on-chain. + +##### Don'ts ❌ + +```tact +import "@stdlib/deploy"; + +message Login { + privateKey: Int as uint256; + signature: Slice; + data: Slice; +} + +contract Test with Deployable { + receive(msg: Login) { + let publicKey = getPublicKey(msg.privateKey); + + require(checkDataSignature(msg.data, msg.signature, publicKey), "Invalid signature!"); + } +} +``` + +## Misuse of signed integers + +Unsigned integers are safer because they prevent most errors by design, while signed integers can have unpredictable consequences if not used carefully. Therefore, signed integers should be used only when absolutely necessary. + +##### Do's ✅ + +Prefer to use unsigned integers unless signed integers are required. + +##### Don'ts ❌ + +The following is an example of the incorrect use of a signed integer. In the `Vote{:tact}` [Message][message], the type of the `votes` field is `Int as int32{:tact}`, which is a $32$-bit singed integer. This can lead to spoofing if the attacker sends the negative number of votes instead of a positive one. + +```tact +message Vote { votes: Int as int32 } + +contract Sample { + votes: Int as uint32 = 0; + + receive(msg: Vote) { + self.votes += msg.votes; + } +} +``` + +## Invalid throw values + +[Exit codes](/book/exit-codes) $0$ and $1$ indicate normal execution of the compute phase of the transaction. Execution can be unexpectedly aborted by calling a [`throw(){:tact}`](/ref/core-debug#throw) or [similar functions](/ref/core-debug) directly with exit codes $0$ and $1$. This can make debugging very difficult since such aborted execution would be indistinguishable from a normal one. + +##### Do's ✅ + +Prefer to use the [`require(){:tact}`](/ref/core-debug#require) function to state expectations. + +```tact +require(isDataValid(msg.data), "Invalid data!"); +``` + +##### Don'ts ❌ + +Don't throw $0$ or $1$ directly. + +```tact +throw(0); +throw(1); +``` + +## Insecure random numbers + +Generating truly secure random numbers in TON is challenging. The [`random()`](/ref/core-random#random) function is pseudo-random and depends on [logical time](https://docs.ton.org/develop/smart-contracts/guidelines/message-delivery-guarantees#what-is-a-logical-time). A hacker can predict the randomized number by [brute-forcing](https://en.wikipedia.org/wiki/Brute-force_attack) the logical time in the current block. + +##### Do's ✅ + +* For critical applications **avoid relying solely on on-chain solutions**. + +* Use [`random(){:tact}`](/ref/core-random#random) with randomized logical time to enhance security by making predictions harder for attackers without access to a validator node. Note, however, that it is still **not entirely foolproof**. + +* Consider using the **commit-and-disclose scheme**: + 1. Participants generate random numbers off-chain and send their hashes to the contract. + 2. Once all hashes are received, participants disclose their original numbers. + 3. Combine the disclosed numbers (e.g., summing them) to produce a secure random value. + +For more details, refer to the [Secure Random number generation page in TON Docs](https://docs.ton.org/v3/guidelines/smart-contracts/security/random-number-generation). + +##### Don'ts ❌ + +Don't rely on the [`random(){:tact}`](/ref/core-random#random) function. + +```tact +if (random(1, 10) == 7) { + ... send reward ... +} +``` + +Don't use randomization in `external_message` receivers, as it remains vulnerable even with randomizing logical time. + +## Optimized message handling + +String parsing from human-friendly formats into machine-readable binary structures should be done **off-chain**. This approach ensures that only optimized and compact messages are sent to the blockchain, minimizing computational and storage costs while avoiding unnecessary gas overhead. + +##### Do's ✅ + +Perform string parsing from human-readable formats into machine-readable binary structures **off-chain** to keep the contract efficient. + +```tact +message Sample { + parsedField: Slice; +} + +contract Example { + receive(msg: Sample) { + // Process msg.parsedField directly + } +} +``` + +##### Don'ts ❌ + +Avoid parsing strings from human-readable formats into binary structures **on-chain**, as this increases computational overhead and gas costs. + +```tact +message Sample { field: String } + +contract Example { + receive(msg: Sample) { + // Parsing occurs on-chain, which is inefficient + let parsed = field.fromBase64(); + } +} +``` + +## Gas limitation + +Be careful with the `Out of gas error`. It cannot be handled, so try to pre-calculate the gas consumption for each receiver [using tests](/book/debug#tests) whenever possible. This will help to avoid wasting extra gas because the transaction will fail anyway. + +##### Do's ✅ + +```tact +message Vote { votes: Int as int32 } + +contract Example { + const voteGasUsage = 10000; // precompute with tests + + receive(msg: Vote) { + require(context().value > getComputeFee(self.voteGasUsage, false), "Not enough gas!"); + } +} +``` + +## Identity validation + +Always validate the identity of the sender if your contract logic revolves around trusted senders. This can be done using the [`Ownable{:tact}`](/ref/stdlib-ownable) trait or using [state init](/book/expressions#initof) validation. You can read more about [Jetton validation](/cookbook/jettons#accepting-jetton-transfer) and [NFT validation](/cookbook/nfts#accepting-nft-ownership-assignment). + +##### Do's ✅ + +Use the [`Ownable{:tact}`](/ref/stdlib-ownable) trait. + +```tact +import "@stdlib/ownable"; + +contract Counter with Ownable { + owner: Address; + val: Int as uint32; + + init() { + self.owner = address("OWNER_ADDRESS"); + self.val = 0; + } + + receive("admin-double") { + self.requireOwner(); + self.val = self.val * 2; + } +} +``` + +##### Don'ts ❌ + +Don't execute a message without validating the sender's identity! + +```tact +contract Example with Deployable { + myJettonWalletAddress: Address; + myJettonAmount: Int as coins = 0; + + init(jettonWalletCode: Cell, jettonMasterAddress: Address) { + self.myJettonWalletAddress = calculateJettonWalletAddress( + myAddress(), + jettonMasterAddress, + jettonWalletCode, + ); + } + + receive(msg: JettonTransferNotification) { + self.myJettonAmount += msg.amount; + } +} +``` + +## Replay protection + +Replay protection is a security mechanism that prevents an attacker from reusing a previous message. More information about replay protection can be found on the [External messages page in TON Docs](https://docs.ton.org/develop/smart-contracts/guidelines/external-messages). + +##### Do's ✅ + +To differentiate messages, always include and validate a unique identifier, such as `seqno`. Update the identifier after successful processing to avoid duplicates. + +Alternatively, you can implement a replay protection similar to the one in the [highload v3 wallet](https://github.com/ton-blockchain/highload-wallet-contract-v3/blob/main/contracts/highload-wallet-v3.func#L60), which is not based on `seqno`. + +```tact +message Msg { + newMessage: Cell; + signature: Slice; +} + +struct DataToVerify { + seqno: Int as uint64; + message: Cell; +} + +contract Sample { + publicKey: Int as uint256; + seqno: Int as uint64; + + init(publicKey: Int, seqno: Int) { + self.publicKey = publicKey; + self.seqno = seqno; + } + + external(msg: Msg) { + require(checkDataSignature(DataToVerify{ + seqno: self.seqno, + message: msg.newMessage + }.toSlice(), msg.signature, self.publicKey), "Invalid signature"); + acceptMessage(); + self.seqno += 1; + nativeSendMessage(msg.newMessage, 0); + } +} +``` + +##### Don'ts ❌ + +Do not rely on signature verification without the inclusion of a sequence number. Messages without replay protection can be resent by attackers because there is nothing to distinguish a valid original message from a replayed one. + +```tact +message Msg { + newMessage: Cell; + signature: Slice; +} + +contract Sample { + publicKey: Int as uint256; + + init(publicKey: Int, seqno: Int) { + self.publicKey = publicKey; + } + + external(msg: Msg) { + require(checkDataSignature(msg.toSlice(), msg.signature, self.publicKey), "Invalid signature"); + acceptMessage(); + nativeSendMessage(msg.newMessage, 0); + } +} +``` + +## Race condition of messages + +A message cascade can be processed over many blocks. Assume that while one message flow is running, an attacker can initiate a second message flow in parallel. That is, if a property was checked at the beginning, such as whether the user has enough tokens, do not assume that it will still be satisfied at the third stage in the same contract. + +## Handle/Send bounced messages + +Send messages with the bounce flag set to `true{:tact}`, which is a default for the [`send(){:tact}`](/ref/core-common#send) function. Messages bounce when the execution of a contract has failed. You may want to deal with this by rolling back the state of the contract by wrapping code in [`try...catch{:tact}`](/book/statements#try-catch) statements and some additional processing depending on your logic. + +##### Do's ✅ + +Handle bounced messages via a [bounced message receiver](/book/bounced/#bounced-message-receiver) to correctly react to failed messages. + +```tact +contract JettonDefaultWallet { + const minTonsForStorage: Int = ton("0.01"); + const gasConsumption: Int = ton("0.01"); + + balance: Int; + owner: Address; + master: Address; + + init(master: Address, owner: Address) { + self.balance = 0; + self.owner = owner; + self.master = master; + } + + receive(msg: TokenBurn) { + let ctx: Context = context(); + require(ctx.sender == self.owner, "Invalid sender"); + + self.balance = self.balance - msg.amount; + require(self.balance >= 0, "Invalid balance"); + + let fwdFee: Int = ctx.readForwardFee(); + require(ctx.value > fwdFee + 2 * self.gasConsumption + self.minTonsForStorage, "Invalid value - Burn"); + + send(SendParameters{ + to: self.master, + value: 0, + mode: SendRemainingValue, + bounce: true, + body: TokenBurnNotification{ + queryId: msg.queryId, + amount: msg.amount, + owner: self.owner, + response_destination: self.owner + }.toCell() + }); + } + + bounced(src: bounced) { + self.balance = self.balance + src.amount; + } +} +``` + +## Transaction and phases + +From [Sending messages page](/book/send#outbound-message-processing) of the Book: + +> Each transaction on TON Blockchain consists of multiple phases. Outbound messages are evaluated in compute phase, but are **not** sent in that phase. Instead, they're queued in order of appearance for the action phase, where all actions listed in compute phase, like outbound messages or reserve requests, are executed. + +Hence, if the compute phase fails, [registers](https://docs.ton.org/v3/documentation/tvm/tvm-overview#control-registers) `c4` (persistent data) and `c5` (actions) won't be updated. However, it is possible to manually save their state using [`commit(){:tact}`](/ref/core-advanced/#commit) function. + +## Return gas excesses carefully + +If excess gas is not returned to the sender, the funds will accumulate in your contracts over time. Nothing terrible in principle, just a suboptimal practice. You can add a function to rake out excess, but popular contracts like TON Jetton still return to the sender with the [Message][message] with `0xd53276db` opcode. + +##### Do's ✅ + +Return excesses using a [Message][message] with `0xd53276db` opcode. + +```tact +message(0xd53276db) Excesses {} +message Vote { votes: Int as int32 } + +contract Sample { + votes: Int as uint32 = 0; + + receive(msg: Vote) { + self.votes += msg.votes; + + send(SendParameters{ + to: sender(), + value: 0, + mode: SendRemainingValue | SendIgnoreErrors, + body: Excesses{}.toCell(), + }); + } +} +``` + +Also, you can leverage [`notify(){:tact}`](/ref/core-base/#self-notify) or [`forward(){:tact}`](/ref/core-base/#self-forward) standard functions. + +```tact +message(0xd53276db) Excesses {} +message Vote { votes: Int as int32 } + +contract Sample { + votes: Int as uint32 = 0; + + receive(msg: Vote) { + self.votes += msg.votes; + + self.notify(Excesses{}.toCell()); + } +} +``` + +## Pulling data from other contract + +Contracts in the blockchain can reside in separate shards, processed by other set of validators, meaning that one contract cannot pull data from other contracts. That is, no contract can call a [getter function](/book/functions#getter-functions)) from other contracts. + +Thus, any on-chain communication is asynchronous and done by sending and receiving messages. + +##### Do's ✅ + +Exchange messages to pull data from other contract. + +```tact +message ProvideMoney {} +message TakeMoney { money: Int as coins } + +contract OneContract { + money: Int as coins; + + init(money: Int) { + self.money = money; + } + + receive(msg: ProvideMoney) { + self.reply(TakeMoney{money: self.money}.toCell()); + } +} + +contract AnotherContract { + oneContractAddress: Address; + + init(oneContractAddress: Address) { + self.oneContractAddress = oneContractAddress; + } + + receive("get money") { + self.forward(self.oneContractAddress, ProvideMoney{}.toCell(), false, null); + } + + receive(msg: TakeMoney) { + require(sender() == self.oneContractAddress, "Invalid money provider!"); + // Money processing + } +} +``` + +[struct]: /book/structs-and-messages#structs +[message]: /book/structs-and-messages#messages diff --git a/docs/src/content/docs/ref/core-advanced.mdx b/docs/src/content/docs/ref/core-advanced.mdx index 8bbdcd726..25dba26cd 100644 --- a/docs/src/content/docs/ref/core-advanced.mdx +++ b/docs/src/content/docs/ref/core-advanced.mdx @@ -13,22 +13,269 @@ Various niche, dangerous or unstable features which can produce unexpected resul ::: +## gasConsumed + +

+ +```tact +fun gasConsumed(): Int; +``` + +Returns the [nanoToncoin][nanotoncoin] [`Int{:tact}`][int] amount of [gas][gas] consumed by [TVM][tvm] in the current transaction so far. The resulting value includes the cost of calling this function. + +Usage example: + +```tact +let gas: Int = gasConsumed(); +``` + +:::note[Useful links:] + + [Gas in TON Docs](https://docs.ton.org/v3/documentation/smart-contracts/transaction-fees/fees#gas) + +::: + +## myStorageDue + +

+ +```tact +fun myStorageDue(): Int; +``` + +Returns the [nanoToncoin][nanotoncoin] [`Int{:tact}`][int] amount of the accumulated [storage fee][storage-fee] debt. Storage fees are deducted from the incoming message value before the new contract balance is calculated. + +Usage example: + +```tact +let debt: Int = myStorageDue(); +``` + +:::note[Useful links:] + + [Storage fee in TON Docs][storage-fee]\ + [Storage fee calculation in TON Docs][storage-fee-calc] + +::: + +## getStorageFee + +

+ +```tact +fun getStorageFee(cells: Int, bits: Int, seconds: Int, isMasterchain: Bool): Int; +``` + +Calculates and returns the [storage fee][storage-fee] in [nanoToncoins][nanotoncoin] [`Int{:tact}`][int] for storing a contract with a given number of `cells` and `bits` for a number of `seconds`. Uses the prices of the [masterchain][masterchain] if `isMasterchain` is `true{:tact}`, otherwise the prices of the [basechain][basechain]. The current prices are obtained from the [config param 18 of TON Blockchain](https://docs.ton.org/develop/howto/blockchain-configs#param-18). + +Note, that the values of `cells` and `bits` are taken modulo their maximum values plus $1$. That is, specifying values higher than those listed in [account state limits (`max_acc_state_cells` and `max_acc_state_bits`)](/book/exit-codes#50) will have the same result as with specifying the exact limits. In addition, make sure you take into account the [deduplication of cells with the same hash][deduplication]. + +Attempts to specify negative number of `cells`, `bits` or `seconds` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`. + +Usage example: + +```tact +let fee: Int = getStorageFee(1_000, 1_000, 1_000, false); +// ----- ----- ----- ----- +// ↑ ↑ ↑ ↑ +// | | | Isn't on the masterchain, +// | | | but on the basechain +// | | Number of seconds to calculate +// | | the storage fee for +// | Number of bits in a contract +// Number of cells in a contract +``` + +:::note[Useful links:] + + [Storage fee in TON Docs][storage-fee]\ + [Storage fee calculation in TON Docs][storage-fee-calc] + +::: + +## getComputeFee + +

+ +```tact +fun getComputeFee(gasUsed: Int, isMasterchain: Bool): Int; +``` + +Calculates and returns the [compute fee][compute-fee] in [nanoToncoins][nanotoncoin] [`Int{:tact}`][int] for a transaction that consumed `gasUsed` amount of [gas][gas]. Uses the prices of the [masterchain][masterchain] if `isMasterchain` is `true{:tact}`, otherwise the prices of the [basechain][basechain]. The current prices are obtained from the [config param 20 for the masterchain and config param 21 for the basechain][param-20-21] of TON Blockchain. + +When the `gasUsed` is less than a certain threshold called [`flat_gas_limit`][param-20-21], there's a minimum price to pay based on the value of [`flat_gas_price`][param-20-21]. The less gas is used below this threshold, the higher the minimum price will be. See the example for [`getSimpleComputeFee(){:tact}`](#getsimplecomputefee) to derive that threshold. + +Attempts to specify negative value of `gasUsed` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`. + +Usage example: + +```tact +let fee: Int = getComputeFee(1_000, false); +// ----- ----- +// ↑ ↑ +// | Isn't on the masterchain, +// | but on the basechain +// Number of gas units +// consumed per transaction +``` + +:::note[Useful links:] + + [Compute fee in TON Docs][compute-fee]\ + [Compute fee calculation in TON Docs][compute-fee-calc]\ + [`getSimpleComputeFee(){:tact}`](#getsimplecomputefee) + +::: + +## getSimpleComputeFee + +

+ +```tact +fun getSimpleComputeFee(gasUsed: Int, isMasterchain: Bool): Int; +``` + +Similar to [`getComputeFee(){:tact}`](#getcomputefee), but without the [`flat_gas_price`][param-20-21], i.e. without a minimum price to pay if the `gasUsed` is less than a certain threshold called [`flat_gas_limit`][param-20-21]. Calculates and returns only the `gasUsed` times the current gas price. + +Attempts to specify negative value of `gasUsed` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`. + +Usage example: + +```tact +let fee = getComputeFee(0, false); +let feeNoFlat = getSimpleComputeFee(0, false); +let maxFlatPrice = fee - feeNoFlat; +``` + +:::note[Useful links:] + + [Compute fee in TON Docs][compute-fee]\ + [Compute fee calculation in TON Docs][compute-fee-calc]\ + [`getComputeFee(){:tact}`](#getcomputefee) + +::: + ## Context.readForwardFee ```tact -extends fun readForwardFee(self: Context): Int +extends fun readForwardFee(self: Context): Int; ``` Extension function for the [`Context{:tact}`](/ref/core-common#context). -Reads [forward fee](https://docs.ton.org/develop/smart-contracts/guidelines/processing) and returns it as [`Int{:tact}`][int] amount of [nanoToncoins](/book/integers#nanotoncoin). +Reads [forward fee](https://docs.ton.org/develop/smart-contracts/guidelines/processing) and returns it as [`Int{:tact}`][int] amount of [nanoToncoins][nanotoncoin]. + +Usage example: + +```tact +let fwdFee: Int = context().readForwardFee(); +``` + +:::note[Useful links:] + + [`getOriginalFwdFee(){:tact}`](#getoriginalfwdfee) + +::: + +## getForwardFee + +

+ +```tact +fun getForwardFee(cells: Int, bits: Int, isMasterchain: Bool): Int; +``` + +Calculates and returns the [forward fee][forward-fee] in [nanoToncoins][nanotoncoin] [`Int{:tact}`][int] for an outgoing message consisting of a given number of `cells` and `bits`. Uses the prices of the [masterchain][masterchain] if `isMasterchain` is `true{:tact}`, otherwise the prices of the [basechain][basechain]. The current prices are obtained from the [config param 24 for the masterchain and config param 25 for the basechain][param-24-25] of TON Blockchain. + +If both the source and the destination addresses are in the [basechain][basechain], then specify `isMasterchain` as `false{:tact}`. Otherwise, specify `true{:tact}`. + +Note, that the values of `cells` and `bits` are taken modulo their maximum values plus $1$. That is, specifying values higher than those listed in [account state limits (`max_msg_cells` and `max_msg_bits`)](/book/exit-codes#50) will have the same result as with specifying the exact limits. + +However, regardless of the values of `cells` and `bits`, this function always adds the minimum price based on the value of [`lump_price`][param-24-25]. See the example for [`getSimpleForwardFee(){:tact}`](#getsimpleforwardfee) to derive it. In addition, make sure you take into account the [deduplication of cells with the same hash][deduplication], since for example the root cell and its data bits don't count towards the forward fee and are covered by the [`lump_price`][param-24-25]. + +Attempts to specify negative number of `cells` or `bits` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`. + +Usage example: + +```tact +let fee: Int = getForwardFee(1_000, 1_000, false); +// ----- ----- ----- +// ↑ ↑ ↑ +// | | Both source and destination +// | | isn't on the masterchain, +// | | but on the basechain +// | Number of bits in a message +// Number of cells in a message +``` + +:::note[Useful links:] + + [Forward fee in TON Docs][forward-fee]\ + [Forward fee calculation in TON Docs][forward-fee-calc]\ + [`CDATASIZEQ` instruction for computing the number of distinct cells, data bits and refs in a `Cell{:tact}`](https://docs.ton.org/v3/documentation/tvm/instructions#F940)\ + [`getSimpleForwardFee(){:tact}`](#getsimpleforwardfee)\ + [`getOriginalFwdFee(){:tact}`](#getoriginalfwdfee) + +::: + +## getSimpleForwardFee + +

+ +```tact +fun getSimpleForwardFee(cells: Int, bits: Int, isMasterchain: Bool): Int; +``` + +Similar to [`getForwardFee(){:tact}`](#getforwardfee), but without the [`lump_price`][param-24-25], i.e. without the minimum price to pay regardless of the amount of `cells` or `bits`. Calculates and returns only the `cells` times the current cell price plus `bits` times the current bit price. + +Attempts to specify negative number of `cells` or `bits` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`. + +Usage example: + +```tact +let fee = getForwardFee(1_000, 1_000, false); +let feeNoLump = getSimpleForwardFee(1_000, 1_000, false); +let lumpPrice = fee - feeNoLump; +``` + +:::note[Useful links:] + + [Forward fee in TON Docs][forward-fee]\ + [Forward fee calculation in TON Docs][forward-fee-calc]\ + [`getForwardFee(){:tact}`](#getforwardfee) + +::: + +## getOriginalFwdFee + +

+ +```tact +fun getOriginalFwdFee(fwdFee: Int, isMasterchain: Bool): Int; +``` + +Calculates and returns the so-called _original_ [forward fee][forward-fee] in [nanoToncoins][nanotoncoin] [`Int{:tact}`][int] for an outgoing message based on the `fwdFee` obtained from the incoming message. If both the source and the destination addresses are in the [basechain][basechain], then specify `isMasterchain` as `false{:tact}`. Otherwise, specify `true{:tact}`. + +This function is useful when the outgoing message depends heavily on the structure of the incoming message, so much so that you cannot fully predict the fee using [`getForwardFee(){:tact}`](#getforwardfee) alone. Even if you could, calculating the exact fee with [nanoToncoin][nanotoncoin]-level precision can be very expensive, so the approximation given by this function is often good enough. + +Attempts to specify a negative value of `fwdFee` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`. Usage example: ```tact let fwdFee: Int = context().readForwardFee(); +let origFee: Int = getOriginalFwdFee(fee, false); ``` +:::note[Useful links:] + + [Forward fee in TON Docs][forward-fee]\ + [Forward fee calculation in TON Docs][forward-fee-calc]\ + [`getForwardFee(){:tact}`](#getforwardfee)\ + [`Context.readForwardFee(){:tact}`](#contextreadforwardfee) + +::: + ## getConfigParam ```tact @@ -199,7 +446,7 @@ fun nativeSendMessage(cell: Cell, mode: Int); fun nativeReserve(amount: Int, mode: Int); ``` -Calls native `raw_reserve` function with specified amount and mode. The `raw_reserve` is a function that creates an output action to reserve a specific amount of [nanoToncoins](/book/integers#nanotoncoin) from the remaining balance of the account. +Calls native `raw_reserve` function with specified amount and mode. The `raw_reserve` is a function that creates an output action to reserve a specific amount of [nanoToncoins][nanotoncoin] from the remaining balance of the account. It has the following signature in FunC: @@ -208,10 +455,10 @@ raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; ``` The function takes two arguments: -* `amount`: The number of [nanoToncoins](/book/integers#nanotoncoin) to reserve. +* `amount`: The number of [nanoToncoins][nanotoncoin] to reserve. * `mode`: Determines the reservation behavior. -Function `raw_reserve` is roughly equivalent to creating an outbound message carrying the specified `amount` of [nanoToncoins](/book/integers#nanotoncoin) (or `b` $-$ `amount` [nanoToncoins](/book/integers#nanotoncoin), where `b` is the remaining balance) to oneself. This ensures that subsequent output actions cannot spend more money than the remainder. +Function `raw_reserve` is roughly equivalent to creating an outbound message carrying the specified `amount` of [nanoToncoins][nanotoncoin] (or `b` $-$ `amount` [nanoToncoins][nanotoncoin], where `b` is the remaining balance) to oneself. This ensures that subsequent output actions cannot spend more money than the remainder. It's possible to use raw [`Int{:tact}`][int] values and manually provide them for the `mode`, but for your convenience there's a set of constants which you may use to construct the compound `mode` with ease. Take a look at the following tables for more information on base modes and optional flags. @@ -227,9 +474,9 @@ The resulting `mode` value can have the following base modes: Mode value | Constant name | Description ---------: | :---------------------------- | ----------- -$0$ | `ReserveExact{:tact}` | Reserves exactly the specified `amount` of [nanoToncoins](/book/integers#nanotoncoin). -$1$ | `ReserveAllExcept{:tact}` | Reserves all, but the specified `amount` of [nanoToncoins](/book/integers#nanotoncoin). -$2$ | `ReserveAtMost{:tact}` | Reserves at most the specified `amount` of [nanoToncoins](/book/integers#nanotoncoin). +$0$ | `ReserveExact{:tact}` | Reserves exactly the specified `amount` of [nanoToncoins][nanotoncoin]. +$1$ | `ReserveAllExcept{:tact}` | Reserves all, but the specified `amount` of [nanoToncoins][nanotoncoin]. +$2$ | `ReserveAtMost{:tact}` | Reserves at most the specified `amount` of [nanoToncoins][nanotoncoin]. ### Optional flags {#nativereserve-optional-flags} @@ -334,3 +581,22 @@ parsedVarAddr.address.loadUint(123); // 345 [int]: /book/integers [slice]: /book/cells#slices [s]: /book/structs-and-messages#structs +[masterchain]: /book/masterchain +[cell-hash]: /ref/core-cell#cellhash +[nanotoncoin]: /book/integers#nanotoncoin + +[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview +[basechain]: https://docs.ton.org/v3/concepts/ton-blockchain/smart-contract-addresses#address-components +[deduplication]: https://docs.ton.org/v3/documentation/data-formats/tlb/library-cells + +[storage-fee]: https://docs.ton.org/v3/documentation/smart-contracts/transaction-fees/fees-low-level#storage-fee +[storage-fee-calc]: https://docs.ton.org/v3/guidelines/smart-contracts/fee-calculation#storage-fee + +[gas]: https://docs.ton.org/v3/documentation/smart-contracts/transaction-fees/fees#gas +[compute-fee]: https://docs.ton.org/v3/documentation/smart-contracts/transaction-fees/fees-low-level#computation-fees +[compute-fee-calc]: https://docs.ton.org/v3/guidelines/smart-contracts/fee-calculation#computation-fee +[param-20-21]: https://docs.ton.org/v3/documentation/network/configs/blockchain-configs#param-20-and-21 + +[forward-fee]: https://docs.ton.org/v3/documentation/smart-contracts/transaction-fees/forward-fees +[forward-fee-calc]: https://docs.ton.org/v3/guidelines/smart-contracts/fee-calculation#forward-fee +[param-24-25]: https://docs.ton.org/v3/documentation/network/configs/blockchain-configs#param-24-and-25 diff --git a/docs/src/content/docs/ref/core-cells.mdx b/docs/src/content/docs/ref/core-cells.mdx index f29d38650..770130032 100644 --- a/docs/src/content/docs/ref/core-cells.mdx +++ b/docs/src/content/docs/ref/core-cells.mdx @@ -209,6 +209,24 @@ let fizz: Builder = b.storeBit(true); // writes 1 let buzz: Builder = b.storeBit(false); // writes 0 ``` +## Builder.storeBuilder + +```tact +extends fun storeBuilder(self: Builder, cell: Builder): Builder; +``` + +Extension function for the [`Builder{:tact}`][builder]. + +Appends all data from a [`Builder{:tact}`][builder] `cell` to the copy of the [`Builder{:tact}`][builder]. Returns that copy. + +Usage example: + +```tact +let b: Builder = beginCell().storeCoins(42); +let fizz: Builder = beginCell().storeBuilder(b); +b.endCell() == fizz.endCell(); // true +``` + ## Builder.storeSlice ```tact diff --git a/src/imports/stdlib.ts b/src/imports/stdlib.ts index a79113eed..f38b5ce7c 100644 --- a/src/imports/stdlib.ts +++ b/src/imports/stdlib.ts @@ -128,62 +128,69 @@ files['std/cells.tact'] = 'bnQsIGJpdHM6IEludCk6IEJ1aWxkZXI7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIlNUVVgi' + 'CkBuYW1lKHN0b3JlX3VpbnQpCmV4dGVuZHMgbmF0aXZlIHN0b3JlVWludChzZWxmOiBCdWlsZGVyLCB2YWx1ZTogSW50LCBiaXRzOiBJbnQpOiBCdWlsZGVyOwoKQG5h' + 'bWUoX190YWN0X3N0b3JlX2Jvb2wpCmV4dGVuZHMgbmF0aXZlIHN0b3JlQm9vbChzZWxmOiBCdWlsZGVyLCB2YWx1ZTogQm9vbCk6IEJ1aWxkZXI7CgovLy8gRXh0ZW5z' + - 'aW9uIGZ1bmN0aW9uIGZvciB0aGUgYEJ1aWxkZXJgLiBBbGlhcyB0byBgQnVpbGRlci5zdG9yZUJvb2woKWAuCi8vLwovLy8gYGBgdGFjdAovLy8gbGV0IGI6IEJ1aWxk' + - 'ZXIgPSBiZWdpbkNlbGwoKTsKLy8vIGxldCBmaXp6OiBCdWlsZGVyID0gYi5zdG9yZUJpdCh0cnVlKTsgIC8vIHdyaXRlcyAxCi8vLyBsZXQgYnV6ejogQnVpbGRlciA9' + - 'IGIuc3RvcmVCaXQoZmFsc2UpOyAvLyB3cml0ZXMgMAovLy8gYGBgCi8vLwovLy8gQHNpbmNlIFRhY3QgMS41LjAKLy8vIEBzZWUgaHR0cHM6Ly9kb2NzLnRhY3QtbGFu' + - 'Zy5vcmcvcmVmL2NvcmUtY2VsbHMjYnVpbGRlcnN0b3JlYml0Ci8vLwpAbmFtZShfX3RhY3Rfc3RvcmVfYm9vbCkKZXh0ZW5kcyBuYXRpdmUgc3RvcmVCaXQoc2VsZjog' + - 'QnVpbGRlciwgdmFsdWU6IEJvb2wpOiBCdWlsZGVyOwoKYXNtIGV4dGVuZHMgZnVuIHN0b3JlQ29pbnMoc2VsZjogQnVpbGRlciwgdmFsdWU6IEludCk6IEJ1aWxkZXIg' + - 'eyBTVFZBUlVJTlQxNiB9Cgphc20oY2VsbCBzZWxmKSBleHRlbmRzIGZ1biBzdG9yZVJlZihzZWxmOiBCdWlsZGVyLCBjZWxsOiBDZWxsKTogQnVpbGRlciB7IFNUUkVG' + - 'IH0KCmFzbSBleHRlbmRzIGZ1biBzdG9yZVNsaWNlKHNlbGY6IEJ1aWxkZXIsIGNlbGw6IFNsaWNlKTogQnVpbGRlciB7IFNUU0xJQ0VSIH0KCmFzbSBleHRlbmRzIGZ1' + - 'biBzdG9yZUJ1aWxkZXIoc2VsZjogQnVpbGRlciwgY2VsbDogQnVpbGRlcik6IEJ1aWxkZXIgeyBTVEJSIH0KCkBuYW1lKF9fdGFjdF9zdG9yZV9hZGRyZXNzKQpleHRl' + - 'bmRzIG5hdGl2ZSBzdG9yZUFkZHJlc3Moc2VsZjogQnVpbGRlciwgYWRkcmVzczogQWRkcmVzcyk6IEJ1aWxkZXI7CgovLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIGZvciB0' + - 'aGUgYEJ1aWxkZXJgLgovLy8KLy8vIElmIHRoZSBgY2VsbGAgaXMgbm90IGBudWxsYCwgc3RvcmVzIDEgYXMgYSBzaW5nbGUgYml0IGFuZCB0aGVuIHJlZmVyZW5jZSBg' + - 'Y2VsbGAgaW50byB0aGUgY29weSBvZiB0aGUgYEJ1aWxkZXJgLiBSZXR1cm5zIHRoYXQgY29weS4KLy8vCi8vLyBJZiB0aGUgYGNlbGxgIGlzIGBudWxsYCwgb25seSBz' + - 'dG9yZXMgMCBhcyBhIHNpbmdsZSBiaXQgaW50byB0aGUgY29weSBvZiB0aGUgYEJ1aWxkZXJgLiBSZXR1cm5zIHRoYXQgY29weS4KLy8vCi8vLyBBcyBhIHNpbmdsZSBg' + - 'Q2VsbGAgY2FuIHN0b3JlIHVwIHRvIDQgcmVmZXJlbmNlcywgYXR0ZW1wdHMgdG8gc3RvcmUgbW9yZSB0aHJvdyBhbiBleGNlcHRpb24gd2l0aCBleGl0IGNvZGUgODog' + - 'YENlbGwgb3ZlcmZsb3dgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGxldCBiOiBCdWlsZGVyID0gYmVnaW5DZWxsKCk7Ci8vLyBsZXQgZml6ejogQnVpbGRlciA9IGIKLy8v' + - 'ICAgICAuc3RvcmVNYXliZVJlZihlbXB0eUNlbGwoKSkgLy8gMSwgdGhlbiBlbXB0eSBjZWxsCi8vLyAgICAgLnN0b3JlTWF5YmVSZWYobnVsbCk7ICAgICAgIC8vIDAK' + - 'Ly8vIGBgYAovLy8KLy8vIEBzaW5jZSBUYWN0IDEuNS4wCi8vLyBAc2VlIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWNlbGxzI2J1aWxkZXJzdG9y' + - 'ZW1heWJlcmVmCi8vLwphc20oY2VsbCBzZWxmKSBleHRlbmRzIGZ1biBzdG9yZU1heWJlUmVmKHNlbGY6IEJ1aWxkZXIsIGNlbGw6IENlbGw/KTogQnVpbGRlciB7IFNU' + - 'T1BUUkVGIH0KCmFzbSBleHRlbmRzIGZ1biBlbmRDZWxsKHNlbGY6IEJ1aWxkZXIpOiBDZWxsIHsgRU5EQyB9Cgphc20gZXh0ZW5kcyBmdW4gcmVmcyhzZWxmOiBCdWls' + - 'ZGVyKTogSW50IHsgQlJFRlMgfQoKYXNtIGV4dGVuZHMgZnVuIGJpdHMoc2VsZjogQnVpbGRlcik6IEludCB7IEJCSVRTIH0KCi8vCi8vIFNsaWNlCi8vCgphc20gZXh0' + - 'ZW5kcyBmdW4gYmVnaW5QYXJzZShzZWxmOiBDZWxsKTogU2xpY2UgeyBDVE9TIH0KCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZFJlZihzZWxmOiBT' + - 'bGljZSk6IENlbGwgeyBMRFJFRiB9CgovLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41LjAuCi8vLwov' + - 'Ly8gUHJlbG9hZHMgdGhlIG5leHQgcmVmZXJlbmNlIGZyb20gdGhlIGBTbGljZWAgYXMgYSBgQ2VsbGAuIERvZXNuJ3QgbW9kaWZ5IHRoZSBvcmlnaW5hbCBgU2xpY2Vg' + - 'LgovLy8KLy8vIEF0dGVtcHRzIHRvIHByZWxvYWQgc3VjaCByZWZlcmVuY2UgYENlbGxgIHdoZW4gYFNsaWNlYCBkb2Vzbid0IGNvbnRhaW4gaXQgdGhyb3cgYW4gZXhj' + - 'ZXB0aW9uIHdpdGggZXhpdCBjb2RlIDg6IGBDZWxsIG92ZXJmbG93YC4KLy8vCi8vLyBBdHRlbXB0cyB0byBwcmVsb2FkIG1vcmUgZGF0YSB0aGFuIGBTbGljZWAgY29u' + - 'dGFpbnMgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDk6IGBDZWxsIHVuZGVyZmxvd2AuCi8vLwovLy8gYGBgdGFjdAovLy8gbGV0IHMxOiBTbGljZSA9' + - 'IGJlZ2luQ2VsbCgpLnN0b3JlUmVmKGVtcHR5Q2VsbCgpKS5hc1NsaWNlKCk7Ci8vLyBsZXQgZml6ejogQ2VsbCA9IHMxLnByZWxvYWRSZWYoKTsgLy8gZGlkbid0IG1v' + - 'ZGlmeSBzMQovLy8KLy8vIGxldCBzMjogU2xpY2UgPSBiZWdpbkNlbGwoKQovLy8gICAgIC5zdG9yZVJlZihlbXB0eUNlbGwoKSkKLy8vICAgICAuc3RvcmVSZWYoczEu' + - 'YXNDZWxsKCkpCi8vLyAgICAgLmFzU2xpY2UoKTsKLy8vIGxldCByZWYxOiBDZWxsID0gczIucHJlbG9hZFJlZigpOwovLy8gbGV0IHJlZjI6IENlbGwgPSBzMi5wcmVs' + - 'b2FkUmVmKCk7Ci8vLyByZWYxID09IHJlZjI7IC8vIHRydWUKLy8vIGBgYAovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2Nv' + - 'cmUtY2VsbHMjc2xpY2VwcmVsb2FkcmVmCi8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL2Jvb2svZXhpdC1jb2RlcwovLy8KYXNtIGV4dGVuZHMgZnVuIHBy' + - 'ZWxvYWRSZWYoc2VsZjogU2xpY2UpOiBDZWxsIHsgUExEUkVGIH0KCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3' + - 'aXRoIGFzbSAiTERTTElDRVgiCkBuYW1lKGxvYWRfYml0cykKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7' + - 'CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIlBMRFNMSUNFWCIKQG5hbWUocHJlbG9hZF9iaXRz' + - 'KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVy' + - 'LCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxESVgiCkBuYW1lKGxvYWRfaW50KQpleHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRJbnQoc2VsZjogU2xpY2UsIGw6' + - 'IEludCk6IEludDsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiUExESVgiCkBuYW1lKHByZWxv' + - 'YWRfaW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkSW50KHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBJbnQ7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBp' + - 'bGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxEVVgiCkBuYW1lKGxvYWRfdWludCkKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkVWludChzZWxmOiBTbGlj' + - 'ZSwgbDogSW50KTogSW50OwoKLy8gc3BlY2lhbCB0cmVhdG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28gbm90IHJlcGxhY2VkIHdpdGggYXNtICJQTERVWCIKQG5hbWUo' + - 'cHJlbG9hZF91aW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkVWludChzZWxmOiBTbGljZSwgbDogSW50KTogSW50OwoKYXNtKC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVz' + - 'IGZ1biBsb2FkQm9vbChzZWxmOiBTbGljZSk6IEJvb2wgeyAxIExESSB9CgovLy8gRXh0ZW5zaW9uIG11dGF0aW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQWxp' + - 'YXMgdG8gYFNsaWNlLmxvYWRCb29sKClgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGxldCBzOiBTbGljZSA9IGJlZ2luQ2VsbCgpLnN0b3JlQm9vbCh0cnVlKS5hc1NsaWNl' + - 'KCk7Ci8vLyBsZXQgZml6ejogQm9vbCA9IHMubG9hZEJpdCgpOyAvLyB0cnVlCi8vLyBgYGAKLy8vCi8vLyBAc2luY2UgVGFjdCAxLjUuMAovLy8gQHNlZSBodHRwczov' + - 'L2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1jZWxscyNzbGljZWxvYWRiaXQKLy8vCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZEJpdChzZWxm' + - 'OiBTbGljZSk6IEJvb2wgeyAxIExESSB9Cgphc20oIC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVzIGZ1biBsb2FkQ29pbnMoc2VsZjogU2xpY2UpOiBJbnQgeyBMRFZBUlVJ' + - 'TlQxNiB9CgpAbmFtZShfX3RhY3RfbG9hZF9hZGRyZXNzKQpleHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRBZGRyZXNzKHNlbGY6IFNsaWNlKTogQWRkcmVzczsKCmFz' + - 'bSBleHRlbmRzIG11dGF0ZXMgZnVuIHNraXBCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpIHsgU0RTS0lQRklSU1QgfQoKYXNtIGV4dGVuZHMgZnVuIGVuZFBhcnNlKHNl' + - 'bGY6IFNsaWNlKSB7IEVORFMgfQoKLy8KLy8gU2xpY2Ugc2l6ZQovLwoKYXNtIGV4dGVuZHMgZnVuIHJlZnMoc2VsZjogU2xpY2UpOiBJbnQgeyBTUkVGUyB9Cgphc20g' + - 'ZXh0ZW5kcyBmdW4gYml0cyhzZWxmOiBTbGljZSk6IEludCB7IFNCSVRTIH0KCmFzbSBleHRlbmRzIGZ1biBlbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTRU1QVFkg' + - 'fQoKYXNtIGV4dGVuZHMgZnVuIGRhdGFFbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTREVNUFRZIH0KCmFzbSBleHRlbmRzIGZ1biByZWZzRW1wdHkoc2VsZjogU2xp' + - 'Y2UpOiBCb29sIHsgU1JFTVBUWSB9CgovLwovLyBDb252ZXJzaW9ucwovLwoKaW5saW5lIGV4dGVuZHMgZnVuIGFzU2xpY2Uoc2VsZjogQnVpbGRlcik6IFNsaWNlIHsK' + - 'ICAgIHJldHVybiBzZWxmLmVuZENlbGwoKS5iZWdpblBhcnNlKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc1NsaWNlKHNlbGY6IENlbGwpOiBTbGljZSB7CiAgICBy' + - 'ZXR1cm4gc2VsZi5iZWdpblBhcnNlKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc0NlbGwoc2VsZjogU2xpY2UpOiBDZWxsIHsKICAgIHJldHVybiBiZWdpbkNlbGwo' + - 'KQogICAgICAgIC5zdG9yZVNsaWNlKHNlbGYpCiAgICAgICAgLmVuZENlbGwoKTsKfQoKaW5saW5lIGV4dGVuZHMgZnVuIGFzQ2VsbChzZWxmOiBCdWlsZGVyKTogQ2Vs' + - 'bCB7CiAgICByZXR1cm4gc2VsZi5lbmRDZWxsKCk7Cn0KCmlubGluZSBmdW4gZW1wdHlDZWxsKCk6IENlbGwgewogICAgcmV0dXJuIGJlZ2luQ2VsbCgpLmVuZENlbGwo' + - 'KTsKfQoKaW5saW5lIGZ1biBlbXB0eVNsaWNlKCk6IFNsaWNlIHsKICAgIHJldHVybiBlbXB0eUNlbGwoKS5hc1NsaWNlKCk7Cn0K'; + 'aW9uIGZ1bmN0aW9uIGZvciB0aGUgYEJ1aWxkZXJgLiBBbGlhcyB0byBgQnVpbGRlci5zdG9yZUJvb2woKWAuIEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNS4wLgovLy8K' + + 'Ly8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewovLy8gICAgIGxldCBiOiBCdWlsZGVyID0gYmVnaW5DZWxsKCk7Ci8vLyAgICAgbGV0IGZpeno6IEJ1aWxkZXIg' + + 'PSBiLnN0b3JlQml0KHRydWUpOyAgLy8gd3JpdGVzIDEKLy8vICAgICBsZXQgYnV6ejogQnVpbGRlciA9IGIuc3RvcmVCaXQoZmFsc2UpOyAvLyB3cml0ZXMgMAovLy8g' + + 'fQovLy8gYGBgCi8vLwovLy8gU2VlOiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1jZWxscyNidWlsZGVyc3RvcmViaXQKLy8vCkBuYW1lKF9fdGFj' + + 'dF9zdG9yZV9ib29sKQpleHRlbmRzIG5hdGl2ZSBzdG9yZUJpdChzZWxmOiBCdWlsZGVyLCB2YWx1ZTogQm9vbCk6IEJ1aWxkZXI7Cgphc20gZXh0ZW5kcyBmdW4gc3Rv' + + 'cmVDb2lucyhzZWxmOiBCdWlsZGVyLCB2YWx1ZTogSW50KTogQnVpbGRlciB7IFNUVkFSVUlOVDE2IH0KCmFzbShjZWxsIHNlbGYpIGV4dGVuZHMgZnVuIHN0b3JlUmVm' + + 'KHNlbGY6IEJ1aWxkZXIsIGNlbGw6IENlbGwpOiBCdWlsZGVyIHsgU1RSRUYgfQoKYXNtIGV4dGVuZHMgZnVuIHN0b3JlU2xpY2Uoc2VsZjogQnVpbGRlciwgY2VsbDog' + + 'U2xpY2UpOiBCdWlsZGVyIHsgU1RTTElDRVIgfQoKLy8vIEV4dGVuc2lvbiBmdW5jdGlvbiBmb3IgdGhlIGBCdWlsZGVyYC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41' + + 'LjAuCi8vLwovLy8gQXBwZW5kcyBhbGwgZGF0YSBmcm9tIGEgYEJ1aWxkZXJgIGBjZWxsYCB0byB0aGUgY29weSBvZiB0aGUgYEJ1aWxkZXJgLiBSZXR1cm5zIHRoYXQg' + + 'Y29weS4KLy8vCi8vLyBgYGB0YWN0Ci8vLyBmdW4gZXhhbXBsZSgpIHsKLy8vICAgICBsZXQgYjogQnVpbGRlciA9IGJlZ2luQ2VsbCgpLnN0b3JlQ29pbnMoNDIpOwov' + + 'Ly8gICAgIGxldCBmaXp6OiBCdWlsZGVyID0gYmVnaW5DZWxsKCkuc3RvcmVCdWlsZGVyKGIpOwovLy8gICAgIGIuZW5kQ2VsbCgpID09IGZpenouZW5kQ2VsbCgpOyAv' + + 'LyB0cnVlCi8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6IGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWNlbGxzI2J1aWxkZXJzdG9yZWJ1aWxkZXIK' + + 'Ly8vCmFzbSBleHRlbmRzIGZ1biBzdG9yZUJ1aWxkZXIoc2VsZjogQnVpbGRlciwgY2VsbDogQnVpbGRlcik6IEJ1aWxkZXIgeyBTVEJSIH0KCkBuYW1lKF9fdGFjdF9z' + + 'dG9yZV9hZGRyZXNzKQpleHRlbmRzIG5hdGl2ZSBzdG9yZUFkZHJlc3Moc2VsZjogQnVpbGRlciwgYWRkcmVzczogQWRkcmVzcyk6IEJ1aWxkZXI7CgovLy8gRXh0ZW5z' + + 'aW9uIGZ1bmN0aW9uIGZvciB0aGUgYEJ1aWxkZXJgLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBJZiB0aGUgYGNlbGxgIGlzIG5vdCBgbnVsbGAs' + + 'IHN0b3JlcyAxIGFzIGEgc2luZ2xlIGJpdCBhbmQgdGhlbiByZWZlcmVuY2UgYGNlbGxgIGludG8gdGhlIGNvcHkgb2YgdGhlIGBCdWlsZGVyYC4gUmV0dXJucyB0aGF0' + + 'IGNvcHkuCi8vLwovLy8gSWYgdGhlIGBjZWxsYCBpcyBgbnVsbGAsIG9ubHkgc3RvcmVzIDAgYXMgYSBzaW5nbGUgYml0IGludG8gdGhlIGNvcHkgb2YgdGhlIGBCdWls' + + 'ZGVyYC4gUmV0dXJucyB0aGF0IGNvcHkuCi8vLwovLy8gQXMgYSBzaW5nbGUgYENlbGxgIGNhbiBzdG9yZSB1cCB0byA0IHJlZmVyZW5jZXMsIGF0dGVtcHRzIHRvIHN0' + + 'b3JlIG1vcmUgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDg6IGBDZWxsIG92ZXJmbG93YC4KLy8vCi8vLyBgYGB0YWN0Ci8vLyBmdW4gZXhhbXBsZSgp' + + 'IHsKLy8vICAgICBsZXQgYjogQnVpbGRlciA9IGJlZ2luQ2VsbCgpOwovLy8gICAgIGxldCBmaXp6OiBCdWlsZGVyID0gYgovLy8gICAgICAgICAuc3RvcmVNYXliZVJl' + + 'ZihlbXB0eUNlbGwoKSkgLy8gMSwgdGhlbiBlbXB0eSBjZWxsCi8vLyAgICAgICAgIC5zdG9yZU1heWJlUmVmKG51bGwpOyAgICAgICAvLyAwCi8vLyB9Ci8vLyBgYGAK' + + 'Ly8vCi8vLyBTZWU6IGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWNlbGxzI2J1aWxkZXJzdG9yZW1heWJlcmVmCi8vLwphc20oY2VsbCBzZWxmKSBl' + + 'eHRlbmRzIGZ1biBzdG9yZU1heWJlUmVmKHNlbGY6IEJ1aWxkZXIsIGNlbGw6IENlbGw/KTogQnVpbGRlciB7IFNUT1BUUkVGIH0KCmFzbSBleHRlbmRzIGZ1biBlbmRD' + + 'ZWxsKHNlbGY6IEJ1aWxkZXIpOiBDZWxsIHsgRU5EQyB9Cgphc20gZXh0ZW5kcyBmdW4gcmVmcyhzZWxmOiBCdWlsZGVyKTogSW50IHsgQlJFRlMgfQoKYXNtIGV4dGVu' + + 'ZHMgZnVuIGJpdHMoc2VsZjogQnVpbGRlcik6IEludCB7IEJCSVRTIH0KCi8vCi8vIFNsaWNlCi8vCgphc20gZXh0ZW5kcyBmdW4gYmVnaW5QYXJzZShzZWxmOiBDZWxs' + + 'KTogU2xpY2UgeyBDVE9TIH0KCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZFJlZihzZWxmOiBTbGljZSk6IENlbGwgeyBMRFJFRiB9CgovLy8gRXh0' + + 'ZW5zaW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41LjAuCi8vLwovLy8gUHJlbG9hZHMgdGhlIG5leHQgcmVmZXJlbmNl' + + 'IGZyb20gdGhlIGBTbGljZWAgYXMgYSBgQ2VsbGAuIERvZXNuJ3QgbW9kaWZ5IHRoZSBvcmlnaW5hbCBgU2xpY2VgLgovLy8KLy8vIEF0dGVtcHRzIHRvIHByZWxvYWQg' + + 'c3VjaCByZWZlcmVuY2UgYENlbGxgIHdoZW4gYFNsaWNlYCBkb2Vzbid0IGNvbnRhaW4gaXQgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDg6IGBDZWxs' + + 'IG92ZXJmbG93YC4KLy8vCi8vLyBBdHRlbXB0cyB0byBwcmVsb2FkIG1vcmUgZGF0YSB0aGFuIGBTbGljZWAgY29udGFpbnMgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGgg' + + 'ZXhpdCBjb2RlIDk6IGBDZWxsIHVuZGVyZmxvd2AuCi8vLwovLy8gYGBgdGFjdAovLy8gZnVuIGV4YW1wbGVzKCkgewovLy8gICAgIGxldCBzMTogU2xpY2UgPSBiZWdp' + + 'bkNlbGwoKS5zdG9yZVJlZihlbXB0eUNlbGwoKSkuYXNTbGljZSgpOwovLy8gICAgIGxldCBmaXp6OiBDZWxsID0gczEucHJlbG9hZFJlZigpOyAvLyBkaWRuJ3QgbW9k' + + 'aWZ5IHMxCi8vLwovLy8gICAgIGxldCBzMjogU2xpY2UgPSBiZWdpbkNlbGwoKQovLy8gICAgICAgICAuc3RvcmVSZWYoZW1wdHlDZWxsKCkpCi8vLyAgICAgICAgIC5z' + + 'dG9yZVJlZihzMS5hc0NlbGwoKSkKLy8vICAgICAgICAgLmFzU2xpY2UoKTsKLy8vICAgICBsZXQgcmVmMTogQ2VsbCA9IHMyLnByZWxvYWRSZWYoKTsKLy8vICAgICBs' + + 'ZXQgcmVmMjogQ2VsbCA9IHMyLnByZWxvYWRSZWYoKTsKLy8vICAgICByZWYxID09IHJlZjI7IC8vIHRydWUKLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNlZToKLy8vICog' + + 'aHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtY2VsbHMjc2xpY2VwcmVsb2FkcmVmCi8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL2Jvb2sv' + + 'ZXhpdC1jb2RlcwovLy8KYXNtIGV4dGVuZHMgZnVuIHByZWxvYWRSZWYoc2VsZjogU2xpY2UpOiBDZWxsIHsgUExEUkVGIH0KCi8vIHNwZWNpYWwgdHJlYXRtZW50IGlu' + + 'IEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiTERTTElDRVgiCkBuYW1lKGxvYWRfYml0cykKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2Fk' + + 'Qml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBh' + + 'c20gIlBMRFNMSUNFWCIKQG5hbWUocHJlbG9hZF9iaXRzKQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7CgovLyBz' + + 'cGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxESVgiCkBuYW1lKGxvYWRfaW50KQpleHRlbmRzIG11dGF0' + + 'ZXMgbmF0aXZlIGxvYWRJbnQoc2VsZjogU2xpY2UsIGw6IEludCk6IEludDsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBs' + + 'YWNlZCB3aXRoIGFzbSAiUExESVgiCkBuYW1lKHByZWxvYWRfaW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkSW50KHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBJbnQ7Cgov' + + 'LyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxEVVgiCkBuYW1lKGxvYWRfdWludCkKZXh0ZW5kcyBt' + + 'dXRhdGVzIG5hdGl2ZSBsb2FkVWludChzZWxmOiBTbGljZSwgbDogSW50KTogSW50OwoKLy8gc3BlY2lhbCB0cmVhdG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28gbm90' + + 'IHJlcGxhY2VkIHdpdGggYXNtICJQTERVWCIKQG5hbWUocHJlbG9hZF91aW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkVWludChzZWxmOiBTbGljZSwgbDogSW50KTog' + + 'SW50OwoKYXNtKC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVzIGZ1biBsb2FkQm9vbChzZWxmOiBTbGljZSk6IEJvb2wgeyAxIExESSB9CgovLy8gRXh0ZW5zaW9uIG11dGF0' + + 'aW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQWxpYXMgdG8gYFNsaWNlLmxvYWRCb29sKClgLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBg' + + 'YGB0YWN0Ci8vLyBmdW4gZXhhbXBsZSgpIHsKLy8vICAgICBsZXQgczogU2xpY2UgPSBiZWdpbkNlbGwoKS5zdG9yZUJvb2wodHJ1ZSkuYXNTbGljZSgpOwovLy8gICAg' + + 'IGxldCBmaXp6OiBCb29sID0gcy5sb2FkQml0KCk7IC8vIHRydWUKLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNlZTogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVm' + + 'L2NvcmUtY2VsbHMjc2xpY2Vsb2FkYml0Ci8vLwphc20oLT4gMSAwKSBleHRlbmRzIG11dGF0ZXMgZnVuIGxvYWRCaXQoc2VsZjogU2xpY2UpOiBCb29sIHsgMSBMREkg' + + 'fQoKYXNtKCAtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZENvaW5zKHNlbGY6IFNsaWNlKTogSW50IHsgTERWQVJVSU5UMTYgfQoKQG5hbWUoX190YWN0X2xv' + + 'YWRfYWRkcmVzcykKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkQWRkcmVzcyhzZWxmOiBTbGljZSk6IEFkZHJlc3M7Cgphc20gZXh0ZW5kcyBtdXRhdGVzIGZ1biBz' + + 'a2lwQml0cyhzZWxmOiBTbGljZSwgbDogSW50KSB7IFNEU0tJUEZJUlNUIH0KCmFzbSBleHRlbmRzIGZ1biBlbmRQYXJzZShzZWxmOiBTbGljZSkgeyBFTkRTIH0KCi8v' + + 'Ci8vIFNsaWNlIHNpemUKLy8KCmFzbSBleHRlbmRzIGZ1biByZWZzKHNlbGY6IFNsaWNlKTogSW50IHsgU1JFRlMgfQoKYXNtIGV4dGVuZHMgZnVuIGJpdHMoc2VsZjog' + + 'U2xpY2UpOiBJbnQgeyBTQklUUyB9Cgphc20gZXh0ZW5kcyBmdW4gZW1wdHkoc2VsZjogU2xpY2UpOiBCb29sIHsgU0VNUFRZIH0KCmFzbSBleHRlbmRzIGZ1biBkYXRh' + + 'RW1wdHkoc2VsZjogU2xpY2UpOiBCb29sIHsgU0RFTVBUWSB9Cgphc20gZXh0ZW5kcyBmdW4gcmVmc0VtcHR5KHNlbGY6IFNsaWNlKTogQm9vbCB7IFNSRU1QVFkgfQoK' + + 'Ly8KLy8gQ29udmVyc2lvbnMKLy8KCmlubGluZSBleHRlbmRzIGZ1biBhc1NsaWNlKHNlbGY6IEJ1aWxkZXIpOiBTbGljZSB7CiAgICByZXR1cm4gc2VsZi5lbmRDZWxs' + + 'KCkuYmVnaW5QYXJzZSgpOwp9CgppbmxpbmUgZXh0ZW5kcyBmdW4gYXNTbGljZShzZWxmOiBDZWxsKTogU2xpY2UgewogICAgcmV0dXJuIHNlbGYuYmVnaW5QYXJzZSgp' + + 'Owp9CgppbmxpbmUgZXh0ZW5kcyBmdW4gYXNDZWxsKHNlbGY6IFNsaWNlKTogQ2VsbCB7CiAgICByZXR1cm4gYmVnaW5DZWxsKCkKICAgICAgICAuc3RvcmVTbGljZShz' + + 'ZWxmKQogICAgICAgIC5lbmRDZWxsKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc0NlbGwoc2VsZjogQnVpbGRlcik6IENlbGwgewogICAgcmV0dXJuIHNlbGYuZW5k' + + 'Q2VsbCgpOwp9CgppbmxpbmUgZnVuIGVtcHR5Q2VsbCgpOiBDZWxsIHsKICAgIHJldHVybiBiZWdpbkNlbGwoKS5lbmRDZWxsKCk7Cn0KCmlubGluZSBmdW4gZW1wdHlT' + + 'bGljZSgpOiBTbGljZSB7CiAgICByZXR1cm4gZW1wdHlDZWxsKCkuYXNTbGljZSgpOwp9Cg=='; files['std/config.tact'] = 'YXNtIGZ1biBnZXRDb25maWdQYXJhbShpZDogSW50KTogQ2VsbD8geyBDT05GSUdPUFRQQVJBTSB9Cg=='; files['std/context.tact'] = @@ -199,54 +206,121 @@ files['std/contract.tact'] = 'KHM6IFN0YXRlSW5pdCk6IEFkZHJlc3MgewogICAgcmV0dXJuIGNvbnRyYWN0QWRkcmVzc0V4dCgwLCBzLmNvZGUsIHMuZGF0YSk7Cn0KCkBuYW1lKF9fdGFjdF9hZGRy' + 'ZXNzX3RvX3NsaWNlKQpleHRlbmRzIG5hdGl2ZSBhc1NsaWNlKHNlbGY6IEFkZHJlc3MpOiBTbGljZTsKCkBuYW1lKF9fdGFjdF9jcmVhdGVfYWRkcmVzcykKbmF0aXZl' + 'IG5ld0FkZHJlc3MoY2hhaW46IEludCwgaGFzaDogSW50KTogQWRkcmVzczsKCmFzbSBmdW4gbXlBZGRyZXNzKCk6IEFkZHJlc3MgeyBNWUFERFIgfQoKYXNtIGZ1biBt' + - 'eUJhbGFuY2UoKTogSW50IHsgQkFMQU5DRSBGSVJTVCB9CgovLyBSZXR1cm5zIGdhcyBjb25zdW1lZCBieSBWTSBzbyBmYXIgKGluY2x1ZGluZyB0aGlzIGluc3RydWN0' + - 'aW9uKS4KYXNtIGZ1biBnYXNDb25zdW1lZCgpOiBJbnQgeyBHQVNDT05TVU1FRCB9CgovLyBDYWxjdWxhdGVzIGNvbXB1dGF0aW9uIGNvc3QgaW4gbmFub3RvbnMgZm9y' + - 'IHRyYW5zYWN0aW9uIHRoYXQgY29uc3VtZXMgYGdhc191c2VkYCBnYXMuCmFzbSBmdW4gZ2V0Q29tcHV0ZUZlZShnYXNfdXNlZDogSW50LCBpc19tYXN0ZXJjaGFpbjog' + - 'Qm9vbCk6IEludCB7IEdFVEdBU0ZFRSB9CgovLyBDYWxjdWxhdGVzIHN0b3JhZ2UgZmVlcyBpbiBuYW5vdG9ucyBmb3IgY29udHJhY3QgYmFzZWQgb24gY3VycmVudCBz' + - 'dG9yYWdlIHByaWNlcy4gYGNlbGxzYCBhbmQgYGJpdHNgIGFyZSB0aGUgc2l6ZSBvZiB0aGUgQWNjb3VudFN0YXRlICh3aXRoIGRlZHVwbGljYXRpb24sIGluY2x1ZGlu' + - 'ZyByb290IGNlbGwpLgphc20gZnVuIGdldFN0b3JhZ2VGZWUoY2VsbHM6IEludCwgYml0czogSW50LCBzZWNvbmRzOiBJbnQsIGlzX21hc3RlcmNoYWluOiBCb29sKTog' + - 'SW50IHsgR0VUU1RPUkFHRUZFRSB9CgovLyBDYWxjdWxhdGVzIGZvcndhcmQgZmVlcyBpbiBuYW5vdG9ucyBmb3Igb3V0Z29pbmcgbWVzc2FnZS4gYGlzX21hc3RlcmNo' + - 'YWluYCBpcyB0cnVlIGlmIHRoZSBzb3VyY2Ugb3IgdGhlIGRlc3RpbmF0aW9uIGlzIGluIG1hc3RlcmNoYWluLCBmYWxzZSBpZiBib3RoIGFyZSBpbiBiYXNlY2hhaW4u' + - 'IE5vdGUsIGBjZWxsc2AgYW5kIGBiaXRzYCBpbiBNZXNzYWdlIHNob3VsZCBiZSBjb3VudGVkIHdpdGggYWNjb3VudCBmb3IgZGVkdXBsaWNhdGlvbiBhbmQgcm9vdC1p' + - 'cy1ub3QtY291bnRlZCBydWxlcy4KYXNtIGZ1biBnZXRGb3J3YXJkRmVlKGNlbGxzOiBJbnQsIGJpdHM6IEludCwgaXNfbWFzdGVyY2hhaW46IEJvb2wpOiBJbnQgeyBH' + - 'RVRGT1JXQVJERkVFIH0KCi8vIENhbGN1bGF0ZXMgYWRkaXRpb25hbCBjb21wdXRhdGlvbiBjb3N0IGluIG5hbm90b25zIGZvciB0cmFuc2FjdGlvbiB0aGF0IGNvbnN1' + - 'bWVzIGFkZGl0aW9uYWwgYGdhc191c2VkYC4gSW4gb3RoZXIgd29yZHMsIHNhbWUgYXMgYGdldENvbXB1dGVGZWVgLCBidXQgd2l0aG91dCBmbGF0IHByaWNlIChqdXN0' + - 'IGAoZ2FzX3VzZWQgKiBwcmljZSkgLyAyXjE2YCkuCmFzbSBmdW4gZ2V0U2ltcGxlQ29tcHV0ZUZlZShnYXNfdXNlZDogSW50LCBpc19tYXN0ZXJjaGFpbjogQm9vbCk6' + - 'IEludCB7IEdFVEdBU0ZFRVNJTVBMRSB9CgovLyBDYWxjdWxhdGVzIGFkZGl0aW9uYWwgZm9yd2FyZCBjb3N0IGluIG5hbm90b25zIGZvciBtZXNzYWdlIHRoYXQgY29u' + - 'dGFpbnMgYWRkaXRpb25hbCBgY2VsbHNgIGFuZCBgYml0c2AuIEluIG90aGVyIHdvcmRzLCBzYW1lIGFzIGBnZXRGb3J3YXJkRmVlYCwgYnV0IHdpdGhvdXQgbHVtcCBw' + - 'cmljZSAoanVzdCBgKGJpdHMqYml0X3ByaWNlICsgY2VsbHMqY2VsbF9wcmljZSkgLyAyXjE2YCkuCmFzbSBmdW4gZ2V0U2ltcGxlRm9yd2FyZEZlZShjZWxsczogSW50' + - 'LCBiaXRzOiBJbnQsIGlzX21hc3RlcmNoYWluOiBCb29sKTogSW50IHsgR0VURk9SV0FSREZFRVNJTVBMRSB9CgovLyBDYWxjdWxhdGUgYGZ3ZF9mZWUgKiAyXjE2IC8g' + - 'Zmlyc3RfZnJhY2AuIENhbiBiZSB1c2VkIHRvIGdldCB0aGUgb3JpZ2luYWwgYGZ3ZF9mZWVgIG9mIHRoZSBtZXNzYWdlIChhcyByZXBsYWNlbWVudCBmb3IgaGFyZGNv' + - 'ZGVkIHZhbHVlcykgZnJvbSBgZndkX2ZlZWAgcGFyc2VkIGZyb20gaW5jb21pbmcgbWVzc2FnZS4gYGlzX21hc3RlcmNoYWluYCBpcyB0cnVlIGlmIHRoZSBzb3VyY2Ug' + - 'b3IgdGhlIGRlc3RpbmF0aW9uIGlzIGluIG1hc3RlcmNoYWluLCBmYWxzZSBpZiBib3RoIGFyZSBpbiBiYXNlY2hhaW4uCmFzbSBmdW4gZ2V0T3JpZ2luYWxGd2RGZWUo' + - 'ZndkX2ZlZTogSW50LCBpc19tYXN0ZXJjaGFpbjogQm9vbCk6IEludCB7IEdFVE9SSUdJTkFMRldERkVFIH0KCi8vIEN1cnJlbnQgZGVidCBmb3Igc3RvcmFnZSBmZWUg' + - 'aW4gbmFub3RvbnMuCmFzbSBmdW4gbXlTdG9yYWdlRHVlKCk6IEludCB7IERVRVBBWU1FTlQgfQoKLy8vIFN0cnVjdCByZXByZXNlbnRpbmcgdGhlIHN0YW5kYXJkIGFk' + - 'ZHJlc3Mgb24gVE9OIEJsb2NrY2hhaW4gd2l0aCBzaWduZWQgOC1iaXQgYHdvcmtjaGFpbmAgSUQgYW5kIGFuIHVuc2lnbmVkIDI1Ni1iaXQgYGFkZHJlc3NgIGluIHRo' + - 'ZSBzcGVjaWZpZWQgYHdvcmtjaGFpbmAuCi8vLwovLy8gQXQgdGhlIG1vbWVudCwgb25seSBgd29ya2NoYWluYCBJRHMgdXNlZCBvbiBUT04gYXJlIDAgb2YgdGhlIGJh' + - 'c2VjaGFpbiBhbmQgLTEgb2YgdGhlIG1hc3RlcmNoYWluLgovLy8KLy8vIEBzaW5jZSBUYWN0IDEuNS4wCi8vLyBAc2VlIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3Jn' + - 'L3JlZi9jb3JlLWFkdmFuY2VkI3BhcnNlc3RkYWRkcmVzcwovLy8gQHNlZSBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJsb2NrY2hhaW4vdG9uL2Jsb2IvbWFzdGVyL2Ny' + - 'eXB0by9ibG9jay9ibG9jay50bGIjTDEwNS1MMTA2Ci8vLwpzdHJ1Y3QgU3RkQWRkcmVzcyB7CiAgICB3b3JrY2hhaW46IEludCBhcyBpbnQ4OwogICAgYWRkcmVzczog' + - 'SW50IGFzIHVpbnQyNTY7Cn0KCi8vLyBTdHJ1Y3QgcmVwcmVzZW50aW5nIHRoZSBhZGRyZXNzIG9mIHZhcmlhYmxlIGxlbmd0aCB3aXRoIHNpZ25lZCAzMi1iaXQgYHdv' + - 'cmtjaGFpbmAgSUQgYW5kIGEgYFNsaWNlYCBjb250YWluaW5nIHVuc2lnbmVkIGBhZGRyZXNzYCBpbiB0aGUgc3BlY2lmaWVkIGB3b3JrY2hhaW5gLgovLy8KLy8vIFZh' + - 'cmlhYmxlLWxlbmd0aCBhZGRyZXNzZXMgYXJlIGludGVuZGVkIGZvciBmdXR1cmUgZXh0ZW5zaW9ucywgYW5kIHdoaWxlIHZhbGlkYXRvcnMgbXVzdCBiZSByZWFkeSB0' + - 'byBhY2NlcHQgdGhlbSBpbiBpbmJvdW5kIG1lc3NhZ2VzLCB0aGUgc3RhbmRhcmQgKG5vbi12YXJpYWJsZSkgYWRkcmVzc2VzIGFyZSB1c2VkIHdoZW5ldmVyIHBvc3Np' + - 'YmxlLgovLy8KLy8vIEBzaW5jZSBUYWN0IDEuNS4wCi8vLyBAc2VlIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI3BhcnNldmFyYWRk' + - 'cmVzcwovLy8gQHNlZSBodHRwczovL2dpdGh1Yi5jb20vdG9uLWJsb2NrY2hhaW4vdG9uL2Jsb2IvbWFzdGVyL2NyeXB0by9ibG9jay9ibG9jay50bGIjTDEwNy1MMTA4' + - 'Ci8vLwpzdHJ1Y3QgVmFyQWRkcmVzcyB7CiAgICB3b3JrY2hhaW46IEludCBhcyBpbnQzMjsKICAgIGFkZHJlc3M6IFNsaWNlOwp9CgovLy8gQ29udmVydHMgYSBgU2xp' + - 'Y2VgIGNvbnRhaW5pbmcgYW4gYWRkcmVzcyBpbnRvIHRoZSBgU3RkQWRkcmVzc2AgU3RydWN0IGFuZCByZXR1cm5zIGl0LgovLy8KLy8vIGBgYHRhY3QKLy8vIGxldCBh' + - 'ZGRyID0gYWRkcmVzcygiRVFEdEZwRXdjRkFFY1JlNW1MVmgyTjZDMHgtX2hKRU03VzYxX0pMblNGNzRwNHEyIik7Ci8vLyBsZXQgcGFyc2VkQWRkciA9IHBhcnNlU3Rk' + - 'QWRkcmVzcyhhZGRyLmFzU2xpY2UoKSk7Ci8vLwovLy8gcGFyc2VkQWRkci53b3JrY2hhaW47IC8vIDAKLy8vIHBhcnNlZEFkZHIuYWRkcmVzczsgICAvLyAxMDcuLi4y' + - 'ODcKLy8vIGBgYAovLy8KLy8vIEBzaW5jZSBUYWN0IDEuNS4wCi8vLyBAc2VlIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI3BhcnNl' + - 'c3RkYWRkcmVzcwovLy8KYXNtIGZ1biBwYXJzZVN0ZEFkZHJlc3Moc2xpY2U6IFNsaWNlKTogU3RkQWRkcmVzcyB7IFJFV1JJVEVTVERBRERSIH0KCi8vLyBDb252ZXJ0' + - 'cyBhIGBTbGljZWAgY29udGFpbmluZyBhbiBhZGRyZXNzIG9mIHZhcmlhYmxlIGxlbmd0aCBpbnRvIHRoZSBgVmFyQWRkcmVzc2AgU3RydWN0IGFuZCByZXR1cm5zIGl0' + - 'LgovLy8KLy8vIGBgYHRhY3QKLy8vIGxldCB2YXJBZGRyU2xpY2UgPSBiZWdpbkNlbGwoKQovLy8gICAgIC5zdG9yZVVpbnQoNiwgMykgICAgIC8vIHRvIHJlY29nbml6' + - 'ZSB0aGUgZm9sbG93aW5nIGFzIGEgVmFyQWRkcmVzcwovLy8gICAgIC5zdG9yZVVpbnQoMTIzLCA5KSAgIC8vIG1ha2UgYWRkcmVzcyBvY2N1cHkgMTIzIGJpdHMKLy8v' + - 'ICAgICAuc3RvcmVVaW50KDIzNCwgMzIpICAvLyBzcGVjaWZ5IHdvcmtjaGFpbiBJRCBvZiAyMzQKLy8vICAgICAuc3RvcmVVaW50KDM0NSwgMTIzKSAvLyBzcGVjaWZ5' + - 'IGFkZHJlc3Mgb2YgMzQ1Ci8vLyAgICAgLmFzU2xpY2UoKTsKLy8vIGxldCBwYXJzZWRWYXJBZGRyID0gcGFyc2VWYXJBZGRyZXNzKHZhckFkZHJTbGljZSk7Ci8vLwov' + - 'Ly8gcGFyc2VkVmFyQWRkci53b3JrY2hhaW47ICAgICAgICAgICAgIC8vIDIzNAovLy8gcGFyc2VkVmFyQWRkci5hZGRyZXNzOyAgICAgICAgICAgICAgIC8vIENTe0Nl' + - 'bGx7MDAyLi4uMmIzfSBiaXRzOiA0NC4uMTY3OyByZWZzOiAwLi4wfQovLy8gcGFyc2VkVmFyQWRkci5hZGRyZXNzLmxvYWRVaW50KDEyMyk7IC8vIDM0NQovLy8gYGBg' + - 'Ci8vLwovLy8gQHNpbmNlIFRhY3QgMS41LjAKLy8vIEBzZWUgaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjcGFyc2V2YXJhZGRyZXNz' + - 'Ci8vLwphc20gZnVuIHBhcnNlVmFyQWRkcmVzcyhzbGljZTogU2xpY2UpOiBWYXJBZGRyZXNzIHsgUkVXUklURVZBUkFERFIgfQo='; + 'eUJhbGFuY2UoKTogSW50IHsgQkFMQU5DRSBGSVJTVCB9CgovLy8gR2xvYmFsIGZ1bmN0aW9uLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBSZXR1' + + 'cm5zIHRoZSBuYW5vVG9uY29pbiBgSW50YCBhbW91bnQgb2YgZ2FzIGNvbnN1bWVkIGJ5IFRWTSBpbiB0aGUgY3VycmVudCB0cmFuc2FjdGlvbiBzbyBmYXIuIFRoZSBy' + + 'ZXN1bHRpbmcgdmFsdWUgaW5jbHVkZXMgdGhlIGNvc3Qgb2YgY2FsbGluZyB0aGlzIGZ1bmN0aW9uLgovLy8KLy8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewov' + + 'Ly8gICAgIGxldCBnYXM6IEludCA9IGdhc0NvbnN1bWVkKCk7Ci8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6IGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9j' + + 'b3JlLWFkdmFuY2VkI2dhc2NvbnN1bWVkCi8vLwphc20gZnVuIGdhc0NvbnN1bWVkKCk6IEludCB7IEdBU0NPTlNVTUVEIH0KCi8vLyBHbG9iYWwgZnVuY3Rpb24uIEF2' + + 'YWlsYWJsZSBzaW5jZSBUYWN0IDEuNS4wLgovLy8KLy8vIFJldHVybnMgdGhlIG5hbm9Ub25jb2luIGBJbnRgIGFtb3VudCBvZiB0aGUgYWNjdW11bGF0ZWQgc3RvcmFn' + + 'ZSBmZWUgZGVidC4gU3RvcmFnZSBmZWVzIGFyZSBkZWR1Y3RlZCBmcm9tIHRoZSBpbmNvbWluZyBtZXNzYWdlIHZhbHVlIGJlZm9yZSB0aGUgbmV3IGNvbnRyYWN0IGJh' + + 'bGFuY2UgaXMgY2FsY3VsYXRlZC4KLy8vCi8vLyBgYGB0YWN0Ci8vLyBmdW4gZXhhbXBsZSgpIHsKLy8vICAgICBsZXQgZGVidDogSW50ID0gbXlTdG9yYWdlRHVlKCk7' + + 'Ci8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6IGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI215c3RvcmFnZWR1ZQovLy8KYXNtIGZ1' + + 'biBteVN0b3JhZ2VEdWUoKTogSW50IHsgRFVFUEFZTUVOVCB9CgovLy8gR2xvYmFsIGZ1bmN0aW9uLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBD' + + 'YWxjdWxhdGVzIGFuZCByZXR1cm5zIHRoZSBzdG9yYWdlIGZlZSBpbiBuYW5vVG9uY29pbnMgYEludGAgZm9yIHN0b3JpbmcgYSBjb250cmFjdCB3aXRoIGEgZ2l2ZW4g' + + 'bnVtYmVyIG9mIGBjZWxsc2AgYW5kIGBiaXRzYCBmb3IgYSBudW1iZXIgb2YgYHNlY29uZHNgLiBVc2VzIHRoZSBwcmljZXMgb2YgdGhlIG1hc3RlcmNoYWluIGlmIGBp' + + 'c01hc3RlcmNoYWluYCBpcyBgdHJ1ZWAsIG90aGVyd2lzZSB0aGUgcHJpY2VzIG9mIHRoZSBiYXNlY2hhaW4uIFRoZSBjdXJyZW50IHByaWNlcyBhcmUgb2J0YWluZWQg' + + 'ZnJvbSB0aGUgY29uZmlnIHBhcmFtIDE4IG9mIFRPTiBCbG9ja2NoYWluLgovLy8KLy8vIE5vdGUsIHRoYXQgdGhlIHZhbHVlcyBvZiBgY2VsbHNgIGFuZCBgYml0c2Ag' + + 'YXJlIHRha2VuIG1vZHVsbyB0aGVpciBtYXhpbXVtIHZhbHVlcyBwbHVzIDEuIFRoYXQgaXMsIHNwZWNpZnlpbmcgdmFsdWVzIGhpZ2hlciB0aGFuIHRob3NlIGxpc3Rl' + + 'ZCBpbiBhY2NvdW50IHN0YXRlIGxpbWl0cyAoYG1heF9hY2Nfc3RhdGVfY2VsbHNgIGFuZCBgbWF4X2FjY19zdGF0ZV9iaXRzYCkgd2lsbCBoYXZlIHRoZSBzYW1lIHJl' + + 'c3VsdCBhcyB3aXRoIHNwZWNpZnlpbmcgdGhlIGV4YWN0IGxpbWl0cy4gSW4gYWRkaXRpb24sIG1ha2Ugc3VyZSB5b3UgdGFrZSBpbnRvIGFjY291bnQgdGhlIGRlZHVw' + + 'bGljYXRpb24gb2YgY2VsbHMgd2l0aCB0aGUgc2FtZSBoYXNoLgovLy8KLy8vIEF0dGVtcHRzIHRvIHNwZWNpZnkgbmVnYXRpdmUgbnVtYmVyIG9mIGBjZWxsc2AsIGBi' + + 'aXRzYCBvciBgc2Vjb25kc2AgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDU6IGBJbnRlZ2VyIG91dCBvZiBleHBlY3RlZCByYW5nZWAuCi8vLwovLy8g' + + 'YGBgdGFjdAovLy8gZnVuIGV4YW1wbGUoKSB7Ci8vLyAgICAgbGV0IGZlZTogSW50ID0gZ2V0U3RvcmFnZUZlZSgxXzAwMCwgMV8wMDAsIDFfMDAwLCBmYWxzZSk7Ci8v' + + 'LyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI2dldHN0b3JhZ2VmZWUKLy8vICog' + + 'aHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjZ2V0c2ltcGxlc3RvcmFnZWZlZQovLy8KYXNtIGZ1biBnZXRTdG9yYWdlRmVlKGNlbGxz' + + 'OiBJbnQsIGJpdHM6IEludCwgc2Vjb25kczogSW50LCBpc01hc3RlcmNoYWluOiBCb29sKTogSW50IHsgR0VUU1RPUkFHRUZFRSB9CgovLy8gR2xvYmFsIGZ1bmN0aW9u' + + 'LiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBDYWxjdWxhdGVzIGFuZCByZXR1cm5zIHRoZSBjb21wdXRlIGZlZSBpbiBuYW5vVG9uY29pbnMgYElu' + + 'dGAgZm9yIGEgdHJhbnNhY3Rpb24gdGhhdCBjb25zdW1lZCBgZ2FzVXNlZGAgYW1vdW50IG9mIGdhcy4gVXNlcyB0aGUgcHJpY2VzIG9mIHRoZSBtYXN0ZXJjaGFpbiBp' + + 'ZiBgaXNNYXN0ZXJjaGFpbmAgaXMgYHRydWVgLCBvdGhlcndpc2UgdGhlIHByaWNlcyBvZiB0aGUgYmFzZWNoYWluLiBUaGUgY3VycmVudCBwcmljZXMgYXJlIG9idGFp' + + 'bmVkIGZyb20gdGhlIGNvbmZpZyBwYXJhbSAyMCBmb3IgdGhlIG1hc3RlcmNoYWluIGFuZCBjb25maWcgcGFyYW0gMjEgZm9yIHRoZSBiYXNlY2hhaW4gb2YgVE9OIEJs' + + 'b2NrY2hhaW4uCi8vLwovLy8gV2hlbiB0aGUgYGdhc1VzZWRgIGlzIGxlc3MgdGhhbiBhIGNlcnRhaW4gdGhyZXNob2xkIGNhbGxlZCBgZmxhdF9nYXNfbGltaXRgLCB0' + + 'aGVyZSdzIGEgbWluaW11bSBwcmljZSB0byBwYXkgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBmbGF0X2dhc19wcmljZWAuIFRoZSBsZXNzIGdhcyBpcyB1c2VkIGJlbG93' + + 'IHRoaXMgdGhyZXNob2xkLCB0aGUgaGlnaGVyIHRoZSBtaW5pbXVtIHByaWNlIHdpbGwgYmUuIFNlZSB0aGUgZXhhbXBsZSBmb3IgYGdldFNpbXBsZUNvbXB1dGVGZWUo' + + 'KWAgdG8gZGVyaXZlIHRoYXQgdGhyZXNob2xkLgovLy8KLy8vIEF0dGVtcHRzIHRvIHNwZWNpZnkgbmVnYXRpdmUgdmFsdWUgb2YgYGdhc1VzZWRgIHRocm93IGFuIGV4' + + 'Y2VwdGlvbiB3aXRoIGV4aXQgY29kZSA1OiBgSW50ZWdlciBvdXQgb2YgZXhwZWN0ZWQgcmFuZ2VgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewov' + + 'Ly8gICAgIGxldCBmZWU6IEludCA9IGdldENvbXB1dGVGZWUoMV8wMDAsIGZhbHNlKTsKLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNlZTogaHR0cHM6Ly9kb2NzLnRhY3Qt' + + 'bGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjZ2V0Y29tcHV0ZWZlZQovLy8KYXNtIGZ1biBnZXRDb21wdXRlRmVlKGdhc1VzZWQ6IEludCwgaXNNYXN0ZXJjaGFpbjog' + + 'Qm9vbCk6IEludCB7IEdFVEdBU0ZFRSB9CgovLy8gR2xvYmFsIGZ1bmN0aW9uLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBTaW1pbGFyIHRvIGBn' + + 'ZXRDb21wdXRlRmVlKClgLCBidXQgd2l0aG91dCB0aGUgYGZsYXRfZ2FzX3ByaWNlYCwgaS5lLiB3aXRob3V0IGEgbWluaW11bSBwcmljZSB0byBwYXkgaWYgdGhlIGBn' + + 'YXNVc2VkYCBpcyBsZXNzIHRoYW4gYSBjZXJ0YWluIHRocmVzaG9sZCBjYWxsZWQgYGZsYXRfZ2FzX2xpbWl0YC4gQ2FsY3VsYXRlcyBhbmQgcmV0dXJucyBvbmx5IHRo' + + 'ZSBgZ2FzVXNlZGAgdGltZXMgdGhlIGN1cnJlbnQgZ2FzIHByaWNlLgovLy8KLy8vIEF0dGVtcHRzIHRvIHNwZWNpZnkgbmVnYXRpdmUgbnVtYmVyIG9mIGBjZWxsc2As' + + 'IGBiaXRzYCBvciBgc2Vjb25kc2AgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDU6IGBJbnRlZ2VyIG91dCBvZiBleHBlY3RlZCByYW5nZWAuCi8vLwov' + + 'Ly8gYGBgdGFjdAovLy8gZnVuIGV4YW1wbGUoKSB7Ci8vLyAgICAgbGV0IGZlZSA9IGdldENvbXB1dGVGZWUoMCwgZmFsc2UpOwovLy8gICAgIGxldCBmZWVOb0ZsYXQg' + + 'PSBnZXRTaW1wbGVDb21wdXRlRmVlKDAsIGZhbHNlKTsKLy8vICAgICBsZXQgbWF4RmxhdFByaWNlID0gZmVlIC0gZmVlTm9GbGF0OwovLy8gfQovLy8gYGBgCi8vLwov' + + 'Ly8gU2VlOgovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1hZHZhbmNlZCNnZXRzaW1wbGVzdG9yYWdlZmVlCi8vLyAqIGh0dHBzOi8vZG9j' + + 'cy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI2dldHN0b3JhZ2VmZWUKLy8vCmFzbSBmdW4gZ2V0U2ltcGxlQ29tcHV0ZUZlZShnYXNVc2VkOiBJbnQsIGlz' + + 'TWFzdGVyY2hhaW46IEJvb2wpOiBJbnQgeyBHRVRHQVNGRUVTSU1QTEUgfQoKLy8vIEdsb2JhbCBmdW5jdGlvbi4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41LjAuCi8v' + + 'LwovLy8gQ2FsY3VsYXRlcyBhbmQgcmV0dXJucyB0aGUgZm9yd2FyZCBmZWUgaW4gbmFub1RvbmNvaW5zIGBJbnRgIGZvciBhbiBvdXRnb2luZyBtZXNzYWdlIGNvbnNp' + + 'c3Rpbmcgb2YgYSBnaXZlbiBudW1iZXIgb2YgYGNlbGxzYCBhbmQgYGJpdHNgLiBVc2VzIHRoZSBwcmljZXMgb2YgdGhlIG1hc3RlcmNoYWluIGlmIGBpc01hc3RlcmNo' + + 'YWluYCBpcyBgdHJ1ZXs6dGFjdH1gLCBvdGhlcndpc2UgdGhlIHByaWNlcyBvZiB0aGUgYmFzZWNoYWluLiBUaGUgY3VycmVudCBwcmljZXMgYXJlIG9idGFpbmVkIGZy' + + 'b20gdGhlIGNvbmZpZyBwYXJhbSAyNCBmb3IgdGhlIG1hc3RlcmNoYWluIGFuZCBjb25maWcgcGFyYW0gMjUgZm9yIHRoZSBiYXNlY2hhaW4gb2YgVE9OIEJsb2NrY2hh' + + 'aW4uCi8vLwovLy8gSWYgYm90aCB0aGUgc291cmNlIGFuZCB0aGUgZGVzdGluYXRpb24gYWRkcmVzc2VzIGFyZSBpbiB0aGUgYmFzZWNoYWluLCB0aGVuIHNwZWNpZnkg' + + 'YGlzTWFzdGVyY2hhaW5gIGFzIGBmYWxzZWAuIE90aGVyd2lzZSwgc3BlY2lmeSBgdHJ1ZWAuCi8vLwovLy8gTm90ZSwgdGhhdCB0aGUgdmFsdWVzIG9mIGBjZWxsc2Ag' + + 'YW5kIGBiaXRzYCBhcmUgdGFrZW4gbW9kdWxvIHRoZWlyIG1heGltdW0gdmFsdWVzIHBsdXMgMS4gVGhhdCBpcywgc3BlY2lmeWluZyB2YWx1ZXMgaGlnaGVyIHRoYW4g' + + 'dGhvc2UgbGlzdGVkIGluIGFjY291bnQgc3RhdGUgbGltaXRzIChgbWF4X21zZ19jZWxsc2AgYW5kIGBtYXhfbXNnX2JpdHNgKSB3aWxsIGhhdmUgdGhlIHNhbWUgcmVz' + + 'dWx0IGFzIHdpdGggc3BlY2lmeWluZyB0aGUgZXhhY3QgbGltaXRzLgovLy8KLy8vIEhvd2V2ZXIsIHJlZ2FyZGxlc3Mgb2YgdGhlIHZhbHVlcyBvZiBgY2VsbHNgIGFu' + + 'ZCBgYml0c2AsIHRoaXMgZnVuY3Rpb24gYWx3YXlzIGFkZHMgdGhlIG1pbmltdW0gcHJpY2UgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBsdW1wX3ByaWNlYC4gU2VlIHRo' + + 'ZSBleGFtcGxlIGZvciBbYGdldFNpbXBsZUZvcndhcmRGZWUoKXs6dGFjdH1gXSgjZ2V0c2ltcGxlZm9yd2FyZGZlZSkgdG8gZGVyaXZlIGl0LiBJbiBhZGRpdGlvbiwg' + + 'bWFrZSBzdXJlIHlvdSB0YWtlIGludG8gYWNjb3VudCB0aGUgZGVkdXBsaWNhdGlvbiBvZiBjZWxscyB3aXRoIHRoZSBzYW1lIGhhc2gsIHNpbmNlIGZvciBleGFtcGxl' + + 'IHRoZSByb290IGNlbGwgYW5kIGl0cyBkYXRhIGJpdHMgZG9uJ3QgY291bnQgdG93YXJkcyB0aGUgZm9yd2FyZCBmZWUgYW5kIGFyZSBjb3ZlcmVkIGJ5IHRoZSBgbHVt' + + 'cF9wcmljZWAuCi8vLwovLy8gQXR0ZW1wdHMgdG8gc3BlY2lmeSBuZWdhdGl2ZSBudW1iZXIgb2YgYGNlbGxzYCBvciBgYml0c2AgdGhyb3cgYW4gZXhjZXB0aW9uIHdp' + + 'dGggZXhpdCBjb2RlIDU6IGBJbnRlZ2VyIG91dCBvZiBleHBlY3RlZCByYW5nZWAuCi8vLwovLy8gYGBgdGFjdAovLy8gZnVuIGV4YW1wbGUoKSB7Ci8vLyAgICAgbGV0' + + 'IGZlZTogSW50ID0gZ2V0Rm9yd2FyZEZlZSgxXzAwMCwgMV8wMDAsIGZhbHNlKTsKLy8vIH0KLy8vIGBgYAovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRh' + + 'Y3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjZ2V0Zm9yd2FyZGZlZQovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1hZHZhbmNlZCNn' + + 'ZXRzaW1wbGVmb3J3YXJkZmVlCi8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI2dldG9yaWdpbmFsZndkZmVlCi8vLwphc20g' + + 'ZnVuIGdldEZvcndhcmRGZWUoY2VsbHM6IEludCwgYml0czogSW50LCBpc01hc3RlcmNoYWluOiBCb29sKTogSW50IHsgR0VURk9SV0FSREZFRSB9CgovLy8gR2xvYmFs' + + 'IGZ1bmN0aW9uLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUuMC4KLy8vCi8vLyBTaW1pbGFyIHRvIGBnZXRGb3J3YXJkRmVlKClgLCBidXQgd2l0aG91dCB0aGUgYGx1' + + 'bXBfcHJpY2VgLCBpLmUuIHdpdGhvdXQgdGhlIG1pbmltdW0gcHJpY2UgdG8gcGF5IHJlZ2FyZGxlc3Mgb2YgdGhlIGFtb3VudCBvZiBgY2VsbHNgIG9yIGBiaXRzYC4g' + + 'Q2FsY3VsYXRlcyBhbmQgcmV0dXJucyBvbmx5IHRoZSBgY2VsbHNgIHRpbWVzIHRoZSBjdXJyZW50IGNlbGwgcHJpY2UgcGx1cyBgYml0c2AgdGltZXMgdGhlIGN1cnJl' + + 'bnQgYml0IHByaWNlLgovLy8KLy8vIEF0dGVtcHRzIHRvIHNwZWNpZnkgbmVnYXRpdmUgbnVtYmVyIG9mIGBjZWxsc2Agb3IgYGJpdHNgIHRocm93IGFuIGV4Y2VwdGlv' + + 'biB3aXRoIGV4aXQgY29kZSA1OiBgSW50ZWdlciBvdXQgb2YgZXhwZWN0ZWQgcmFuZ2VgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewovLy8gICAg' + + 'IGxldCBmZWUgPSBnZXRGb3J3YXJkRmVlKDFfMDAwLCAxXzAwMCwgZmFsc2UpOwovLy8gICAgIGxldCBmZWVOb0x1bXAgPSBnZXRTaW1wbGVGb3J3YXJkRmVlKDFfMDAw' + + 'LCAxXzAwMCwgZmFsc2UpOwovLy8gICAgIGxldCBsdW1wUHJpY2UgPSBmZWUgLSBmZWVOb0x1bXA7Ci8vLyB9Ci8vLyBgYGAKLy8vCi8vLyBTZWU6Ci8vLyAqIGh0dHBz' + + 'Oi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWFkdmFuY2VkI2dldHNpbXBsZWZvcndhcmRmZWUKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVm' + + 'L2NvcmUtYWR2YW5jZWQjZ2V0Zm9yd2FyZGZlZQovLy8KYXNtIGZ1biBnZXRTaW1wbGVGb3J3YXJkRmVlKGNlbGxzOiBJbnQsIGJpdHM6IEludCwgaXNNYXN0ZXJjaGFp' + + 'bjogQm9vbCk6IEludCB7IEdFVEZPUldBUkRGRUVTSU1QTEUgfQoKLy8vIEdsb2JhbCBmdW5jdGlvbi4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41LjAuCi8vLwovLy8g' + + 'Q2FsY3VsYXRlcyBhbmQgcmV0dXJucyB0aGUgc28tY2FsbGVkIF9vcmlnaW5hbF8gZm9yd2FyZCBmZWUgaW4gbmFub1RvbmNvaW5zIGBJbnRgIGZvciBhbiBvdXRnb2lu' + + 'ZyBtZXNzYWdlIGJhc2VkIG9uIHRoZSBgZndkRmVlYCBvYnRhaW5lZCBmcm9tIHRoZSBpbmNvbWluZyBtZXNzYWdlLiBJZiBib3RoIHRoZSBzb3VyY2UgYW5kIHRoZSBk' + + 'ZXN0aW5hdGlvbiBhZGRyZXNzZXMgYXJlIGluIHRoZSBiYXNlY2hhaW4sIHRoZW4gc3BlY2lmeSBgaXNNYXN0ZXJjaGFpbmAgYXMgYGZhbHNlYC4gT3RoZXJ3aXNlLCBz' + + 'cGVjaWZ5IGB0cnVlYC4KLy8vCi8vLyBUaGlzIGZ1bmN0aW9uIGlzIHVzZWZ1bCB3aGVuIHRoZSBvdXRnb2luZyBtZXNzYWdlIGRlcGVuZHMgaGVhdmlseSBvbiB0aGUg' + + 'c3RydWN0dXJlIG9mIHRoZSBpbmNvbWluZyBtZXNzYWdlLCBzbyBtdWNoIHNvIHRoYXQgeW91IGNhbm5vdCBmdWxseSBwcmVkaWN0IHRoZSBmZWUgdXNpbmcgYGdldEZv' + + 'cndhcmRGZWUoKWAgYWxvbmUuIEV2ZW4gaWYgeW91IGNvdWxkLCBjYWxjdWxhdGluZyB0aGUgZXhhY3QgZmVlIHdpdGggbmFub1RvbmNvaW4tbGV2ZWwgcHJlY2lzaW9u' + + 'IGNhbiBiZSB2ZXJ5IGV4cGVuc2l2ZSwgc28gdGhlIGFwcHJveGltYXRlIHZhbHVlIGdpdmVuIGJ5IHRoaXMgZnVuY3Rpb24gaXMgb2Z0ZW4gZ29vZCBlbm91Z2guCi8v' + + 'LwovLy8gQXR0ZW1wdHMgdG8gc3BlY2lmeSBhIG5lZ2F0aXZlIHZhbHVlIG9mIGBmd2RGZWVgIHRocm93IGFuIGV4Y2VwdGlvbiB3aXRoIGV4aXQgY29kZSA1OiBgSW50' + + 'ZWdlciBvdXQgb2YgZXhwZWN0ZWQgcmFuZ2VgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewovLy8gICAgIGxldCBmd2RGZWU6IEludCA9IGNvbnRl' + + 'eHQoKS5yZWFkRm9yd2FyZEZlZSgpOwovLy8gICAgIGxldCBvcmlnRmVlOiBJbnQgPSBnZXRPcmlnaW5hbEZ3ZEZlZShmZWUsIGZhbHNlKTsKLy8vIH0KLy8vIGBgYAov' + + 'Ly8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjZ2V0b3JpZ2luYWxmd2RmZWUKLy8vICogaHR0cHM6Ly9k' + + 'b2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjZ2V0Zm9yd2FyZGZlZQovLy8gKiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1hZHZh' + + 'bmNlZCNjb250ZXh0cmVhZGZvcndhcmRmZWUKLy8vCmFzbSBmdW4gZ2V0T3JpZ2luYWxGd2RGZWUoZndkRmVlOiBJbnQsIGlzTWFzdGVyY2hhaW46IEJvb2wpOiBJbnQg' + + 'eyBHRVRPUklHSU5BTEZXREZFRSB9CgovLy8gU3RydWN0IHJlcHJlc2VudGluZyB0aGUgc3RhbmRhcmQgYWRkcmVzcyBvbiBUT04gQmxvY2tjaGFpbiB3aXRoIHNpZ25l' + + 'ZCA4LWJpdCBgd29ya2NoYWluYCBJRCBhbmQgYW4gdW5zaWduZWQgMjU2LWJpdCBgYWRkcmVzc2AgaW4gdGhlIHNwZWNpZmllZCBgd29ya2NoYWluYC4gQXZhaWxhYmxl' + + 'IHNpbmNlIFRhY3QgMS41LjAuCi8vLwovLy8gQXQgdGhlIG1vbWVudCwgb25seSBgd29ya2NoYWluYCBJRHMgdXNlZCBvbiBUT04gYXJlIDAgb2YgdGhlIGJhc2VjaGFp' + + 'biBhbmQgLTEgb2YgdGhlIG1hc3RlcmNoYWluLgovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjcGFy' + + 'c2VzdGRhZGRyZXNzCi8vLyAqIGh0dHBzOi8vZ2l0aHViLmNvbS90b24tYmxvY2tjaGFpbi90b24vYmxvYi9tYXN0ZXIvY3J5cHRvL2Jsb2NrL2Jsb2NrLnRsYiNMMTA1' + + 'LUwxMDYKLy8vCnN0cnVjdCBTdGRBZGRyZXNzIHsKICAgIHdvcmtjaGFpbjogSW50IGFzIGludDg7CiAgICBhZGRyZXNzOiBJbnQgYXMgdWludDI1NjsKfQoKLy8vIFN0' + + 'cnVjdCByZXByZXNlbnRpbmcgdGhlIGFkZHJlc3Mgb2YgdmFyaWFibGUgbGVuZ3RoIHdpdGggc2lnbmVkIDMyLWJpdCBgd29ya2NoYWluYCBJRCBhbmQgYSBgU2xpY2Vg' + + 'IGNvbnRhaW5pbmcgdW5zaWduZWQgYGFkZHJlc3NgIGluIHRoZSBzcGVjaWZpZWQgYHdvcmtjaGFpbmAuIEF2YWlsYWJsZSBzaW5jZSBUYWN0IDEuNS4wLgovLy8KLy8v' + + 'IFZhcmlhYmxlLWxlbmd0aCBhZGRyZXNzZXMgYXJlIGludGVuZGVkIGZvciBmdXR1cmUgZXh0ZW5zaW9ucywgYW5kIHdoaWxlIHZhbGlkYXRvcnMgbXVzdCBiZSByZWFk' + + 'eSB0byBhY2NlcHQgdGhlbSBpbiBpbmJvdW5kIG1lc3NhZ2VzLCB0aGUgc3RhbmRhcmQgKG5vbi12YXJpYWJsZSkgYWRkcmVzc2VzIGFyZSB1c2VkIHdoZW5ldmVyIHBv' + + 'c3NpYmxlLgovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjcGFyc2V2YXJhZGRyZXNzCi8vLyAqIGh0' + + 'dHBzOi8vZ2l0aHViLmNvbS90b24tYmxvY2tjaGFpbi90b24vYmxvYi9tYXN0ZXIvY3J5cHRvL2Jsb2NrL2Jsb2NrLnRsYiNMMTA3LUwxMDgKLy8vCnN0cnVjdCBWYXJB' + + 'ZGRyZXNzIHsKICAgIHdvcmtjaGFpbjogSW50IGFzIGludDMyOwogICAgYWRkcmVzczogU2xpY2U7Cn0KCi8vLyBBc3NlbWJseSBmdW5jdGlvbi4gQXZhaWxhYmxlIHNp' + + 'bmNlIFRhY3QgMS41LjAuCi8vLwovLy8gQ29udmVydHMgYSBgU2xpY2VgIGNvbnRhaW5pbmcgYW4gYWRkcmVzcyBpbnRvIHRoZSBgU3RkQWRkcmVzc2AgU3RydWN0IGFu' + + 'ZCByZXR1cm5zIGl0LgovLy8KLy8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewovLy8gICAgIGxldCBhZGRyID0gYWRkcmVzcygiRVFEdEZwRXdjRkFFY1JlNW1M' + + 'VmgyTjZDMHgtX2hKRU03VzYxX0pMblNGNzRwNHEyIik7Ci8vLyAgICAgbGV0IHBhcnNlZEFkZHIgPSBwYXJzZVN0ZEFkZHJlc3MoYWRkci5hc1NsaWNlKCkpOwovLy8K' + + 'Ly8vICAgICBwYXJzZWRBZGRyLndvcmtjaGFpbjsgLy8gMAovLy8gICAgIHBhcnNlZEFkZHIuYWRkcmVzczsgICAvLyAxMDcuLi4yODcKLy8vIH0KLy8vIGBgYAovLy8K' + + 'Ly8vIFNlZTogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2NvcmUtYWR2YW5jZWQjcGFyc2VzdGRhZGRyZXNzCi8vLwphc20gZnVuIHBhcnNlU3RkQWRkcmVz' + + 'cyhzbGljZTogU2xpY2UpOiBTdGRBZGRyZXNzIHsgUkVXUklURVNUREFERFIgfQoKLy8vIEFzc2VtYmx5IGZ1bmN0aW9uLiBBdmFpbGFibGUgc2luY2UgVGFjdCAxLjUu' + + 'MC4KLy8vCi8vLyBDb252ZXJ0cyBhIGBTbGljZWAgY29udGFpbmluZyBhbiBhZGRyZXNzIG9mIHZhcmlhYmxlIGxlbmd0aCBpbnRvIHRoZSBgVmFyQWRkcmVzc2AgU3Ry' + + 'dWN0IGFuZCByZXR1cm5zIGl0LgovLy8KLy8vIGBgYHRhY3QKLy8vIGZ1biBleGFtcGxlKCkgewovLy8gICAgIGxldCB2YXJBZGRyU2xpY2UgPSBiZWdpbkNlbGwoKQov' + + 'Ly8gICAgICAgICAuc3RvcmVVaW50KDYsIDMpICAgICAvLyB0byByZWNvZ25pemUgdGhlIGZvbGxvd2luZyBhcyBhIFZhckFkZHJlc3MKLy8vICAgICAgICAgLnN0b3Jl' + + 'VWludCgxMjMsIDkpICAgLy8gbWFrZSBhZGRyZXNzIG9jY3VweSAxMjMgYml0cwovLy8gICAgICAgICAuc3RvcmVVaW50KDIzNCwgMzIpICAvLyBzcGVjaWZ5IHdvcmtj' + + 'aGFpbiBJRCBvZiAyMzQKLy8vICAgICAgICAgLnN0b3JlVWludCgzNDUsIDEyMykgLy8gc3BlY2lmeSBhZGRyZXNzIG9mIDM0NQovLy8gICAgICAgICAuYXNTbGljZSgp' + + 'OwovLy8gICAgIGxldCBwYXJzZWRWYXJBZGRyID0gcGFyc2VWYXJBZGRyZXNzKHZhckFkZHJTbGljZSk7Ci8vLwovLy8gICAgIHBhcnNlZFZhckFkZHIud29ya2NoYWlu' + + 'OyAgICAgICAgICAgICAvLyAyMzQKLy8vICAgICBwYXJzZWRWYXJBZGRyLmFkZHJlc3M7ICAgICAgICAgICAgICAgLy8gQ1N7Q2VsbHswMDIuLi4yYjN9IGJpdHM6IDQ0' + + 'Li4xNjc7IHJlZnM6IDAuLjB9Ci8vLyAgICAgcGFyc2VkVmFyQWRkci5hZGRyZXNzLmxvYWRVaW50KDEyMyk7IC8vIDM0NQovLy8gfQovLy8gYGBgCi8vLwovLy8gU2Vl' + + 'OiBodHRwczovL2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1hZHZhbmNlZCNwYXJzZXZhcmFkZHJlc3MKLy8vCmFzbSBmdW4gcGFyc2VWYXJBZGRyZXNzKHNsaWNl' + + 'OiBTbGljZSk6IFZhckFkZHJlc3MgeyBSRVdSSVRFVkFSQUREUiB9Cg=='; files['std/crypto.tact'] = 'YXNtIGV4dGVuZHMgZnVuIGhhc2goc2VsZjogQ2VsbCk6IEludCB7IEhBU0hDVSB9Cgphc20gZXh0ZW5kcyBmdW4gaGFzaChzZWxmOiBTbGljZSk6IEludCB7IEhBU0hT' + 'VSB9Cgphc20gZnVuIGNoZWNrU2lnbmF0dXJlKGhhc2g6IEludCwgc2lnbmF0dXJlOiBTbGljZSwgcHVibGljX2tleTogSW50KTogQm9vbCB7IENIS1NJR05VIH0KCmFz' + diff --git a/stdlib/std/cells.tact b/stdlib/std/cells.tact index 2bb474d1a..99c5b7443 100644 --- a/stdlib/std/cells.tact +++ b/stdlib/std/cells.tact @@ -15,16 +15,17 @@ extends native storeUint(self: Builder, value: Int, bits: Int): Builder; @name(__tact_store_bool) extends native storeBool(self: Builder, value: Bool): Builder; -/// Extension function for the `Builder`. Alias to `Builder.storeBool()`. +/// Extension function for the `Builder`. Alias to `Builder.storeBool()`. Available since Tact 1.5.0. /// /// ```tact -/// let b: Builder = beginCell(); -/// let fizz: Builder = b.storeBit(true); // writes 1 -/// let buzz: Builder = b.storeBit(false); // writes 0 +/// fun example() { +/// let b: Builder = beginCell(); +/// let fizz: Builder = b.storeBit(true); // writes 1 +/// let buzz: Builder = b.storeBit(false); // writes 0 +/// } /// ``` /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-cells#builderstorebit +/// See: https://docs.tact-lang.org/ref/core-cells#builderstorebit /// @name(__tact_store_bool) extends native storeBit(self: Builder, value: Bool): Builder; @@ -35,12 +36,26 @@ asm(cell self) extends fun storeRef(self: Builder, cell: Cell): Builder { STREF asm extends fun storeSlice(self: Builder, cell: Slice): Builder { STSLICER } +/// Extension function for the `Builder`. Available since Tact 1.5.0. +/// +/// Appends all data from a `Builder` `cell` to the copy of the `Builder`. Returns that copy. +/// +/// ```tact +/// fun example() { +/// let b: Builder = beginCell().storeCoins(42); +/// let fizz: Builder = beginCell().storeBuilder(b); +/// b.endCell() == fizz.endCell(); // true +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/core-cells#builderstorebuilder +/// asm extends fun storeBuilder(self: Builder, cell: Builder): Builder { STBR } @name(__tact_store_address) extends native storeAddress(self: Builder, address: Address): Builder; -/// Extension function for the `Builder`. +/// Extension function for the `Builder`. Available since Tact 1.5.0. /// /// If the `cell` is not `null`, stores 1 as a single bit and then reference `cell` into the copy of the `Builder`. Returns that copy. /// @@ -49,14 +64,15 @@ extends native storeAddress(self: Builder, address: Address): Builder; /// As a single `Cell` can store up to 4 references, attempts to store more throw an exception with exit code 8: `Cell overflow`. /// /// ```tact -/// let b: Builder = beginCell(); -/// let fizz: Builder = b -/// .storeMaybeRef(emptyCell()) // 1, then empty cell -/// .storeMaybeRef(null); // 0 +/// fun example() { +/// let b: Builder = beginCell(); +/// let fizz: Builder = b +/// .storeMaybeRef(emptyCell()) // 1, then empty cell +/// .storeMaybeRef(null); // 0 +/// } /// ``` /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-cells#builderstoremayberef +/// See: https://docs.tact-lang.org/ref/core-cells#builderstoremayberef /// asm(cell self) extends fun storeMaybeRef(self: Builder, cell: Cell?): Builder { STOPTREF } @@ -83,16 +99,18 @@ asm(-> 1 0) extends mutates fun loadRef(self: Slice): Cell { LDREF } /// Attempts to preload more data than `Slice` contains throw an exception with exit code 9: `Cell underflow`. /// /// ```tact -/// let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); -/// let fizz: Cell = s1.preloadRef(); // didn't modify s1 -/// -/// let s2: Slice = beginCell() -/// .storeRef(emptyCell()) -/// .storeRef(s1.asCell()) -/// .asSlice(); -/// let ref1: Cell = s2.preloadRef(); -/// let ref2: Cell = s2.preloadRef(); -/// ref1 == ref2; // true +/// fun examples() { +/// let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +/// let fizz: Cell = s1.preloadRef(); // didn't modify s1 +/// +/// let s2: Slice = beginCell() +/// .storeRef(emptyCell()) +/// .storeRef(s1.asCell()) +/// .asSlice(); +/// let ref1: Cell = s2.preloadRef(); +/// let ref2: Cell = s2.preloadRef(); +/// ref1 == ref2; // true +/// } /// ``` /// /// See: @@ -127,15 +145,16 @@ extends native preloadUint(self: Slice, l: Int): Int; asm(-> 1 0) extends mutates fun loadBool(self: Slice): Bool { 1 LDI } -/// Extension mutation function for the `Slice`. Alias to `Slice.loadBool()`. +/// Extension mutation function for the `Slice`. Alias to `Slice.loadBool()`. Available since Tact 1.5.0. /// /// ```tact -/// let s: Slice = beginCell().storeBool(true).asSlice(); -/// let fizz: Bool = s.loadBit(); // true +/// fun example() { +/// let s: Slice = beginCell().storeBool(true).asSlice(); +/// let fizz: Bool = s.loadBit(); // true +/// } /// ``` /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-cells#sliceloadbit +/// See: https://docs.tact-lang.org/ref/core-cells#sliceloadbit /// asm(-> 1 0) extends mutates fun loadBit(self: Slice): Bool { 1 LDI } diff --git a/stdlib/std/contract.tact b/stdlib/std/contract.tact index 5a782dfa8..1420e3be2 100644 --- a/stdlib/std/contract.tact +++ b/stdlib/std/contract.tact @@ -20,88 +20,223 @@ asm fun myAddress(): Address { MYADDR } asm fun myBalance(): Int { BALANCE FIRST } -// Returns gas consumed by VM so far (including this instruction). +/// Global function. Available since Tact 1.5.0. +/// +/// Returns the nanoToncoin `Int` amount of gas consumed by TVM in the current transaction so far. The resulting value includes the cost of calling this function. +/// +/// ```tact +/// fun example() { +/// let gas: Int = gasConsumed(); +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/core-advanced#gasconsumed +/// asm fun gasConsumed(): Int { GASCONSUMED } -// Calculates computation cost in nanotons for transaction that consumes `gas_used` gas. -asm fun getComputeFee(gas_used: Int, is_masterchain: Bool): Int { GETGASFEE } +/// Global function. Available since Tact 1.5.0. +/// +/// Returns the nanoToncoin `Int` amount of the accumulated storage fee debt. Storage fees are deducted from the incoming message value before the new contract balance is calculated. +/// +/// ```tact +/// fun example() { +/// let debt: Int = myStorageDue(); +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/core-advanced#mystoragedue +/// +asm fun myStorageDue(): Int { DUEPAYMENT } -// Calculates storage fees in nanotons for contract based on current storage prices. `cells` and `bits` are the size of the AccountState (with deduplication, including root cell). -asm fun getStorageFee(cells: Int, bits: Int, seconds: Int, is_masterchain: Bool): Int { GETSTORAGEFEE } +/// Global function. Available since Tact 1.5.0. +/// +/// Calculates and returns the storage fee in nanoToncoins `Int` for storing a contract with a given number of `cells` and `bits` for a number of `seconds`. Uses the prices of the masterchain if `isMasterchain` is `true`, otherwise the prices of the basechain. The current prices are obtained from the config param 18 of TON Blockchain. +/// +/// Note, that the values of `cells` and `bits` are taken modulo their maximum values plus 1. That is, specifying values higher than those listed in account state limits (`max_acc_state_cells` and `max_acc_state_bits`) will have the same result as with specifying the exact limits. In addition, make sure you take into account the deduplication of cells with the same hash. +/// +/// Attempts to specify negative number of `cells`, `bits` or `seconds` throw an exception with exit code 5: `Integer out of expected range`. +/// +/// ```tact +/// fun example() { +/// let fee: Int = getStorageFee(1_000, 1_000, 1_000, false); +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#getstoragefee +/// * https://docs.tact-lang.org/ref/core-advanced#getsimplestoragefee +/// +asm fun getStorageFee(cells: Int, bits: Int, seconds: Int, isMasterchain: Bool): Int { GETSTORAGEFEE } -// Calculates forward fees in nanotons for outgoing message. `is_masterchain` is true if the source or the destination is in masterchain, false if both are in basechain. Note, `cells` and `bits` in Message should be counted with account for deduplication and root-is-not-counted rules. -asm fun getForwardFee(cells: Int, bits: Int, is_masterchain: Bool): Int { GETFORWARDFEE } +/// Global function. Available since Tact 1.5.0. +/// +/// Calculates and returns the compute fee in nanoToncoins `Int` for a transaction that consumed `gasUsed` amount of gas. Uses the prices of the masterchain if `isMasterchain` is `true`, otherwise the prices of the basechain. The current prices are obtained from the config param 20 for the masterchain and config param 21 for the basechain of TON Blockchain. +/// +/// When the `gasUsed` is less than a certain threshold called `flat_gas_limit`, there's a minimum price to pay based on the value of `flat_gas_price`. The less gas is used below this threshold, the higher the minimum price will be. See the example for `getSimpleComputeFee()` to derive that threshold. +/// +/// Attempts to specify negative value of `gasUsed` throw an exception with exit code 5: `Integer out of expected range`. +/// +/// ```tact +/// fun example() { +/// let fee: Int = getComputeFee(1_000, false); +/// } +/// ``` +/// +/// See: https://docs.tact-lang.org/ref/core-advanced#getcomputefee +/// +asm fun getComputeFee(gasUsed: Int, isMasterchain: Bool): Int { GETGASFEE } -// Calculates additional computation cost in nanotons for transaction that consumes additional `gas_used`. In other words, same as `getComputeFee`, but without flat price (just `(gas_used * price) / 2^16`). -asm fun getSimpleComputeFee(gas_used: Int, is_masterchain: Bool): Int { GETGASFEESIMPLE } +/// Global function. Available since Tact 1.5.0. +/// +/// Similar to `getComputeFee()`, but without the `flat_gas_price`, i.e. without a minimum price to pay if the `gasUsed` is less than a certain threshold called `flat_gas_limit`. Calculates and returns only the `gasUsed` times the current gas price. +/// +/// Attempts to specify negative number of `cells`, `bits` or `seconds` throw an exception with exit code 5: `Integer out of expected range`. +/// +/// ```tact +/// fun example() { +/// let fee = getComputeFee(0, false); +/// let feeNoFlat = getSimpleComputeFee(0, false); +/// let maxFlatPrice = fee - feeNoFlat; +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#getsimplestoragefee +/// * https://docs.tact-lang.org/ref/core-advanced#getstoragefee +/// +asm fun getSimpleComputeFee(gasUsed: Int, isMasterchain: Bool): Int { GETGASFEESIMPLE } -// Calculates additional forward cost in nanotons for message that contains additional `cells` and `bits`. In other words, same as `getForwardFee`, but without lump price (just `(bits*bit_price + cells*cell_price) / 2^16`). -asm fun getSimpleForwardFee(cells: Int, bits: Int, is_masterchain: Bool): Int { GETFORWARDFEESIMPLE } +/// Global function. Available since Tact 1.5.0. +/// +/// Calculates and returns the forward fee in nanoToncoins `Int` for an outgoing message consisting of a given number of `cells` and `bits`. Uses the prices of the masterchain if `isMasterchain` is `true{:tact}`, otherwise the prices of the basechain. The current prices are obtained from the config param 24 for the masterchain and config param 25 for the basechain of TON Blockchain. +/// +/// If both the source and the destination addresses are in the basechain, then specify `isMasterchain` as `false`. Otherwise, specify `true`. +/// +/// Note, that the values of `cells` and `bits` are taken modulo their maximum values plus 1. That is, specifying values higher than those listed in account state limits (`max_msg_cells` and `max_msg_bits`) will have the same result as with specifying the exact limits. +/// +/// However, regardless of the values of `cells` and `bits`, this function always adds the minimum price based on the value of `lump_price`. See the example for [`getSimpleForwardFee(){:tact}`](#getsimpleforwardfee) to derive it. In addition, make sure you take into account the deduplication of cells with the same hash, since for example the root cell and its data bits don't count towards the forward fee and are covered by the `lump_price`. +/// +/// Attempts to specify negative number of `cells` or `bits` throw an exception with exit code 5: `Integer out of expected range`. +/// +/// ```tact +/// fun example() { +/// let fee: Int = getForwardFee(1_000, 1_000, false); +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#getforwardfee +/// * https://docs.tact-lang.org/ref/core-advanced#getsimpleforwardfee +/// * https://docs.tact-lang.org/ref/core-advanced#getoriginalfwdfee +/// +asm fun getForwardFee(cells: Int, bits: Int, isMasterchain: Bool): Int { GETFORWARDFEE } -// Calculate `fwd_fee * 2^16 / first_frac`. Can be used to get the original `fwd_fee` of the message (as replacement for hardcoded values) from `fwd_fee` parsed from incoming message. `is_masterchain` is true if the source or the destination is in masterchain, false if both are in basechain. -asm fun getOriginalFwdFee(fwd_fee: Int, is_masterchain: Bool): Int { GETORIGINALFWDFEE } +/// Global function. Available since Tact 1.5.0. +/// +/// Similar to `getForwardFee()`, but without the `lump_price`, i.e. without the minimum price to pay regardless of the amount of `cells` or `bits`. Calculates and returns only the `cells` times the current cell price plus `bits` times the current bit price. +/// +/// Attempts to specify negative number of `cells` or `bits` throw an exception with exit code 5: `Integer out of expected range`. +/// +/// ```tact +/// fun example() { +/// let fee = getForwardFee(1_000, 1_000, false); +/// let feeNoLump = getSimpleForwardFee(1_000, 1_000, false); +/// let lumpPrice = fee - feeNoLump; +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#getsimpleforwardfee +/// * https://docs.tact-lang.org/ref/core-advanced#getforwardfee +/// +asm fun getSimpleForwardFee(cells: Int, bits: Int, isMasterchain: Bool): Int { GETFORWARDFEESIMPLE } -// Current debt for storage fee in nanotons. -asm fun myStorageDue(): Int { DUEPAYMENT } +/// Global function. Available since Tact 1.5.0. +/// +/// Calculates and returns the so-called _original_ forward fee in nanoToncoins `Int` for an outgoing message based on the `fwdFee` obtained from the incoming message. If both the source and the destination addresses are in the basechain, then specify `isMasterchain` as `false`. Otherwise, specify `true`. +/// +/// This function is useful when the outgoing message depends heavily on the structure of the incoming message, so much so that you cannot fully predict the fee using `getForwardFee()` alone. Even if you could, calculating the exact fee with nanoToncoin-level precision can be very expensive, so the approximate value given by this function is often good enough. +/// +/// Attempts to specify a negative value of `fwdFee` throw an exception with exit code 5: `Integer out of expected range`. +/// +/// ```tact +/// fun example() { +/// let fwdFee: Int = context().readForwardFee(); +/// let origFee: Int = getOriginalFwdFee(fee, false); +/// } +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#getoriginalfwdfee +/// * https://docs.tact-lang.org/ref/core-advanced#getforwardfee +/// * https://docs.tact-lang.org/ref/core-advanced#contextreadforwardfee +/// +asm fun getOriginalFwdFee(fwdFee: Int, isMasterchain: Bool): Int { GETORIGINALFWDFEE } -/// Struct representing the standard address on TON Blockchain with signed 8-bit `workchain` ID and an unsigned 256-bit `address` in the specified `workchain`. +/// Struct representing the standard address on TON Blockchain with signed 8-bit `workchain` ID and an unsigned 256-bit `address` in the specified `workchain`. Available since Tact 1.5.0. /// /// At the moment, only `workchain` IDs used on TON are 0 of the basechain and -1 of the masterchain. /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-advanced#parsestdaddress -/// @see https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L105-L106 +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#parsestdaddress +/// * https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L105-L106 /// struct StdAddress { workchain: Int as int8; address: Int as uint256; } -/// Struct representing the address of variable length with signed 32-bit `workchain` ID and a `Slice` containing unsigned `address` in the specified `workchain`. +/// Struct representing the address of variable length with signed 32-bit `workchain` ID and a `Slice` containing unsigned `address` in the specified `workchain`. Available since Tact 1.5.0. /// /// Variable-length addresses are intended for future extensions, and while validators must be ready to accept them in inbound messages, the standard (non-variable) addresses are used whenever possible. /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-advanced#parsevaraddress -/// @see https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L107-L108 +/// See: +/// * https://docs.tact-lang.org/ref/core-advanced#parsevaraddress +/// * https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L107-L108 /// struct VarAddress { workchain: Int as int32; address: Slice; } +/// Assembly function. Available since Tact 1.5.0. +/// /// Converts a `Slice` containing an address into the `StdAddress` Struct and returns it. /// /// ```tact -/// let addr = address("EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2"); -/// let parsedAddr = parseStdAddress(addr.asSlice()); +/// fun example() { +/// let addr = address("EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2"); +/// let parsedAddr = parseStdAddress(addr.asSlice()); /// -/// parsedAddr.workchain; // 0 -/// parsedAddr.address; // 107...287 +/// parsedAddr.workchain; // 0 +/// parsedAddr.address; // 107...287 +/// } /// ``` /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-advanced#parsestdaddress +/// See: https://docs.tact-lang.org/ref/core-advanced#parsestdaddress /// asm fun parseStdAddress(slice: Slice): StdAddress { REWRITESTDADDR } +/// Assembly function. Available since Tact 1.5.0. +/// /// Converts a `Slice` containing an address of variable length into the `VarAddress` Struct and returns it. /// /// ```tact -/// let varAddrSlice = beginCell() -/// .storeUint(6, 3) // to recognize the following as a VarAddress -/// .storeUint(123, 9) // make address occupy 123 bits -/// .storeUint(234, 32) // specify workchain ID of 234 -/// .storeUint(345, 123) // specify address of 345 -/// .asSlice(); -/// let parsedVarAddr = parseVarAddress(varAddrSlice); -/// -/// parsedVarAddr.workchain; // 234 -/// parsedVarAddr.address; // CS{Cell{002...2b3} bits: 44..167; refs: 0..0} -/// parsedVarAddr.address.loadUint(123); // 345 +/// fun example() { +/// let varAddrSlice = beginCell() +/// .storeUint(6, 3) // to recognize the following as a VarAddress +/// .storeUint(123, 9) // make address occupy 123 bits +/// .storeUint(234, 32) // specify workchain ID of 234 +/// .storeUint(345, 123) // specify address of 345 +/// .asSlice(); +/// let parsedVarAddr = parseVarAddress(varAddrSlice); +/// +/// parsedVarAddr.workchain; // 234 +/// parsedVarAddr.address; // CS{Cell{002...2b3} bits: 44..167; refs: 0..0} +/// parsedVarAddr.address.loadUint(123); // 345 +/// } /// ``` /// -/// @since Tact 1.5.0 -/// @see https://docs.tact-lang.org/ref/core-advanced#parsevaraddress +/// See: https://docs.tact-lang.org/ref/core-advanced#parsevaraddress /// asm fun parseVarAddress(slice: Slice): VarAddress { REWRITEVARADDR }