diff --git a/CHANGELOG.md b/CHANGELOG.md index 15971c468..4b80f5e8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Do not automatically validate all addresses when receiving/sending messages or using address manipulating functions: PR [#1207](https://github.com/tact-lang/tact/pull/1207) - Remove `enabledMasterchain` compiler config option from `tact.config.json`: PR [#1207](https://github.com/tact-lang/tact/pull/1207) - Remove `org.ton.chain.any.v0` interface: PR [#1207](https://github.com/tact-lang/tact/pull/1207) +- To reduce fees, Tact no longer stores the parent contract code in the system cell that holds all the child contract codes used in `initOf`. Instead, the `MYCODE` instruction is used: PR [#1213](https://github.com/tact-lang/tact/pull/1213) ### Fixed @@ -60,6 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a link to the article by CertiK to Security best practices page: PR [#1185](https://github.com/tact-lang/tact/pull/1185) - Added a note on `dump()` being computationally expensive: PR [#1189](https://github.com/tact-lang/tact/pull/1189) - Fixed links in Chinese translation: PR [#1206](https://github.com/tact-lang/tact/pull/1206) +- Added a note on 255 being the maximum number of messages that can be sent during action phase: PR [#1237](https://github.com/tact-lang/tact/pull/1237) - Added onchain metadata creation for NFTs and Jettons to the cookbook: PR [#1236](https://github.com/tact-lang/tact/pull/1236) ### Release contributors diff --git a/RELEASE.md b/RELEASE.md index 03c168641..7bb7437be 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,6 +2,10 @@ - [ ] Improve the changelog for `vX.Y.Z`: grammar, wording, polishing - [ ] Make sure there are no open issues for the [vX.Y.Z milestone](https://github.com/tact-lang/tact/issues?q=is%3Aopen+is%3Aissue+milestone%3AvX.Y.Z) (except for the current one, of course) +- [ ] Remove "(not released yet)" from docs: + - [ ] `cd docs` — important as to not change the texts elsewhere, such as in code comments + - [ ] `regex='([sS]ince Tact \d\.\d) \(not released yet\)'; rg "$regex" -r '$1'` (or similar with `grep`) — to preview the changes + - [ ] `regex='([sS]ince Tact \d\.\d) \(not released yet\)'; rg "$regex" -l | xargs sd "$regex" '$1'` (or similar with `grep` and `sed`) — to apply the changes - [ ] Bump Tact version in: - [ ] [`package.json`](./package.json) file - [ ] [CHANGELOG.md](./CHANGELOG.md): `Unreleased` -> `vX.Y.Z` @@ -16,7 +20,6 @@ $ git checkout vX.Y.Z $ yarn all && npm publish ``` -- [ ] Update [tact-docs](https://github.com/tact-lang/tact-docs) with the most recent Tact features (tracked in: ) - [ ] Request or perform the plugins/parsers/tools updates and releases: - [ ] (tracked in: ) - [ ] (tracked in: ) diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md new file mode 100644 index 000000000..7f36f241d --- /dev/null +++ b/STYLEGUIDE.md @@ -0,0 +1,117 @@ +# Styleguide + +Due to stringent security and correctness requirements we have to use a subset of TypeScript features that are known to be _less unsound_. + +## Do + +Prefer the most simple and basic language features. + +- Variables `const x = f()` +- Functions `(x: T) => x`, `f(x)` +- Objects `{ foo: 'bar' }`, `x.foo`, `const { foo } = x;` +- Statements `if`, `for`, `return` +- Aliases `type F = { foo: T }` +- Simple types `number`, `string`, `boolean`, `undefined` +- Literal types `1`, `"hello"` +- Unions of literal types `1 | 2 | 3` +- Tagged unions `{ kind: 'A', a: number } | { kind: 'B' }` + +## Don't + +### Don't explicitly break type system + +- **Don't use `as`**, except `as const`. It was meant for gradually typing legacy code, not for production use in TS-native projects. Often `x as T` actually was meant to be `const y: T = x;` or `x satisfies T`. +- **Don't use `any`.** Its actual meaning is "completely disregard type errors here". Prefer `unknown` or `never`. +- **Don't use guard types `x is T`.** These are just `as` in disguise. Might be used in very simple cases, such as ADT boilerplate. +- **Don't use overloading**. It's almost the same as intersection types, and intersection types are broken. +- **Don't use `@ts-ignore`.** It's a worse version of `as` that can take TS compiler into an arbitrary incorrect state. +- **Don't use `x!` operator.** It does no checks at runtime, and is essentially `x as NotNull`. +- **Don't pick into values that are generic** `(x: T) => typeof x === 'number'`. Properly designed languages do not allow this. + +Workarounds for bugs in compiler will sometimes require an `as` or `any`. In this case the workaround should be small and generic, separated into its own file, and annotated with reference to TypeScript issue. + +For example, `["a", "b"].includes(x)` doesn't narrow `x` to `'a' | 'b'`. Instead of multiple `x as ('a' | 'b')` over the whole codebase we can define a wrapper once in `util.ts`, thoroughly test and review it, and keep it in containment this way. + +```typescript +// `extends string` is usually a bad idea, but `key is K` is an even worse idea +export const includes = ( + keys: readonly K[], + key: string, +): key is K => { + // we have to do this, otherwise next line will complain that `key` isn't `K` + const keys1: readonly string[] = keys; + return keys1.includes(key); +}; +``` + +### Don't use mutability unless required + +- **Don't use `let`.** Most likely it should be a separate function where each assignment is a `return`. +- **Never use `var`.** They're hoisted up to the function or module scope. `for (var i = 0; i < n; ++i) a.push(() => i)` creates an array of functions referencing the same value of `i === n`. +- **Never use `let` in tests.** `jest` execution order is hard to understand, and most likely it will lead to flaky tests. +- **Never use `let` global variables.** These break tree-shaking, make code untestable, make behavior dependent on module initialization order. +- **Don't assign to function arguments** `(x) => { x = 1; }` +- **Don't use `for (let ...)` loops.** Prefer built-in array methods. +- **All object fields must be `readonly`**: `{ readonly foo: string }`. +- **Tag fields on tagged unions absolutely have to be readonly.** By assigning into a tag field of a `A | B` union, we can narrow it to an `A` type while it will have `B` (or some arbitrary combination of `A` and `B`) at runtime. +- **Arrays should be readonly**: `readonly string[]` +- **Tuples should be readonly**: `readonly [string, number]` +- **Prefer freezing highly reused objects** with `Object.freeze`. +- **Avoid `void` type.** + +### Don't use untagged unions + +- **Don't use untagged non-literal unions** `{ a: 1 } | { b: 2 }`. These will require an `in`-condition for narrowing to select either of branches. +- **Don't use `in`.** TypeScript has no distinction between open and closed object types. There might be no field in an object type, but there will be one at runtime. Narrowing for `in` operators is also buggy in other ways, and doesn't properly apply to either of its arguments. + +### Don't use JS object "features" + +- **Don't use optional fields** `foo?: Bar`. Every `?` doubles number of cases that should be tested. Eventually some combination of non-defined and undefined fields will be non-semantic. +- _**Don't use optional fields**_. In JS there is a distinction between field that is not defined and a field that is `undefined` (sic). `'a' in {} === false`, `'a' in { a: undefined } === true`. TypeScript doesn't handle this properly in its type system. +- **Don't use `Proxy`**. These break type safety, are incorrectly handled in debuggers, lead to very unexpected heisenbugs. +- **Don't use `get` and `set`**. See `Proxy` above. +- **Don't use `...` with objects**. It will require intersection types or inheritance to type. Prefer aggregation: `{ ...a, b }` → `{ a, b }`. +- **Don't use `interface ... extends`**. There is no way to safely distinguish objects supporting parent and child interfaces at runtime. + - Except where it's required to untie type recursion. For example `type Foo = A` would only work as `interface Foo extends A {}` + +### Don't use JS function "features" + +- **Don't use optional parameters** `(x?: number) => {}`. TypeScript has a quirk that allows passing function with less arguments to parameter that requires more. Eventually this leads to passing unexpected values to optional parameters. Prefer decomposing the function into two, where one takes full set of parameters, and the other one is a simpler version that takes less. +- **Don't use default parameters** `(x = 1) => {}`. See reasoning above. +- **Don't use `...rest` parameters**. TypeScript doesn't have "mapped tuple types", so typing these will be problematic. In most cases passing an array would suffice, and would only take two more characters to type. +- **Don't use `arguments`**. This is a worse version of `...rest` that isn't even an array. +- **Don't pass functions directly** to built-in functions `.map(foo)`. A good example of why this is a bad idea: `["10", "10", "10"].map(parseInt)`. Prefer `.map(x => foo(x))`. + +### Don't use OOP + +- **Don't use methods**. Methods don't store reference to `this` in their closure, and take it from object _syntactically_, i.e. `x.f()` would use `x` as `this`. It means `const { f } = x; f();` will be a runtime error, and TypeScript would emit no error here at compile time. +- **Don't use inheritance**. Overriding is allowed in JS, Liskov substitution is not guaranteed. +- **Don't use `class`**. In TS `private` is only a type system feature, and all the private fields are globally accessible at runtime. Worse, created objects have part of their description in `prototype`, and can't be worked with as regular objects. If class is converted to regular function, all the fields are properly private, accessing them requires no `this.` prefix, and in most frequent case there is only one exposed method that can be returned directly. + +### Don't use funny types + +- **Don't use conditional types**. These lack any theory behind them, and were meant to type legacy JS code. +- **Don't use `Omit`, `Pick`, `Exclude` and `Extract`**. These are conditional types in disguise. +- **Don't use mapped object types** `{ [K in keyof T]: ... }`. These lack any theory behind them, don't have introducing syntax, and are buggy. +- **Don't use index types** `T["foo"]`. They disregard variance at their use site, and most likely will pin types in another library/module with both lower and upper bounds, thus they're very detrimental to modularity. Most likely type of `foo` field should be defined separately. +- **Don't use indexed types** `{ [k: string]: number }`. It's `Record`. The only valid case is to avoid type recursion quirks in TS: `type Json = null | boolean | number | string | Json[] | { [k: string]: Json }` would emit error with `Record` +- **Don't define bounds on generic parameters** ` string>`. Type inference is broken here, and in many cases will force TS to infer type of bound instead of expected type. +- **Don't use intersection types** `A & B`. They are so broken it's hard to even count them as intersection types. +- **Don't use `Object`, `object`, `{}`, `Function`**. There's barely a case when common supertypes of objects and functions are even needed. + +### Don't use arcane language "features" + +- **Don't use `export default`**. It breaks IDE features such as renaming, and also has complex semantics. +- **Don't use `while`** loops. Every iteration must have at least an explicit "fuel" check. `while` _always_ eventually leads to infinite loops. +- **Don't use `for (... in ...)`**. It requires `hasOwnProperty` check. Prefer `Object.entries`, `Object.keys` or `Object.values`. +- **Don't define `toString` and `toJSON`** on objects. These obfuscate results of `console.log`, make objects different depending on the way they're logged (`util.inspect` wouldn't use them). +- **Don't use `JSON.parse` and `JSON.stringify`** without `zod`. Both functions have very broken types: `JSON.stringify(undefined)`. + +### Other considerations + +- **Beware of `${}`** in template strings. Any inlining succeeds, and there won't be any compile-time errors even if it's a function `${(x: number) => x}`. +- **Avoid `null`**. `typeof null === 'object'`, and there is `undefined` anyway. +- **Avoid exceptions**. Exceptions are untyped. +- **Avoid tuples**. TS gives them minimal distinction from arrays, and type system is broken around them. Occasionally for performance reasons tuples might be a better option than objects. +- **Avoid `enum`**. It's equivalent to unions since 5.0, except generates boilerplate JS code. A version that doesn't generate extraneous code, `const enum`, is not properly supported by `babel`. +- **Avoid iterators**. They're untypable unless fixed in JS standard. Prefer generators. Prefer iterating with `for (... of ...)`. diff --git a/cspell.json b/cspell.json index da482d34d..761007fc5 100644 --- a/cspell.json +++ b/cspell.json @@ -52,6 +52,7 @@ "getsimpleforwardfee", "gettest", "Héctor", + "heisenbugs", "infixl", "infixr", "initof", @@ -66,6 +67,7 @@ "Korshakov", "Laika", "langle", + "Liskov", "lparen", "lvalue", "lvalues", @@ -107,7 +109,9 @@ "Ston", "struct", "structs", + "styleguide", "subtyping", + "supertypes", "Tarjan", "testdata", "Topup", @@ -118,6 +122,7 @@ "uintptr", "uninit", "unixfs", + "untypable", "varuint", "workchain", "xffff", @@ -126,7 +131,8 @@ "ignoreRegExpList": [ "\\b[xB]\\{[a-fA-F0-9]*_?\\}", // binary literals in Fift-asm "\\b0[xX][a-fA-F0-9_]*\\b", // hexadecimal numbers - "\\b(?:address|crc32|cell|slice|rawSlice)\\(\".+\"\\)" // some comptime functions + "\\b(?:address|crc32|cell|slice|rawSlice)\\(\".+\"\\)", // some comptime functions + "ince Tact " // regex in RELEASE.md ], "flagWords": [], "ignorePaths": [ diff --git a/docs/src/content/docs/book/exit-codes.mdx b/docs/src/content/docs/book/exit-codes.mdx index 69d8e4fb9..d00c141b5 100644 --- a/docs/src/content/docs/book/exit-codes.mdx +++ b/docs/src/content/docs/book/exit-codes.mdx @@ -62,8 +62,8 @@ Exit code | Origin | Brief description [$133$](#133) | Tact compiler ([Compute phase][c]) | Contract stopped. Reserved, but never thrown. [$134$](#134) | Tact compiler ([Compute phase][c]) | Invalid argument. [$135$](#135) | Tact compiler ([Compute phase][c]) | Code of a contract was not found. -~~[$136$](#136)~~ | ~~Tact compiler ([Compute phase][c])~~ | ~~Invalid address.~~ Removed since Tact 1.6 -~~[$137$](#137)~~ | ~~Tact compiler ([Compute phase][c])~~ | ~~Masterchain support is not enabled for this contract.~~ Removed since Tact 1.6 +~~[$136$](#136)~~ | ~~Tact compiler ([Compute phase][c])~~ | ~~Invalid address.~~ Removed since Tact 1.6 (not released yet) +~~[$137$](#137)~~ | ~~Tact compiler ([Compute phase][c])~~ | ~~Masterchain support is not enabled for this contract.~~ Removed since Tact 1.6 (not released yet) :::note @@ -502,6 +502,12 @@ When processing the message, TON Blockchain tries to pack it according to the [r If there would not be enough funds to process all the cells in a message, the message is too large or its Merkle depth is too big, an error with exit code $40$ is thrown: `Cannot process a message`. +:::note + + If the [optional flag +2](/book/message-mode#optional-flags) is set, this error won't be thrown and the given message won't be sent. + +::: + ### 41: Library reference is null {#41} If the library reference was required during library change action, but it was null, an error with exit code $41$ is thrown: `Library reference is null`. @@ -625,7 +631,7 @@ If the code of the contract doesn't match the one saved in TypeScript wrappers, ### 136: Invalid address {#136} -

+

A value of type [`Address{:tact}`][p] is valid in Tact when: @@ -648,7 +654,7 @@ try { ### 137: Masterchain support is not enabled for this contract {#137} -

+

Prior to removal, any attempts to point to masterchain (ID $-1$) or otherwise interact with it without enabling masterchain support were throwing an exception with exit code $137$: `Masterchain support is not enabled for this contract`. diff --git a/docs/src/content/docs/book/functions.mdx b/docs/src/content/docs/book/functions.mdx index e2f3d27a6..208a3d355 100644 --- a/docs/src/content/docs/book/functions.mdx +++ b/docs/src/content/docs/book/functions.mdx @@ -150,7 +150,7 @@ contract Treasure { ### Explicit resolution of method ID collisions -

+

Like other functions in TON contracts, getters have their _unique_ associated function selectors, which are $19$-bit signed integer identifiers commonly called _method IDs_. diff --git a/docs/src/content/docs/book/maps.mdx b/docs/src/content/docs/book/maps.mdx index 574efa07a..4183465de 100644 --- a/docs/src/content/docs/book/maps.mdx +++ b/docs/src/content/docs/book/maps.mdx @@ -31,6 +31,24 @@ Allowed value types: * [Struct](/book/structs-and-messages#structs) * [Message](/book/structs-and-messages#messages) +## Serialization + +It's possible to do [integer serialization](/book/integers#common-serialization-types) of map keys, values or both to [preserve space and reduce storage costs](/book/integers#serialization): + +```tact +struct SerializedMapInside { + // Both keys and values here would be serialized as 8-bit unsigned integers, + // thus preserving the space and reducing storage costs: + countersButCompact: map; +} +``` + +:::note + + Read more about serialization in Tact: [Compatibility with FunC](/book/func#convert-serialization). + +::: + ## Operations ### Declare, `emptyMap()` {#emptymap} @@ -98,7 +116,7 @@ if (gotButUnsure != null) { ### Replace values, `.replace()` {#replace} -

+

To replace the value under a key, if such a key exists, use the `.replace(){:tact}` [method](/book/functions#extension-function). It returns `true{:tact}` on successful replacement and `false{:tact}` otherwise. @@ -140,7 +158,7 @@ replaced2; // false ### Replace and get old value, `.replaceGet()` {#replaceget} -

+

Like [`.replace()`](#replace), but instead of returning a [`Bool{:tact}`](/book/types#booleans) it returns the old (pre-replacement) value on successful replacement and [`null{:tact}`](/book/optionals) otherwise. @@ -313,7 +331,9 @@ There, both maps are formed manually and both contain the same key-value pair. I ### Convert to a `Cell`, `.asCell()` {#ascell} -Use `.asCell(){:tact}` [method](/book/functions#extension-function) on maps to convert all their values to a [`Cell{:tact}`][cell] type. Be mindful, that [`Cell{:tact}`][cell] type is able to store up to 1023 bits, so converting larger maps to the Cell will result in error. +On [TVM][tvm], maps are represented as a [`Cell{:tact}`][cell] type and it's possible to construct and parse them directly. However, doing so is highly error-prone and quite messy, which is why Tact provides maps as a standalone composite type with many of the helper methods mentioned above. + +To cast maps back to the underlying [`Cell{:tact}`][cell] type, use the `.asCell(){:tact}` [method](/book/functions#extension-function). Since maps are initialized to `null{:tact}`, calling `.asCell(){:tact}` on a map with no values assigned will return `null{:tact}` and **not** an empty [`Cell{:tact}`][cell]. As an example, this method is useful for sending small maps directly in the body of the reply: @@ -335,7 +355,7 @@ contract Example { // Internal message receiver, which responds to empty messages receive() { // Here we're converting the map to a Cell and making a reply with it - self.reply(self.fizz.asCell()); + self.reply(self.fizz.asCell()!!); // explicitly asserting that the map isn't null } } ``` @@ -429,24 +449,6 @@ It's often useful to set an upper-bound restriction on such maps, so that you [d ::: -## Serialization - -It's possible to do [integer serialization](/book/integers#common-serialization-types) of map keys, values or both to [preserve space and reduce storage costs](/book/integers#serialization): - -```tact -struct SerializedMapInside { - // Both keys and values here would be serialized as 8-bit unsigned integers, - // thus preserving the space and reducing storage costs: - countersButCompact: map; -} -``` - -:::note - - Read about other serialization options: [Compatibility with FunC](/book/func#convert-serialization). - -::: - ## Limits and drawbacks While maps can be convenient to work with on a small scale, they cause a number of issues if the number of items is unbounded and map can significantly grow in size: @@ -492,3 +494,4 @@ If you still need a large map or an unbound (infinitely large) map, it's better [cell]: /book/cells#cells [hashmap]: https://docs.ton.org/develop/data-formats/tl-b-types#hashmap +[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview diff --git a/docs/src/content/docs/book/message-mode.mdx b/docs/src/content/docs/book/message-mode.mdx index bc846f9ae..b3478f9c6 100644 --- a/docs/src/content/docs/book/message-mode.mdx +++ b/docs/src/content/docs/book/message-mode.mdx @@ -13,7 +13,7 @@ It's possible to use raw [`Int{:tact}`][int] values and manually provide them fo Mode value | Constant name | Description ---------: | :---------------------------- | ----------- -$0$ | `SendDefaultMode{:tact}` | Ordinary message (default). +$0$ | `SendDefaultMode{:tact}` | Ordinary message (default). $64$ | `SendRemainingValue{:tact}` | Carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. $128$ | `SendRemainingBalance{:tact}` | Carry all the remaining balance of the current smart contract instead of the value originally indicated in the message. diff --git a/docs/src/content/docs/book/send.mdx b/docs/src/content/docs/book/send.mdx index 9c2283da7..b6ba343e4 100644 --- a/docs/src/content/docs/book/send.mdx +++ b/docs/src/content/docs/book/send.mdx @@ -57,11 +57,16 @@ send(SendParameters{ }); ``` -The [optional flag](/book/message-mode#optional-flags) `SendIgnoreErrors{:tact}` means that even when an error occurs during message sending next messages would be sent anyway. **No error during the sending phase would revert a transaction.** +The [optional flag](/book/message-mode#optional-flags) `SendIgnoreErrors{:tact}` means that if an error occurs during [message send](#outbound-message-processing), it will be ignored and the given message will be skipped. Message-related [action phase][phases] [exit codes](/book/exit-codes) that might be thrown without the `SendIgnoreErrors{:tact}` set are: + +* $36$: [`Invalid destination address in outbound message`](/book/exit-codes#36) +* $37$: [`Not enough Toncoin`](/book/exit-codes#37) +* $39$: [`Outbound message doesn't fit into a cell`](/book/exit-codes#39) +* $40$: [`Cannot process a message`](/book/exit-codes#40) ## Send typed message -To send a binary typed message you can use the following code: +To send a typed message you can use the following code: ```tact let recipient: Address = address("..."); @@ -98,7 +103,7 @@ send(SendParameters{ Each transaction on TON Blockchain consists of [multiple phases][phases]. Outbound messages are evaluated in [compute phase][compute], but are **not** sent in that phase. Instead, they're queued in order of appearance for the [action phase][phases], where all actions listed in [compute phase][compute], like outbound messages or [reserve requests](/ref/core-advanced#nativereserve), are executed. -As all the values are computed in [compute phase][compute], all the fees computed by the end of it, and exceptions do not revert the transaction during [action phase][phases], outbound message sends can fail without bounce due to unsufficient [action fees](https://docs.ton.org/develop/howto/fees-low-level#action-fee) or [forward fees][fwdfee]. +As all the values are computed in [compute phase][compute], all the fees computed by the end of it, and exceptions do not revert the transaction during [action phase][phases], outbound message sends can fail without bounce due to insufficient [action fees](https://docs.ton.org/develop/howto/fees-low-level#action-fee) or [forward fees][fwdfee]. Consider the following example: @@ -132,17 +137,22 @@ There, the second message won't actually be sent: * When the second message is processed, contract tries to send $\mathrm{R}$ [nanoToncoins](/book/integers#nanotoncoin), but fails to do so because there is already a smaller amount left. -:::note +## Message sending limits + +In total, there could be no more than $255$ actions queued for execution, which means that the maximum allowed number of messages sent per transaction is $255$. + +Attempts to queue more throw an exception with an [exit code 33](/book/exit-codes#33) during [action phase][phases]: `Action list is too long`. + +## Message sending functions - Read more about all message sending functions in the Reference: - * [`send(){:tact}`](/ref/core-common#send) - * [`emit(){:tact}`](/ref/core-common#emit) - * [`self.notify(){:tact}`](/ref/core-base#self-notify) - * [`self.reply(){:tact}`](/ref/core-base#self-reply) - * [`self.forward(){:tact}`](/ref/core-base#self-forward) - * [`nativeSendMessage(){:tact}`](/ref/core-advanced#nativesendmessage) +Read more about all message sending functions in the Reference: -::: +* [`send(){:tact}`](/ref/core-common#send) +* [`emit(){:tact}`](/ref/core-common#emit) +* [`self.notify(){:tact}`](/ref/core-base#self-notify) +* [`self.reply(){:tact}`](/ref/core-base#self-reply) +* [`self.forward(){:tact}`](/ref/core-base#self-forward) +* [`nativeSendMessage(){:tact}`](/ref/core-advanced#nativesendmessage) [p]: /book/types#primitive-types [int]: /book/integers diff --git a/docs/src/content/docs/book/statements.mdx b/docs/src/content/docs/book/statements.mdx index dd8c1f67b..915912cf4 100644 --- a/docs/src/content/docs/book/statements.mdx +++ b/docs/src/content/docs/book/statements.mdx @@ -100,7 +100,7 @@ value += 5; // augmented assignment (one of the many, see below) ## Destructuring assignment -

+

The destructuring assignment is a concise way to unpack [Structs][s] and [Messages][m] into distinct variables. It mirrors the [instantiation syntax](/book/expressions#instantiation), but instead of creating a new [Struct][s] or [Message][m] it binds every field or some of the fields to their respective variables. diff --git a/docs/src/content/docs/book/structs-and-messages.mdx b/docs/src/content/docs/book/structs-and-messages.mdx index 37a9294ec..e173c3337 100644 --- a/docs/src/content/docs/book/structs-and-messages.mdx +++ b/docs/src/content/docs/book/structs-and-messages.mdx @@ -118,7 +118,7 @@ message(0x7362d09c) TokenNotification { This is useful for cases where you want to handle certain opcodes of a given smart contract, such as [Jetton standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md). The short-list of opcodes this contract is able to process is [given here in FunC](https://github.com/ton-blockchain/token-contract/blob/main/ft/op-codes.fc). They serve as an interface to the smart contract. - A message opcode can be any [compile-time](/ref/core-comptime) expression that evaluates to a positive $32$-bit integer, so the following is also valid: + A message opcode can be any [compile-time](/ref/core-comptime) expression that evaluates to a positive $32$-bit integer, so the following is also valid: ```tact // This Message overwrites its unique id (opcode) with 898001897, diff --git a/docs/src/content/docs/ref/core-advanced.mdx b/docs/src/content/docs/ref/core-advanced.mdx index 02531ef29..79fdc270c 100644 --- a/docs/src/content/docs/ref/core-advanced.mdx +++ b/docs/src/content/docs/ref/core-advanced.mdx @@ -434,6 +434,8 @@ fun nativeSendMessage(cell: Cell, mode: Int); [Queues the message](/book/send#outbound-message-processing) to be sent by specifying the complete `cell` and the [message `mode`](/book/message-mode). +Attempts to queue more than $255$ messages throw an exception with an [exit code 33](/book/exit-codes#33): `Action list is too long`. + :::note Prefer using a much more common and user-friendly [`send(){:tact}`](/ref/core-common#send) function unless you have a complex logic that can't be expressed otherwise. @@ -466,6 +468,8 @@ It's possible to use raw [`Int{:tact}`][int] values and manually provide them fo Currently, `amount` must be a non-negative integer, and `mode` must be in the range $0..31$, inclusive. + Additionally, attempts to queue more than $255$ reservations in one transaction throw an exception with an [exit code 33](/book/exit-codes#33): `Action list is too long`. + ::: ### Base modes {#nativereserve-base-modes} diff --git a/docs/src/content/docs/ref/core-common.mdx b/docs/src/content/docs/ref/core-common.mdx index 4afcbf27e..5444ece78 100644 --- a/docs/src/content/docs/ref/core-common.mdx +++ b/docs/src/content/docs/ref/core-common.mdx @@ -37,7 +37,7 @@ let iNeedADolla: Int = myBalance(); :::caution - Beware, that [all message sending functions](/book/send) of Tact can change the _actual_ contract's balance, but they _won't_ update the value returned by this function. + Beware, that [all message sending functions](/book/send#message-sending-functions) of Tact can change the _actual_ contract's balance, but they _won't_ update the value returned by this function. ::: @@ -202,6 +202,8 @@ fun send(params: SendParameters); [Queues the message](/book/send#outbound-message-processing) to be sent using a [`SendParameters{:tact}`](/book/send) [Struct](/book/structs-and-messages#structs). +Attempts to queue more than $255$ messages throw an exception with an [exit code 33](/book/exit-codes#33): `Action list is too long`. + Usage example: ```tact @@ -228,6 +230,8 @@ fun emit(body: Cell); [Queues the message](/book/send#outbound-message-processing) `body` to be sent to the outer world with the purpose of logging and analyzing it later off-chain. The message does not have a recipient and is gas-efficient compared to using any other message sending functions of Tact. +Attempts to queue more than $255$ messages throw an exception with an [exit code 33](/book/exit-codes#33): `Action list is too long`. + Usage example: ```tact diff --git a/docs/src/content/docs/zh-cn/book/functions.mdx b/docs/src/content/docs/zh-cn/book/functions.mdx index 8abd57b11..c35b4ccb8 100644 --- a/docs/src/content/docs/zh-cn/book/functions.mdx +++ b/docs/src/content/docs/zh-cn/book/functions.mdx @@ -139,7 +139,7 @@ contract Treasure { ``` ### 明确解决方法 ID 碰撞问题 -

+

与 TVM 合约中的其他函数一样,getters 也有其*独特*的相关函数选择器,即一些整数 ID(称为*方法 ID*)。 其中一些整数是为内部目的保留的,例如 -4, -3, -2, -1, 0 是保留 ID,而 diff --git a/package.json b/package.json index 4db5dec06..59de8b843 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "clean": "rm -fr dist", "cleanall": "rm -fr dist node_modules", "build": "tsc && node --no-warnings=ExperimentalWarning -r ts-node/register ./scripts/copy-files", - "test": "jest", + "test": "yarn gen:grammar && jest", "coverage": "cross-env COVERAGE=true jest", "release": "yarn clean && yarn build && yarn coverage && yarn release-it --npm.yarn1", "type": "tsc --noEmit", @@ -47,7 +47,7 @@ }, "dependencies": { "@tact-lang/opcode": "^0.0.16", - "@ton/core": "0.59.0", + "@ton/core": "0.59.1", "@ton/crypto": "^3.2.0", "blockstore-core": "1.0.5", "change-case": "^4.1.2", @@ -67,7 +67,7 @@ "@tact-lang/coverage": "^0.0.8", "@tact-lang/ton-abi": "^0.0.3", "@tact-lang/ton-jest": "^0.0.4", - "@ton/sandbox": "^0.22.0", + "@ton/sandbox": "^0.23.0", "@ton/test-utils": "^0.4.2", "@types/glob": "^8.1.0", "@types/jest": "^29.5.12", diff --git a/src/benchmarks/benchmarks.spec.ts b/src/benchmarks/benchmarks.spec.ts index 6a9a80cd0..6e4075284 100644 --- a/src/benchmarks/benchmarks.spec.ts +++ b/src/benchmarks/benchmarks.spec.ts @@ -33,11 +33,11 @@ describe("benchmarks", () => { .description as TransactionDescriptionGeneric ).computePhase as TransactionComputeVm ).gasUsed; - expect(gasUsed).toMatchInlineSnapshot(`3146n`); + expect(gasUsed).toMatchInlineSnapshot(`3018n`); // Verify code size const codeSize = functions.init!.code.toBoc().length; - expect(codeSize).toMatchInlineSnapshot(`241`); + expect(codeSize).toMatchInlineSnapshot(`233`); }); it("benchmark functions (inline)", async () => { @@ -57,10 +57,10 @@ describe("benchmarks", () => { .description as TransactionDescriptionGeneric ).computePhase as TransactionComputeVm ).gasUsed; - expect(gasUsed).toMatchInlineSnapshot(`3015n`); + expect(gasUsed).toMatchInlineSnapshot(`2887n`); // Verify code size const codeSize = functionsInline.init!.code.toBoc().length; - expect(codeSize).toMatchInlineSnapshot(`234`); + expect(codeSize).toMatchInlineSnapshot(`226`); }); }); diff --git a/src/bindings/writeTypescript.ts b/src/bindings/writeTypescript.ts index 5d1bee611..b179f9bf0 100644 --- a/src/bindings/writeTypescript.ts +++ b/src/bindings/writeTypescript.ts @@ -45,7 +45,7 @@ export function writeTypescript( abi: ContractABI, init?: { code: string; - system: string; + system: string | null; args: ABIArgument[]; prefix?: | { @@ -171,11 +171,13 @@ export function writeTypescript( w.inIndent(() => { // Code references w.append(`const __code = Cell.fromBase64('${init.code}');`); - w.append(`const __system = Cell.fromBase64('${init.system}');`); - - // Stack w.append("let builder = beginCell();"); - w.append(`builder.storeRef(__system);`); + + if (init.system !== null) { + w.append(`const __system = Cell.fromBase64('${init.system}');`); + w.append(`builder.storeRef(__system);`); + } + if (init.prefix) { w.append( `builder.storeUint(${init.prefix.value}, ${init.prefix.bits});`, diff --git a/src/generator/writeProgram.ts b/src/generator/writeProgram.ts index 51acceea6..7523c8385 100644 --- a/src/generator/writeProgram.ts +++ b/src/generator/writeProgram.ts @@ -96,7 +96,7 @@ export async function writeProgram( const stdlibHeader = trimIndent(` global (int, slice, int, slice) __tact_context; global slice __tact_context_sender; - global cell __tact_context_sys; + global cell __tact_child_contract_codes; global int __tact_randomized; `); diff --git a/src/generator/writers/writeContract.ts b/src/generator/writers/writeContract.ts index 14264c06a..b205e8c3b 100644 --- a/src/generator/writers/writeContract.ts +++ b/src/generator/writers/writeContract.ts @@ -35,7 +35,10 @@ export function writeStorageOps( ctx.append(`slice $sc = get_data().begin_parse();`); // Load context - ctx.append(`__tact_context_sys = $sc~load_ref();`); + if (type.dependsOn.length > 0) { + ctx.append(`__tact_child_contract_codes = $sc~load_ref();`); + } + ctx.append(`int $loaded = $sc~load_int(1);`); // Load data @@ -78,7 +81,9 @@ export function writeStorageOps( ctx.append(`builder b = begin_cell();`); // Persist system cell - ctx.append(`b = b.store_ref(__tact_context_sys);`); + if (type.dependsOn.length > 0) { + ctx.append(`b = b.store_ref(__tact_child_contract_codes);`); + } // Persist deployment flag ctx.append(`b = b.store_int(true, 1);`); @@ -156,12 +161,9 @@ export function writeInit( }); ctx.fun(ops.contractInitChild(t.name, ctx), () => { - const args = [ - `cell sys'`, - ...init.params.map( - (v) => resolveFuncType(v.type, ctx) + " " + funcIdOf(v.name), - ), - ]; + const args = init.params.map( + (v) => resolveFuncType(v.type, ctx) + " " + funcIdOf(v.name), + ); const sig = `(cell, cell) ${ops.contractInitChild(t.name, ctx)}(${args.join(", ")})`; ctx.signature(sig); if (enabledInline(ctx.ctx)) { @@ -169,33 +171,62 @@ export function writeInit( } ctx.context("type:" + t.name + "$init"); ctx.body(() => { - ctx.write(` - slice sc' = sys'.begin_parse(); - cell source = sc'~load_dict(); - cell contracts = new_dict(); - - ;; Contract Code: ${t.name} - cell mine = ${ctx.used(`__tact_dict_get_code`)}(source, ${t.uid}); - contracts = ${ctx.used(`__tact_dict_set_code`)}(contracts, ${t.uid}, mine); - `); - - // Copy contracts code - for (const c of t.dependsOn) { + ctx.append(";; Build init code cell"); + ctx.append(); + if (t.name === ctx.name) { + // The contract wants to deploy its copy + ctx.write(` + ;; Contract Code: ${t.name} + cell init_code = my_code(); + `); + ctx.append(); + ctx.append(";; Build init data cell"); ctx.append(); + ctx.append("builder b = begin_cell();"); + if (t.dependsOn.length > 0) { + ctx.append("b = b.store_ref(__tact_child_contract_codes);"); + } + } else { + ctx.write(` + slice sc' = __tact_child_contract_codes.begin_parse(); + cell source = sc'~load_dict(); + `); ctx.write(` - ;; Contract Code: ${c.name} - cell code_${c.uid} = __tact_dict_get_code(source, ${c.uid}); - contracts = ${ctx.used(`__tact_dict_set_code`)}(contracts, ${c.uid}, code_${c.uid}); - `); + ;; Contract Code: ${t.name} + cell init_code = ${ctx.used("__tact_dict_get_code")}(source, ${t.uid}); + `); + ctx.append(); + ctx.append(";; Build init data cell"); + if (t.dependsOn.length > 0) { + ctx.write(` + cell contracts = new_dict(); + `); + } + // Copy contracts code + for (const c of t.dependsOn) { + ctx.append(); + ctx.append(`;; Contract Code: ${c.name}`); + if (c.name === ctx.name) { + ctx.append( + `contracts = ${ctx.used("__tact_dict_set_code")}(contracts, ${c.uid}, my_code());`, + ); + } else { + ctx.write(` + cell code_${c.uid} = ${ctx.used("__tact_dict_get_code")}(source, ${c.uid}); + contracts = ${ctx.used("__tact_dict_set_code")}(contracts, ${c.uid}, code_${c.uid}); + `); + } + } + ctx.append(); + ctx.append("builder b = begin_cell();"); + if (t.dependsOn.length > 0) { + ctx.append( + `b = b.store_ref(begin_cell().store_dict(contracts).end_cell());`, + ); + } } - // Build cell - ctx.append(); - ctx.append(`;; Build cell`); - ctx.append(`builder b = begin_cell();`); - ctx.append( - `b = b.store_ref(begin_cell().store_dict(contracts).end_cell());`, - ); + // store initialization bit and contract variables ctx.append(`b = b.store_int(false, 1);`); const args = t.init!.params.length > 0 @@ -211,7 +242,7 @@ export function writeInit( ctx.append( `b = ${ops.writer(funcInitIdOf(t.name), ctx)}(${args});`, ); - ctx.append(`return (mine, b.end_cell());`); + ctx.append(`return (init_code, b.end_cell());`); }); }); } diff --git a/src/generator/writers/writeExpression.ts b/src/generator/writers/writeExpression.ts index 819047865..cc5340e3d 100644 --- a/src/generator/writers/writeExpression.ts +++ b/src/generator/writers/writeExpression.ts @@ -672,7 +672,10 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { if (f.kind === "init_of") { const type = getType(wCtx.ctx, f.contract); - return `${ops.contractInitChild(idText(f.contract), wCtx)}(${["__tact_context_sys", ...f.args.map((a, i) => writeCastedExpression(a, type.init!.params[i]!.type, wCtx))].join(", ")})`; + const initArgs = f.args.map((a, i) => + writeCastedExpression(a, type.init!.params[i]!.type, wCtx), + ); + return `${ops.contractInitChild(idText(f.contract), wCtx)}(${initArgs.join(", ")})`; } // diff --git a/src/packaging/fileFormat.ts b/src/packaging/fileFormat.ts index dc976a3bb..90b3d0a57 100644 --- a/src/packaging/fileFormat.ts +++ b/src/packaging/fileFormat.ts @@ -49,7 +49,7 @@ const initFormat = z.object({ }), z.object({ kind: z.literal("system-cell"), - system: z.string(), + system: z.string().nullable(), }), ]), }); diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index 0188a7068..fc1d8dd1a 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -262,7 +262,6 @@ export async function build(args: { Dictionary.Values.Cell(), ); const ct = getType(ctx, contract); - depends.set(ct.uid, Cell.fromBoc(built[ct.name]!.codeBoc)[0]!); // Mine for (const c of ct.dependsOn) { const cd = built[c.name]; if (!cd) { @@ -273,7 +272,10 @@ export async function build(args: { } depends.set(c.uid, Cell.fromBoc(cd.codeBoc)[0]!); } - const systemCell = beginCell().storeDict(depends).endCell(); + const systemCell = + ct.dependsOn.length > 0 + ? beginCell().storeDict(depends).endCell() + : null; // Collect sources const sources: Record = {}; @@ -309,7 +311,7 @@ export async function build(args: { }, deployment: { kind: "system-cell", - system: systemCell.toBoc().toString("base64"), + system: systemCell?.toBoc().toString("base64") ?? null, }, }, sources, diff --git a/src/test/e2e-emulated/asm-functions.spec.ts b/src/test/e2e-emulated/asm-functions.spec.ts index c67084b51..81c63f613 100644 --- a/src/test/e2e-emulated/asm-functions.spec.ts +++ b/src/test/e2e-emulated/asm-functions.spec.ts @@ -1,4 +1,4 @@ -import { toNano } from "@ton/core"; +import { beginCell, toNano } from "@ton/core"; import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox"; import { AsmFunctionsTester as TestContract } from "./contracts/output/asm-functions_AsmFunctionsTester"; import "@ton/test-utils"; @@ -32,8 +32,43 @@ describe("asm functions", () => { it("should implement asm functions correctly", async () => { expect(await contract.getTestAsmStoreDict()).toEqual(true); expect(await contract.getTestAsmLoadCoins()).toEqual(true); + expect(await contract.getTestAsmLoadCoinsMut()).toEqual(true); + expect( + await contract.getTestAsmLoadCoinsMutRuntime( + beginCell().storeCoins(42n).endCell(), + ), + ).toEqual(42n); expect(await contract.getTestAsmLoadInt()).toEqual(true); expect(await contract.getTestAsmDebugStr()).toEqual(true); expect(await contract.getTestAsmCreateUseWord()).toEqual(true); + + // Struct arrangements + expect(await contract.getTestAsmSecondToLast()).toEqual(true); + expect( + await contract.getTestAsmSecondToLastRuntime( + { $$type: "Two", a: 1n, b: 2n }, + { $$type: "Two", a: 3n, b: 4n }, + ), + ).toEqual(3n); + expect(await contract.getTestAsmFirst()).toEqual(true); + expect( + await contract.getTestAsmFirstRuntime( + { + $$type: "TwoInTwo", + a: { $$type: "Two", a: 1n, b: 2n }, + b: { $$type: "Two", a: 3n, b: 4n }, + }, + { + $$type: "TwoInTwo", + a: { $$type: "Two", a: 5n, b: 6n }, + b: { $$type: "Two", a: 7n, b: 8n }, + }, + { + $$type: "TwoInTwo", + a: { $$type: "Two", a: 9n, b: 10n }, + b: { $$type: "Two", a: 11n, b: 12n }, + }, + ), + ).toEqual(1n); }); }); diff --git a/src/test/e2e-emulated/contracts/asm-functions.tact b/src/test/e2e-emulated/contracts/asm-functions.tact index 698c1ae2f..9360e7e32 100644 --- a/src/test/e2e-emulated/contracts/asm-functions.tact +++ b/src/test/e2e-emulated/contracts/asm-functions.tact @@ -18,6 +18,19 @@ contract AsmFunctionsTester { return s.asmLoadCoins().val == 42; } + get fun testAsmLoadCoinsMut(): Bool { + let s = beginCell().storeCoins(42).asSlice(); + return s.asmLoadCoinsMut() == 42 && s.empty(); + } + + // asmLoadCoinsMut(), but with data supplied at runtime + get fun testAsmLoadCoinsMutRuntime(c: Cell): Int { + let s = c.asSlice(); + let res = s.asmLoadCoinsMut(); + s.endParse(); // like .empty(), but throws on failure + return res; + } + get fun testAsmLoadInt(): Bool { let s = beginCell().storeInt(42, 7).asSlice(); return s.asmLoadInt(7).val == 42; @@ -31,6 +44,28 @@ contract AsmFunctionsTester { get fun testAsmCreateUseWord(): Bool { return asmCreateUseWord(6) == 7; } + + get fun testAsmSecondToLast(): Bool { + return asmSecondToLast(Two{ a: 1, b: 2 }, Two{ a: 3, b: 4 }) == 3; + } + + // asmSecondToLast(), but with data supplied at runtime + get fun testAsmSecondToLastRuntime(s1: Two, s2: Two): Int { + return asmSecondToLast(s1, s2); + } + + get fun testAsmFirst(): Bool { + return asmFirst( + TwoInTwo{ a: Two{ a: 1, b: 2}, b: Two{ a: 3, b: 4 } }, + TwoInTwo{ a: Two{ a: 5, b: 6}, b: Two{ a: 7, b: 8 } }, + TwoInTwo{ a: Two{ a: 9, b: 10}, b: Two{ a: 11, b: 12 } }, + ) == 1; + } + + // asmFirst(), but with data supplied at runtime + get fun testAsmFirstRuntime(s1: TwoInTwo, s2: TwoInTwo, s3: TwoInTwo): Int { + return asmFirst(s1, s2, s3); + } } // Functions to test @@ -41,8 +76,14 @@ asm extends fun asmLoadMapIntInt(self: Slice): MapIntIntSlice { LDDICT } asm extends fun asmLoadCoins(self: Slice): IntSlice { LDVARUINT16 } +asm(-> 1 0) extends mutates fun asmLoadCoinsMut(self: Slice): Int { LDVARUINT16 } + asm(self len -> 1 0) extends fun asmLoadInt(self: Slice, len: Int): SliceInt { LDIX } +asm(b a) fun asmSecondToLast(a: Two, b: Two): Int { DROP DROP DROP } + +asm(a c b) fun asmFirst(a: TwoInTwo, b: TwoInTwo, c: TwoInTwo): Int { DROP2 DROP2 DROP2 DROP2 DROP2 DROP } + asm fun asmDebugStr() { "Works!" DEBUGSTR } asm fun asmCreateUseWord(x: Int): Int { @@ -67,3 +108,13 @@ struct SliceInt { rem: Slice; val: Int; } + +struct Two { + a: Int; + b: Int; +} + +struct TwoInTwo { + a: Two; + b: Two; +} diff --git a/src/test/e2e-emulated/stdlib.spec.ts b/src/test/e2e-emulated/stdlib.spec.ts index 440d82c13..8a39ca4fc 100644 --- a/src/test/e2e-emulated/stdlib.spec.ts +++ b/src/test/e2e-emulated/stdlib.spec.ts @@ -56,7 +56,7 @@ describe("stdlib", () => { .toString(), ).toBe(beginCell().storeBit(true).endCell().toString()); - expect(await contract.getTvm_2023_07Upgrade()).toEqual(1355n); + expect(await contract.getTvm_2023_07Upgrade()).toEqual(1289n); // gas consumed expect(await contract.getTvm_2024_04Upgrade()).toEqual(82009144n); expect( diff --git a/yarn.lock b/yarn.lock index b5824478b..68198260e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1304,10 +1304,10 @@ resolved "https://registry.npmjs.org/@tact-lang/ton-jest/-/ton-jest-0.0.4.tgz" integrity sha512-FWjfiNvhMlE44ZLLL7tgmHbrszMTPMttmYiaTekf1vwFXV3uAOawM8xM9NldYaCVs9eh8840PjgISdMMUTCSCw== -"@ton/core@0.59.0": - version "0.59.0" - resolved "https://registry.npmjs.org/@ton/core/-/core-0.59.0.tgz#58da9fcaa58e5a0c705b63baf1e86cab6e196689" - integrity sha512-LSIkGst7BoY7fMWshejzcH0UJnoW21JGlRrW0ch+6A7Xb/7EuekxgdKym7fHxcry6OIf6FoeFg97lJ960N/Ghg== +"@ton/core@0.59.1": + version "0.59.1" + resolved "https://registry.npmjs.org/@ton/core/-/core-0.59.1.tgz#fe568069ccdf2f4191da1eadc930076c0c012789" + integrity sha512-SxFBAvutYJaIllTkv82vbHTJhJI6NxzqUhi499CDEjJEZ9i6i9lHJiK2df4dlLAb/4SiWX6+QUzESkK4DEdnCw== dependencies: symbol.inspect "1.0.1" @@ -1327,10 +1327,10 @@ jssha "3.2.0" tweetnacl "1.0.3" -"@ton/sandbox@^0.22.0": - version "0.22.0" - resolved "https://registry.npmjs.org/@ton/sandbox/-/sandbox-0.22.0.tgz#76a3b724f40be18512df748deee6c5d5ff0a96fb" - integrity sha512-ilvKtyAGxJyEbIwITlx1y78oDmJyvT+pkLfVm4952NjxZpANdG2nZ8/mBGJlWph9FZtxxXCjCf/79hNihC6P6A== +"@ton/sandbox@^0.23.0": + version "0.23.0" + resolved "https://registry.npmjs.org/@ton/sandbox/-/sandbox-0.23.0.tgz#c3b6ee0b9c057650a32d0c9ebf770a77e0832421" + integrity sha512-V0PfjefTJbpBvsE1b5851LyuZlOXuBZnvxtCKrvK/wDlw348dCnUJuBvWuApZ5RGXq36xJ07XBoYl6sMuS0Xug== "@ton/test-utils@^0.4.2": version "0.4.2" @@ -1579,13 +1579,18 @@ acorn@^8.4.1, acorn@^8.9.0: resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: +agent-base@^7.1.0: version "7.1.1" resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz" integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== dependencies: debug "^4.3.4" +agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" @@ -1991,10 +1996,10 @@ chalk-template@^1.1.0: dependencies: chalk "^5.2.0" -chalk@5.3.0, chalk@^5.2.0, chalk@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" - integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== +chalk@5.4.1, chalk@^5.2.0, chalk@^5.3.0: + version "5.4.1" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== chalk@^2.4.2: version "2.4.2" @@ -2046,10 +2051,10 @@ ci-info@^3.2.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -ci-info@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" - integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== +ci-info@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83" + integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== cjs-module-lexer@^1.0.0: version "1.2.3" @@ -3129,12 +3134,12 @@ http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: agent-base "^7.1.0" debug "^4.3.4" -https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5: - version "7.0.5" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" - integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== +https-proxy-agent@^7.0.6: + version "7.0.6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: - agent-base "^7.0.2" + agent-base "^7.1.2" debug "4" human-signals@^2.1.0: @@ -4009,9 +4014,9 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knip@^5.24.1: - version "5.41.0" - resolved "https://registry.npmjs.org/knip/-/knip-5.41.0.tgz#c186d27df55d10fe38ce69ed66fcb0c7a44bae1a" - integrity sha512-W8omBs+jhC/P/A3kC0xdEGrhYVmsmWafUVz0wzQjYfoaK0YNEBPPLptUeqwQl6ihYVqzb/X0zs50gY9Akw1Bww== + version "5.41.1" + resolved "https://registry.npmjs.org/knip/-/knip-5.41.1.tgz#b6e27186d38e6bccd2ef8346294e78d13322f1cd" + integrity sha512-yNpCCe2REU7U3VRvMASnXSEtfEC2HmOoDW9Vp9teQ9FktJYnuagvSZD3xWq8Ru7sPABkmvbC5TVWuMzIaeADNA== dependencies: "@nodelib/fs.walk" "1.2.8" "@snyk/github-codeowners" "1.1.0" @@ -4429,10 +4434,10 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -ora@8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/ora/-/ora-8.1.0.tgz#c3db2f9f83a2bec9e8ab71fe3b9ae234d65ca3a8" - integrity sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ== +ora@8.1.1: + version "8.1.1" + resolved "https://registry.npmjs.org/ora/-/ora-8.1.1.tgz#8efc8865e44c87e4b55468a47e80a03e678b0e54" + integrity sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw== dependencies: chalk "^5.3.0" cli-cursor "^5.0.0" @@ -4512,19 +4517,19 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^7.0.1: - version "7.0.2" - resolved "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz#0fb02496bd9fb8ae7eb11cfd98386daaac442f58" - integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg== +pac-proxy-agent@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz#da7c3b5c4cccc6655aaafb701ae140fb23f15df2" + integrity sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw== dependencies: "@tootallnate/quickjs-emscripten" "^0.23.0" - agent-base "^7.0.2" + agent-base "^7.1.2" debug "^4.3.4" get-uri "^6.0.1" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.5" + https-proxy-agent "^7.0.6" pac-resolver "^7.0.1" - socks-proxy-agent "^8.0.4" + socks-proxy-agent "^8.0.5" pac-resolver@^7.0.1: version "7.0.1" @@ -4759,19 +4764,19 @@ protocols@^2.0.0, protocols@^2.0.1: resolved "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== -proxy-agent@6.4.0: - version "6.4.0" - resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" - integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== +proxy-agent@6.5.0: + version "6.5.0" + resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz#9e49acba8e4ee234aacb539f89ed9c23d02f232d" + integrity sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A== dependencies: - agent-base "^7.0.2" + agent-base "^7.1.2" debug "^4.3.4" http-proxy-agent "^7.0.1" - https-proxy-agent "^7.0.3" + https-proxy-agent "^7.0.6" lru-cache "^7.14.1" - pac-proxy-agent "^7.0.1" + pac-proxy-agent "^7.1.0" proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.2" + socks-proxy-agent "^8.0.5" proxy-from-env@^1.1.0: version "1.1.0" @@ -4858,15 +4863,15 @@ registry-url@^6.0.1: rc "1.2.8" release-it@^17.6.0: - version "17.10.0" - resolved "https://registry.npmjs.org/release-it/-/release-it-17.10.0.tgz#be44422de7b8d752a97e96da898fc6f2ad7d45d5" - integrity sha512-00cXYEl7RFD5NnjXpwaH9JFjpwe8w3NcfUd4XPxrKQkszp1xppPo42zK9eSbxStKyPA5CVk2KmKPDPDiAKVJTA== + version "17.11.0" + resolved "https://registry.npmjs.org/release-it/-/release-it-17.11.0.tgz#4cb858194f45fdc1966b8a53209efff887d1994e" + integrity sha512-qQGgfMbUZ3/vpXUPmngsgjFObOLjlkwtiozHUYen9fo9AEGciXjG1ZpGr+FNmuBT8R7TOSY+x/s84wOCRKJjbA== dependencies: "@iarna/toml" "2.2.5" "@octokit/rest" "20.1.1" async-retry "1.3.3" - chalk "5.3.0" - ci-info "^4.0.0" + chalk "5.4.1" + ci-info "^4.1.0" cosmiconfig "9.0.0" execa "8.0.0" git-url-parse "14.0.0" @@ -4877,14 +4882,14 @@ release-it@^17.6.0: mime-types "2.1.35" new-github-release-url "2.0.0" open "10.1.0" - ora "8.1.0" + ora "8.1.1" os-name "5.1.0" - proxy-agent "6.4.0" + proxy-agent "6.5.0" semver "7.6.3" shelljs "0.8.5" update-notifier "7.3.1" url-join "5.0.0" - wildcard-match "5.1.3" + wildcard-match "5.1.4" yargs-parser "21.1.1" repeat-string@^1.6.1: @@ -5090,12 +5095,12 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.4: - version "8.0.4" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" - integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== +socks-proxy-agent@^8.0.5: + version "8.0.5" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee" + integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== dependencies: - agent-base "^7.1.1" + agent-base "^7.1.2" debug "^4.3.4" socks "^2.8.3" @@ -5569,10 +5574,10 @@ widest-line@^5.0.0: dependencies: string-width "^7.0.0" -wildcard-match@5.1.3: - version "5.1.3" - resolved "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.3.tgz#7420e57a17307afed9e51eee36aa1dcc1b73ce11" - integrity sha512-a95hPUk+BNzSGLntNXYxsjz2Hooi5oL7xOfJR6CKwSsSALh7vUNuTlzsrZowtYy38JNduYFRVhFv19ocqNOZlg== +wildcard-match@5.1.4: + version "5.1.4" + resolved "https://registry.npmjs.org/wildcard-match/-/wildcard-match-5.1.4.tgz#26428c802f20743ebae255e4e9526ae81ddf1816" + integrity sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g== windows-release@^5.0.1: version "5.1.1"