diff --git a/tvm/exit-codes.mdx b/tvm/exit-codes.mdx index 4d8258e23..022492fab 100644 --- a/tvm/exit-codes.mdx +++ b/tvm/exit-codes.mdx @@ -125,17 +125,18 @@ This is an alternative exit code for the successful execution of the [compute ph If an operation consumes more elements than exist on the stack, an error with exit code 2 is thrown: `Stack underflow`. -```tact title="Tact" -asm fun drop() { DROP } - -contract Loot { - receive("I solemnly swear that I'm up to no good") { - try { - // Removes 100 elements from the stack, causing an underflow - repeat (100) { drop() } - } catch (exitCode) { - // exitCode is 2 +```tolk title="Tolk" +fun drop(): void asm "DROP" + +fun onInternalMessage() { + try { + // Removes 100 elements from the stack, causing an underflow + repeat(100){ + drop(); } + } catch(exitCode) { + // exitCode is 2 + assert (exitCode == 2) throw 1111; } } ``` @@ -150,24 +151,24 @@ contract Loot { If there are too many elements copied into a closure continuation, an error with exit code 3 is thrown: `Stack overflow`. This occurs rarely unless you're deep in the [Fift and TVM assembly](/languages/fift/fift-and-tvm-assembly) trenches: -```tact title="Tact" +```tolk title="Tolk" // Remember kids, don't try to overflow the stack at home! -asm fun stackOverflow() { +fun stackOverflow(): void asm +""" x{} SLICE // s BLESS // c 0 SETNUMARGS // c' 2 PUSHINT // c' 2 SWAP // 2 c' 1 -1 SETCONTARGS // ← this blows up -} - -contract ItsSoOver { - receive("I solemnly swear that I'm up to no good") { - try { - stackOverflow(); - } catch (exitCode) { - // exitCode is 3 - } +""" + +fun onInternalMessage() { + try { + stackOverflow(); + } catch(exitCode) { + // exitCode is 3 + assert (exitCode == 3) throw 1111; } } ``` @@ -182,32 +183,41 @@ contract ItsSoOver { If the value in a calculation goes beyond the range from $-2^{256}$ to $2^{256} - 1$ inclusive, or there's an attempt to divide or perform modulo by zero, an error with exit code 4 is thrown: `Integer overflow`. -```tact title="Tact" -let x = -pow(2, 255) - pow(2, 255); // -2^{256} - -try { - -x; // integer overflow by negation - // since the max positive value is 2^{256} - 1 -} catch (exitCode) { - // exitCode is 4 -} +```tolk title="Tolk" +fun touch(y: T): void asm "NOP" // so that the compiler doesn't remove instructions +fun pow2(y: int): int asm "POW2" + +fun onInternalMessage() { + var x = -pow2(255) - pow2(255); + var zero = x - x; + + try { + touch(-x); // integer overflow by negation + // since the max positive value is 2^{256} - 1 + } catch(exitCode) { + // exitCode is 4 + assert (exitCode == 4) throw 1111; + } + + try { + touch(x / zero); // division by zero! + } catch (exitCode) { + // exitCode is 4 + assert (exitCode == 4) throw 1111; + } -try { - x / 0; // division by zero! -} catch (exitCode) { - // exitCode is 4 -} + try { + touch(x * x * x); // integer overflow! + } catch (exitCode) { + // exitCode is 4 + assert (exitCode == 4) throw 1111; + } -try { - x * x * x; // integer overflow! -} catch (exitCode) { - // exitCode is 4 + // There can also be an integer overflow when performing: + // addition (+), + // subtraction (-), + // division (/) by a negative number or modulo (%) by zero } - -// There can also be an integer overflow when performing: -// addition (+), -// subtraction (-), -// division (/) by a negative number or modulo (%) by zero ``` ### 5: Integer out of expected range @@ -216,40 +226,79 @@ A range check error occurs when some integer is out of its expected range. Any a Examples of specifying an out-of-bounds value: -```tact title="Tact" -try { - // Repeat only operates on an inclusive range from 1 to 2^{31} - 1 - // Any valid integer value greater than that causes an error with exit code 5 - repeat (pow(2, 55)) { - dump("smash. logs. I. must."); +```tolk title="Tolk" +fun touch(y: T): void asm "NOP" // so that the compiler doesn't remove instructions +fun pow2(y: int): int asm "POW2" + +fun onInternalMessage() { + try { + // Repeat only operates on an inclusive range from 1 to 2^{31} - 1 + // Any valid integer value greater than that causes an error with exit code 5 + repeat (pow2(55)) { + touch("smash. I. must."); + } + } catch(exitCode) { + // exitCode is 5 + assert (exitCode == 5) throw 1111; + } + + try { + // Builder.storeUint() function can only use up to 256 bits, thus 512 is too much: + touch(beginCell().storeUint(-1, 512).toCell()); + } catch (exitCode) { + // exitCode is 5 + assert (exitCode == 5) throw 1111; } -} catch (exitCode) { - // exitCode is 5 -} -try { - // Builder.storeUint() function can only use up to 256 bits, thus 512 is too much: - let s: Slice = beginCell().storeUint(-1, 512).asSlice(); -} catch (exitCode) { - // exitCode is 5 + try { + touch(beginCell().storeUint(100, 2).toCell()); // maximum value is 2^{2} - 1 = 3 < 100 + } + catch(exitCode) { + // exitCode is 5 + assert (exitCode == 5) throw 1111; + } + + try { + val deployMsg = createMessage({ + bounce: false, + dest: { + workchain: 0, + stateInit: { + code: beginCell().endCell(), + data: beginCell().endCell(), + }, + toShard: { + fixedPrefixLength: pow2(52), // but fixedPrefixLength is uint5 + closeTo: contract.getAddress() + }, + }, + value: 1, + body: beginCell().endCell(), + }); + + deployMsg.send(SEND_MODE_PAY_FEES_SEPARATELY); + } catch (exitCode) { + // exitCode is 5 + assert (exitCode == 5) throw 1111; + } } + ``` ### 6: Invalid opcode If you specify an instruction that is not defined in the current [TVM][tvm] version or attempt to set an unsupported [code page][tvm], an error with exit code 6 is thrown: `Invalid opcode`. -```tact title="Tact" +```tolk title="Tolk" // There's no such code page, and an attempt to set it fails -asm fun invalidOpcode() { 42 SETCP } - -contract OpOp { - receive("I solemnly swear that I'm up to no good") { - try { - invalidOpcode(); - } catch (exitCode) { - // exitCode is 6 - } +fun invalidOpcode(): void asm "42 SETCP" + +fun onInternalMessage() { + try { + invalidOpcode(); + } catch (exitCode) { + // exitCode is 6 + assert (exitCode == 6) throw 1111; } } ``` @@ -258,18 +307,18 @@ contract OpOp { If an argument to a primitive is of an incorrect value type or there is any other mismatch in types during the [compute phase](#compute), an error with exit code 7 is thrown: `Type check error`. -```tact title="Tact" +```tolk title="Tolk" +fun touch(y: T): void asm "NOP" // so that the compiler doesn't remove instructions // The actual returned value type doesn't match the declared one -asm fun typeCheckError(): map { 42 PUSHINT } - -contract VibeCheck { - receive("I solemnly swear that I'm up to no good") { - try { - // The 0th index doesn't exist - typeCheckError().get(0)!!; - } catch (exitCode) { - // exitCode is 7 - } +fun typeCheckError(): cell asm "42 PUSHINT"; + +fun onInternalMessage() { + try { + // it isn't cell + touch(typeCheckError().beginParse()); + } catch (exitCode) { + // exitCode is 7 + assert (exitCode == 7) throw 1111; } } ``` @@ -280,31 +329,39 @@ To construct a `cell`, a `builder` primitive is used. If you try to store more t This error can be triggered by manual construction of the cells via relevant methods, such as `storeInt()`, or when using structs, their convenience methods. -```tact title="Tact" -// Too many bits -try { - let data = beginCell() - .storeInt(0, 250) - .storeInt(0, 250) - .storeInt(0, 250) - .storeInt(0, 250) - .storeInt(0, 24) // 1024 bits! - .endCell(); -} catch (exitCode) { - // exitCode is 8 -} +```tolk title="Tolk" +fun touch(y: T): void asm "NOP" // so that the compiler doesn't remove instructions + +fun onInternalMessage() { + // Too many bits + try { + val data = beginCell() + .storeInt(0, 250) + .storeInt(0, 250) + .storeInt(0, 250) + .storeInt(0, 250) + .storeInt(0, 24) // 1024 bits! + .toCell(); + touch(data); + } catch (exitCode) { + // exitCode is 8 + assert (exitCode == 8) throw 1111; + } -// Too many refs -try { - let data = beginCell() - .storeRef(emptyCell()) - .storeRef(emptyCell()) - .storeRef(emptyCell()) - .storeRef(emptyCell()) - .storeRef(emptyCell()) // 5 refs! - .endCell(); -} catch (exitCode) { - // exitCode is 8 + // Too many refs + try { + val data = beginCell() + .storeRef(beginCell().endCell()) + .storeRef(beginCell().endCell()) + .storeRef(beginCell().endCell()) + .storeRef(beginCell().endCell()) + .storeRef(beginCell().endCell()) // 5 refs! + .toCell(); + touch(data); + } catch (exitCode) { + // exitCode is 8 + assert (exitCode == 8) throw 1111; + } } ``` @@ -314,19 +371,25 @@ To parse a `cell`, a `slice` primitive is used. If you try to load more data or The most common cause of this error is a mismatch between the expected and actual memory layouts of the cells, so it's recommended to use Tolk structs for parsing the cells instead of manual parsing via relevant methods, such as `loadInt()`. -```tact title="Tact" -// Too few bits -try { - emptySlice().loadInt(1); // 0 bits! -} catch (exitCode) { - // exitCode is 9 -} +```tolk title="Tolk" +fun touch(y: T): void asm "NOP" // so that the compiler doesn't remove instructions -// Too few refs -try { - emptySlice().loadRef(); // 0 refs! -} catch (exitCode) { - // exitCode is 9 +fun onInternalMessage() { + // Too few bits + try { + touch(beginCell().endCell().beginParse().loadInt(1)); // 0 bits! + } catch (exitCode) { + // exitCode is 9 + assert (exitCode == 9) throw 1111; + } + + // Too few refs + try { + touch(beginCell().endCell().beginParse().loadRef()); // 0 refs! + } catch (exitCode) { + // exitCode is 9 + assert (exitCode == 9) throw 1111; + } } ``` @@ -336,24 +399,29 @@ In Tolk, the `map` type is an abstraction over the ["hash" map dictionarie If there is incorrect manipulation of dictionaries, such as improper assumptions about their memory layout, an error with exit code 10 is thrown: `Dictionary error`. Note that Tolk prevents you from getting this error unless you perform [TVM assembly](/languages/fift/fift-and-tvm-assembly) work yourself: -```tact title="Tact" -/// Pre-computed Int to Int dictionary with two entries — 0: 0 and 1: 1 -const cellWithDictIntInt: Cell = cell("te6cckEBBAEAUAABAcABAgPQCAIDAEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMLMbT1U="); - -/// Tries to preload a dictionary from a Slice as a map -asm fun toMapIntCell(x: Slice): map { PLDDICT } +```tolk title="Tolk" +import "@stdlib/tvm-dicts"; -contract DictPic { - receive("I solemnly swear that I'm up to no good") { - try { - // The Int to Int dictionary is being misinterpreted as a map - let m: map = toMapIntCell(cellWithDictIntInt.beginParse()); +fun touch(y: T): void asm "NOP" // so that the compiler doesn't remove instructions +fun cast(y: T): U asm "NOP" +fun cell?.addIntToIDict(mutate self, key: int, number: int): void { + return self.iDictSetBuilder(32, key, beginCell().storeInt(number, 32)); +} - // And the error happens only when we touch it - m.get(0)!!; - } catch (exitCode) { - // exitCode is 10 - } +fun onInternalMessage() { + var dict = createEmptyDict(); + dict.addIntToIDict(0, 0); + dict.addIntToIDict(1, 1); + + // The Int to Int dictionary is being misinterpreted as a map + val m: map = cast(dict); + + try { + // And the error happens only when we touch it + touch(m.get(0).isFound); + } catch (exitCode) { + // exitCode is 10 + assert (exitCode == 10) throw 1111; } } ``` @@ -364,13 +432,17 @@ Described in the [TVM][tvm] docs as "Unknown error, may be thrown by user progra In particular, if you try to send an ill-formed message on-chain or to call a non-existent getter function off-chain, an exit code 11 will be thrown. -```tact title="Tact" -try { - // Unlike sendRawMessage, which uses SENDRAWMSG, this one uses SENDMSG, - // and therefore fails in the Compute phase when the message is ill-formed - sendRawMessageReturnForwardFee(emptyCell(), 0); -} catch (exitCode) { - // exitCode is 11 +```tolk title="Tolk" +fun sendMessage(msg: cell, mode: int): void asm "SENDMSG" + +fun onInternalMessage() { + try { + // fails in the Compute phase when the message is ill-formed + sendMessage(beginCell().endCell(), 0); + } catch (exitCode) { + // exitCode is 11 + assert (exitCode == 11) throw 1111; + } } ``` @@ -386,11 +458,11 @@ However, this code isn't immediately shown as is — instead, the bitwise NOT op This is done to prevent the resulting code (-14) from being produced artificially in user contracts, as all functions that can throw an exit code can only specify integers in the range from 0 to 65535 inclusive. -```tact title="Tact" -try { - repeat (pow(2, 31) - 1) {} -} catch (exitCode) { - // exitCode is -14 +```tolk title="Tolk" +import "@stdlib/gas-payments"; + +fun onInternalMessage() { + setGasLimit(100); } ``` @@ -420,11 +492,15 @@ If the list of actions contains exotic cells, an action entry cell does not have If there are more than 255 actions queued for execution, the [action phase](#action) will throw an error with an exit code 33: `Action list is too long`. -```tact title="Tact" -// For example, let's attempt to queue the reservation of a specific amount of nanoToncoins -// This won't fail in the compute phase, but will result in exit code 33 in the action phase -repeat (256) { - nativeReserve(ton("0.001"), ReserveAtMost); +```tolk title="Tolk" +import "@stdlib/gas-payments"; + +fun onInternalMessage() { + // For example, let's attempt to queue the reservation of a specific amount of nanoToncoins + // This won't fail in the compute phase, but will result in exit code 33 in the action phase + repeat (256) { + reserveToncoinsOnBalance(1000000, RESERVE_MODE_AT_MOST); + } } ``` @@ -432,10 +508,12 @@ repeat (256) { There are only four supported actions at the moment: changing the contract code, sending a message, reserving a specific amount of nanoToncoin, and changing the library cell. If there is any issue with the specified action (invalid message, unsupported action, etc.), an error with exit code 34 is thrown: `Invalid or unsupported action`. -```tact title="Tact" -// For example, let's try to send an ill-formed message: -sendRawMessage(emptyCell(), 0); // won't fail in the compute phase, - // but will result in exit code 34 in the Action phase +```tolk title="Tolk" +fun onInternalMessage() { + // For example, let's try to send an ill-formed message: + // won't fail in the compute phase, but will result in exit code 34 in the Action phase + sendRawMessage(beginCell().endCell(), 0); +} ``` ### 35: Invalid source address in outbound message