TVM Solidity compiler expands Solidity language with different API functions to facilitate TVM contract development.
When deploying contracts, you should use the latest released version of Solidity. Apart from exceptional cases, only the latest version receives security fixes. Furthermore, breaking changes, as well as new features, are introduced regularly. We currently use a 0.y.z version number to indicate this fast pace of change.
- Compiler version
- TVM specific types
- TVM units
- TvmCell
- TvmSlice
- <TvmSlice>.empty(), <TvmSlice>.bitEmpty() and <TvmSlice>.refEmpty()
- <TvmSlice>.size()
- <TvmSlice>.bits()
- <TvmSlice>.refs()
- <TvmSlice>.dataSize()
- <TvmSlice>.dataSizeQ()
- <TvmSlice>.depth()
- <TvmSlice>.hasNBits(), <TvmSlice>.hasNRefs() and <TvmSlice>.hasNBitsAndRefs()
- <TvmSlice>.compare()
- <TvmSlice>.startsWith()
- <TvmSlice>.startsWithOne()
- TvmSlice load primitives
- <TvmSlice>.load()
- <TvmSlice>.loadQ()
- <TvmSlice>.loadRef()
- <TvmSlice>.loadRefAsSlice()
- <TvmSlice>.loadInt() and <TvmSlice>.loadIntQ()
- <TvmSlice>.loadUint() and <TvmSlice>.loadUintQ()
- Load little-endian integers
- <TvmSlice>.loadSlice() and <TvmSlice>.loadSliceQ()
- <TvmSlice>.skip()
- <TvmSlice>.loadZeroes(), <TvmSlice>.loadOnes() and <TvmSlice>.loadSame()
- TvmSlice preload primitives
- TvmBuilder
- <TvmBuilder>.toSlice()
- <TvmBuilder>.toCell()
- <TvmBuilder>.toExoticCell()
- <TvmBuilder>.size()
- <TvmBuilder>.bits()
- <TvmBuilder>.refs()
- <TvmBuilder>.remBits()
- <TvmBuilder>.remRefs()
- <TvmBuilder>.remBitsAndRefs()
- <TvmBuilder>.depth()
- <TvmBuilder>.store()
- <TvmBuilder>.storeQ()
- <TvmBuilder>.storeZeroes(), <TvmBuilder>.storeOnes() and <TvmBuilder>.storeSame()
- <TvmBuilder>.storeInt()
- <TvmBuilder>.storeUint()
- Store little-endian integers
- <TvmBuilder>.storeRef()
- StringBuilder
- optional(T)
- variant
- vector(T)
- stack(T)
- TVM specific control structures
- Changes and extensions in Solidity types
- Integers
- Quiet arithmetic
- varint and varuint
- struct
- Arrays
- bytesN
- bytes
- string
- address
- mapping
- Keyword
emptyMap
- <mapping>.operator[]
- <mapping>.at()
- <mapping>.min() and <mapping>.max()
- <mapping>.next() and <mapping>.prev()
- <mapping>.nextOrEq() and <mapping>.prevOrEq()
- <mapping>.delMin() and <mapping>.delMax()
- <mapping>.fetch()
- <mapping>.exists()
- <mapping>.empty()
- <mapping>.replace()
- <mapping>.add()
- <mapping>.getSet()
- <mapping>.getAdd()
- <mapping>.getDel()
- <mapping>.getReplace()
- <mapping>.keys() <mapping>.values()
- Keyword
- Fixed point number
- Function type
- User-defined type
- require, revert
- Libraries
- Pragmas
- State variables
- Special contract functions
- Function specifiers
- Events and return
- External function calls
- Delete variables
- API functions and members
- Type information
- msg namespace
- tvm namespace
- bls namespace
- bls.verify()
- bls.aggregate()
- bls.fastAggregateVerify()
- bls.aggregateVerify()
- bls.g1Zero() and bls.g2Zero()
- bls.g1IsZero() and bls.g2IsZero()
- bls.g1Add() and bls.g2Add()
- bls.g1Sub() and bls.g2Sub()
- bls.g1Neg() and bls.g2Neg()
- bls.g1Mul() and bls.g2Mul()
- bls.g1InGroup() and bls.g2InGroup()
- bls.r()
- bls.g1MultiExp() and bls.g2MultiExp()
- math namespace
- tx namespace
- block namespace
- rnd namespace
- abi namespace
- gosh namespace
- Exponentiation
- selfdestruct
- sha256
- gasToValue
- valueToGas
- gasleft
- TVM capabilities
- TVM exception codes
- Solidity runtime errors
- Division and rounding
- Contract execution
- Gas optimization hints
TVM Solidity compiler adds its current version to the generated code. This version can be obtained:
-
using tvm_linker from a
*.tvc
file:tvm_linker decode --tvm <tvc-file>
-
using ever-cli from a
*.boc
file, a*.tvc
file, or a network account:
ever-cli decode tvc [--tvc] [--boc] <input>
TVM Solidity compiler expands functionality of some existing types and adds several new TVM specific types: TvmCell, TvmSlice and TvmBuilder. Full description of these types can be found in TVM and Blockchain specifications.
A literal number can take a suffix to specify a subdenomination of currency, where numbers without a postfix are assumed to be nanoevers.
uint a0 = 1 nano; // a0 == 1 == 1e-9 ever
uint a1 = 1 nanoever; // a1 == 1 == 1e-9 ever
uint a3 = 1 ever; // a3 == 1 000 000 000 (1e9)
uint a4 = 1 Ever; // a4 == 1 000 000 000 (1e9)
uint a5 = 1 micro; // a5 == 1 000 == 1e-6 ever
uint a6 = 1 microever; // a6 == 1 000 == 1e-6 ever
uint a7 = 1 milli; // a7 == 1 000 000 == 1e-3 ever
uint a8 = 1 milliever; // a8 == 1 000 000 == 1e-3 ever
uint a9 = 1 kiloever; // a9 == 1 000 000 000 000 (1e12) == 1e3 ever
uint a10 = 1 kEver; // a10 == 1 000 000 000 000 (1e12) == 1e3 ever
uint a11 = 1 megaever; // a11 == 1 000 000 000 000 000 (1e15) == 1e6 ever
uint a12 = 1 MEver; // a12 == 1 000 000 000 000 000 (1e15) == 1e6 ever
uint a13 = 1 gigaever; // a13 == 1 000 000 000 000 000 000 (1e18) == 1e9 ever
uint a14 = 1 GEver; // a14 == 1 000 000 000 000 000 000 (1e18) == 1e9 ever
uint a0 = 1 nano; // a0 == 1
uint a1 = 1 nanoton; // a1 == 1
uint a2 = 1 nTon; // a2 == 1
uint a3 = 1 ton; // a3 == 1 000 000 000 (1e9)
uint a4 = 1 Ton; // a4 == 1 000 000 000 (1e9)
uint a5 = 1 micro; // a5 == 1 000
uint a6 = 1 microton; // a6 == 1 000
uint a7 = 1 milli; // a7 == 1 000 000
uint a8 = 1 milliton; // a8 == 1 000 000
uint a9 = 1 kiloton; // a9 == 1 000 000 000 000 (1e12)
uint a10 = 1 kTon; // a10 == 1 000 000 000 000 (1e12)
uint a11 = 1 megaton; // a11 == 1 000 000 000 000 000 (1e15)
uint a12 = 1 MTon; // a12 == 1 000 000 000 000 000 (1e15)
uint a13 = 1 gigaton; // a13 == 1 000 000 000 000 000 000 (1e18)
uint a14 = 1 GTon; // a14 == 1 000 000 000 000 000 000 (1e18)
TvmCell
represents TVM cell (TVM - 1.1.3). The compiler defines the following
operators and functions to work with this type:
Comparison operators:
==
, !=
(evaluate to bool
)
To create empty cell use TvmCell()
. Example:
TvmCell cell = ...;
if (cell == TvmCell()) { // check whether `cell` is empty
}
<TvmCell>.depth() returns (uint16);
Returns the depth d of the TvmCell
c. If c has no references, then d = 0;
otherwise d is equal to one plus the maximum of depths of cells referred to from c.
If c is a Null instead of a Cell, returns zero.
<TvmCell>.dataSize(uint n) returns (uint /*cells*/, uint /*bits*/, uint /*refs*/);
Returns the number of distinct cells, data bits in the distinct cells and
cell references in the distinct cells. If number of the distinct cells
exceeds n+1
, then a cell overflow exception is thrown.
This function is a wrapper for the CDATASIZE
opcode (TVM - A.11.7).
If CapFastStorageStatBugfix
and CapFastStorageStat
are set, then calculate number of total (not distinct) cells and bits. For example:
TvmBuilder b2;
b2.storeOnes(5);
TvmBuilder b;
b.storeOnes(10);
b.storeRef(b2);
b.storeRef(b2);
b.storeRef(b2);
TvmCell cell = b.toCell();
(uint cells, uint bits, uint refs) = cell.dataSize(100);
// If `CapFastStorageStatBugfix` and `CapFastStorageStat` are set
// cells == 4
// bits == 10 + 3 * 5
// refs == 3
// Otherwise:
// cells == 2
// bits == 5 + 10
// refs == 3
<TvmCell>.dataSizeQ(uint n) returns (optional(uint /*cells*/, uint /*bits*/, uint /*refs*/));
Returns the number of distinct cells, data bits in the distinct cells and
cell references in the distinct cells. If number of the distinct cells
exceeds n+1
, then this function returns an optional
that has no value.
This function is a wrapper for the CDATASIZEQ
opcode (TVM - A.11.7).
<TvmCell>.toSlice() returns (TvmSlice);
Converts a TvmCell
to TvmSlice
.
If the cell is exotic, then the cell is automatically loaded and converted to TvmSlice
. For example:
TvmCell cellProof = ...;
TvmBuilder b;
b.store(
uint8(3), // type of MerkleProof exotic cell
tvm.hash(cellProof),
cellProof.depth(),
cellProof
);
TvmCell merkleProof = b.toExoticCell();
TvmSlice s = merkleProof.toSlice();
// `s` has the same data and references as `cellProof`
If you want load the cell as is, then see <TvmCell>.exoticToSlice().
<TvmCell>.exoticToSlice() returns (TvmSlice, bool)
Converts an ordinary or exotic cell into a TvmSlice
, as if it were an ordinary cell. A flag is returned indicating whether
TvmCell
is exotic. If that be the case, its type can later be deserialized from the first eight bits of TvmSlice
.
Example:
TvmCell cellProof = ...;
TvmBuilder b;
b.store(
uint8(3), // type of MerkleProof exotic cell
tvm.hash(cellProof),
cellProof.depth(),
cellProof
);
{
// convert builder to exotic cell
TvmCell merkleProof = b.toExoticCell();
(TvmSlice s, bool isExotic) = merkleProof.exoticToSlice();
// isExotic == true
uint8 flag = s.load(uint8); // flag == 3
}
{
// convert builder to ordinary cell
TvmCell cell = b.toCell();
(TvmSlice s, bool isExotic) = cell.exoticToSlice();
// isExotic == false
uint8 flag = s.load(uint8); // flag == 3
}
See also:
(1)
<TvmCell>.loadExoticCell() returns (TvmCell)
(2)
<TvmCell>.loadExoticCellQ() returns (TvmCell cell, bool ok)
(1) Loads an exotic cell and returns an ordinary cell. If the cell is already ordinary, does nothing. If it cannot be loaded, throws an exception. It is wrapper for opcode XLOAD
.
(2) Same as (1) but if it cannot be loaded, does not throw exception and ok
is equal to false
. It is wrapper for opcode XLOADQ
.
Example:
TvmCell cellProof = ...;
TvmBuilder b;
b.store(
uint8(3), // type of MerkleProof exotic cell
tvm.hash(cellProof),
cellProof.depth(),
cellProof
);
TvmCell cell = merkleProof.loadExoticCell(); // cell == cellProof
(TvmCell cell, bool ok) = merkleProof.loadExoticCellQ();
// cell == cellProof
// ok == true
TvmSlice
represents TVM cell slice (TVM - 1.1.3). The compiler defines the following
operators and functions to work with this type:
Comparison operators:
<=
, <
, ==
, !=
, >=
, >
(evaluate to bool).
Note: only data bits from the root cells are compared. References are ignored.
String literals can be converted to TvmSlice
:
TvmSlice s = "0189abef_";
TvmSlice
can be converted to bytes
. It costs at least 500 gas units.
(1)
<TvmSlice>.empty() returns (bool);
(2)
<TvmSlice>.bitEmpty() returns (bool);
3()
<TvmSlice>.refEmpty() returns (bool);
(1)
Checks whether the TvmSlice
contains no data bits and no cell references.
(2)
Checks whether the TvmSlice
contains no data bits.
(3)
Checks whether the TvmSlice
contains no cell references.
<TvmSlice>.size() returns (uint16 /*bits*/, uint8 /*refs*/);
Returns the number of data bits and references in the TvmSlice
.
<TvmSlice>.bits() returns (uint16);
Returns the number of data bits in the TvmSlice
.
<TvmSlice>.refs() returns (uint8);
Returns the number of references in the TvmSlice
.
<TvmSlice>.dataSize(uint n) returns (uint /*cells*/, uint /*bits*/, uint /*refs*/);
Returns the number of distinct cells, data bits in the distinct cells and
cell references in the distinct cells. If number of the distinct cells
exceeds n+1
, then a cell overflow exception is thrown.
Note that the returned count of distinct cells
does not take into
account the cell that contains the slice itself.
This function is a wrapper for SDATASIZE
opcode (TVM - A.11.7).
<TvmSlice>.dataSizeQ(uint n) returns (optional(uint /*cells*/, uint /*bits*/, uint /*refs*/));
Returns the number of distinct cells, data bits in the distinct cells and
cell references in the distinct cells. If number of the distinct cells
exceeds n+1
, then this function returns an optional
that has no value.
Note that the returned count of distinct cells
does not take into
account the cell that contains the slice itself.
This function is a wrapper for SDATASIZEQ
opcode (TVM - A.11.7).
<TvmSlice>.depth() returns (uint16);
Returns the depth of TvmSlice
. If the TvmSlice
has no references, then 0 is returned,
otherwise function result is one plus the maximum of depths of the cells referred to from the slice.
<TvmSlice>.hasNBits(uint10 bits) returns (bool);
<TvmSlice>.hasNRefs(uint2 refs) returns (bool);
<TvmSlice>.hasNBitsAndRefs(uint10 bits, uint2 refs) returns (bool);
Checks whether the TvmSlice
contains the specified amount of data bits and references.
<TvmSlice>.compare(TvmSlice other) returns (int2);
Lexicographically compares the slice
and other
data bits of the root slices and returns result as an integer:
- 1 -
slice
>other
- 0 -
slice
==other
- -1 -
slice
<other
<TvmSlice>.startsWith(TvmSlice prefix) returns (bool);
Checks whether prefix
is a prefix of TvmSlice
.
<TvmSlice>.startsWithOne() returns (bool);
Checks whether the first bit of TvmSlice
is a one.
All load*
functions below modify the TvmSlice
object. If you wants to load second reference from the TvmSlice
, you should load the first one with <TvmSlice>.loadRef() and then load the reference you need. The same rule is applied to data bits. To load bits from 2 to 10 positions, you should load or skip first two bits.
<TvmSlice>.load(TypeA, TypeB, ...) returns (TypeA /*a*/, TypeB /*b*/, ...);
Sequentially loads values of the specified types from the TvmSlice
.
Supported types: uintN
, intN
, bytesN
, bool
, ufixedMxN
, fixedMxN
, address
, contract
,
TvmCell
, bytes
, string
, mapping
, array
, optional
and
struct
. Example:
TvmSlice slice = ...;
(uint8 a, uint16 b) = slice.load(uint8, uint16);
(uint16 num0, uint32 num1, address addr) = slice.load(uint16, uint32, address);
See also: <TvmBuilder>.store(). Note: if all the argument types can't be loaded from the slice a cell underflow exception is thrown.
<TvmSlice>.loadQ(TypeA, TypeB, ...) returns (optional(TypeA, TypeB, ...));
Sequentially decodes values of the specified types from the TvmSlice
if the TvmSlice
holds sufficient data for all specified types. Otherwise, returns null
.
TvmSlice slice = ...;
optional(uint) a = slice.loadQ(uint);
optional(uint8, uint16) b = slice.loadQ(uint8, uint16);
See also: <TvmBuilder>.store().
<TvmSlice>.loadRef() returns (TvmCell);
Loads a cell from the TvmSlice
reference.
<TvmSlice>.loadRefAsSlice() returns (TvmSlice);
Loads a cell from the TvmSlice
reference and converts it into a TvmSlice
.
(1)
<TvmSlice>.loadInt(uint9 bitSize) returns (int);
(2)
<TvmSlice>.loadIntQ(uint9 bitSize) returns (optional(int));
(1) Loads a signed integer with the given bitSize from the TvmSlice
.
(2) Loads a signed integer with the given bitSize from the TvmSlice
if TvmSlice
contains it. Otherwise, returns null
.
(1)
<TvmSlice>.loadUint(uint9 bitSize) returns (uint);
(2)
<TvmSlice>.loadUintQ(uint9 bitSize) returns (optional(uint));
(1) Loads an unsigned integer with the given bitSize from the TvmSlice
.
(2) Loads an unsigned integer with the given bitSize from the TvmSlice
if TvmSlice
contains it. Otherwise, returns null
.
(1)
<TvmSlice>.loadIntLE2() returns (int16)
<TvmSlice>.loadIntLE4() returns (int32)
<TvmSlice>.loadIntLE8() returns (int64)
<TvmSlice>.loadUintLE2() returns (uint16)
<TvmSlice>.loadUintLE4() returns (uint32)
<TvmSlice>.loadUintLE8() returns (uint64)
(2)
<TvmSlice>.loadIntLE4Q() returns (optional(int32))
<TvmSlice>.loadIntLE8Q() returns (optional(int64))
<TvmSlice>.loadUintLE4Q() returns (optional(uint32))
<TvmSlice>.loadUintLE8Q() returns (optional(uint64))
(1) Loads the little-endian integer from TvmSlice
.
(2) Same as (1) but returns null
if it's impossible to load the integer.
(1)
<TvmSlice>.loadSlice(uint10 bits) returns (TvmSlice);
(2)
<TvmSlice>.loadSlice(uint10 bits, uint2 refs) returns (TvmSlice);
(3)
<TvmSlice>.loadSliceQ(uint10 bits) returns (optional(TvmSlice));
(4)
<TvmSlice>.loadSliceQ(uint10 bits, uint2 refs) returns (optional(TvmSlice));
(1) Loads the first bits
bits from TvmSlice
.
(2) Loads the first bits
bits and refs
references from TvmSlice
.
(3) and (4) are same as (1) and (2) but return optional
type.
<TvmSlice>.skip(uint10 bits);
<TvmSlice>.skip(uint10 bits, uint3 refs);
Skips the first bits
bits and refs
references from the TvmSlice
.
(1)
<TvmSlice>.loadZeroes() returns (uint10 n);
(2)
<TvmSlice>.loadOnes() returns (uint10 n);
(3)
<TvmSlice>.loadSame(uint1 value) returns (uint10 n);
(1) Returns the count n
of leading zero bits in TvmSlice
, and removes these bits from TvmSlice
.
(2) Returns the count n
of leading one bits in TvmSlice
, and removes these bits from TvmSlice
.
(3) Returns the count n
of leading bits equal to 0 ≤ value ≤ 1
in TvmSlice
, and removes these bits from TvmSlice
.
See also: <TvmBuilder>.storeZeroes(), <TvmBuilder>.storeOnes() and <TvmBuilder>.storeSame().
All preload*
functions below don't modify the TvmSlice
object.
<TvmSlice>.preload(TypeA, TypeB, ...) returns (TypeA /*a*/, TypeB /*b*/, ...);
Same as <TvmSlice>.load() but doesn't modify TvmSlice
.
<TvmSlice>.preloadQ(TypeA, TypeB, ...) returns (optional(TypeA, TypeB, ...));
Same as <TvmSlice>.loadQ() but doesn't modify TvmSlice
.
(1)
<TvmSlice>.preloadRef() returns (TvmCell);
(2)
<TvmSlice>.preloadRef(uint2 index) returns (TvmCell);
(1) Returns the first cell reference of TvmSlice
.
(2) Returns the index
cell reference of TvmSlice
, where 0 ≤ index ≤ 3
.
(1)
<TvmSlice>.preloadInt(uint9 bitSize) returns (int);
(2)
<TvmSlice>.preloadIntQ(uint9 bitSize) returns (optional(int));
Same as <TvmSlice>.loadInt() and <TvmSlice>.loadIntQ() but doesn't modify TvmSlice
.
(1)
<TvmSlice>.preloadUint(uint9 bitSize) returns (uint);
(2)
<TvmSlice>.preloadUintQ(uint9 bitSize) returns (optional(uint));
Same as <TvmSlice>.loadUint() and <TvmSlice>.loadUintQ() but doesn't modify TvmSlice
.
<TvmSlice>.preloadIntLE4() returns (int32)
<TvmSlice>.preloadIntLE8() returns (int64)
<TvmSlice>.preloadUintLE4() returns (uint32)
<TvmSlice>.preloadUintLE8() returns (uint64)
<TvmSlice>.preloadIntLE4Q() returns (optional(int32))
<TvmSlice>.preloadIntLE8Q() returns (optional(int64))
<TvmSlice>.preloadUintLE4Q() returns (optional(uint32))
<TvmSlice>.preloadUintLE8Q() returns (optional(uint64))
Same as Load little-endian integers but doesn't modify TvmSlice
.
(1)
<TvmSlice>.preloadSlice(uint10 bits) returns (TvmSlice);
(2)
<TvmSlice>.preloadSlice(uint10 bits, uint refs) returns (TvmSlice);
(3)
<TvmSlice>.preloadSliceQ(uint10 bits) returns (optional(TvmSlice));
(4)
<TvmSlice>.preloadSliceQ(uint10 bits, uint4 refs) returns (optional(TvmSlice));
Same as <TvmSlice>.loadSlice() and <TvmSlice>.loadSliceQ() but doesn't modify TvmSlice
.
TvmBuilder
represents TVM cell builder (TVM - 1.1.3). TVM Solidity compiler defines the following
functions to work with this type:
<TvmBuilder>.toSlice() returns (TvmSlice);
Converts a TvmBuilder
into TvmSlice
.
<TvmBuilder>.toCell() returns (TvmCell);
Converts a TvmBuilder
into TvmCell
.
<TvmBuilder>.toExoticCell() returns (TvmCell);
Creates an exotic cell from TvmBuilder
. It is wrapper for opcodes TRUE ENDXC
.
Examples:
TvmCell cellProof = getCell();
TvmBuilder b;
b.store(
uint8(3), // type of MerkleProof exotic cell
tvm.hash(cellProof),
cellProof.depth(),
cellProof
);
TvmCell merkleProof = b.toExoticCell();
<TvmBuilder>.size() returns (uint16 /*bits*/, uint8 /*refs*/);
Returns the number of data bits and references already stored in the TvmBuilder
.
<TvmBuilder>.bits() returns (uint16);
Returns the number of data bits already stored in the TvmBuilder
.
<TvmBuilder>.refs() returns (uint8);
Returns the number of references already stored in the TvmBuilder
.
<TvmBuilder>.remBits() returns (uint16);
Returns the number of data bits that can still be stored in the TvmBuilder
.
<TvmBuilder>.remRefs() returns (uint8);
Returns the number of references that can still be stored in the TvmBuilder
.
<TvmBuilder>.remBitsAndRefs() returns (uint16 /*bits*/, uint8 /*refs*/);
Returns the number of data bits and references that can still be stored in the TvmBuilder
.
<TvmBuilder>.depth() returns (uint16);
Returns the depth of TvmBuilder
. If no cell references are stored
in the builder, then 0 is returned; otherwise function result is one plus the maximum of
depths of cells referred to from the builder.
<TvmBuilder>.store(/*list_of_values*/);
Stores the list of values into the TvmBuilder
.
Internal representation of the stored data:
uintN
/intN
/bytesN
- stored as an N-bit string. For example,uint8(100), int16(-3), bytes2(0xaabb)
stored as0x64fffdaabb
.bool
- stored as a binary zero forfalse
or a binary one fortrue
. For example,true, false, true
stored as0xb_
.ufixedMxN
/fixedMxN
- stored as an M-bit string.address
/contract
- stored according to the TL-B scheme ofMsgAddress
.TvmCell
/bytes
/string
- stored as a cell in reference.TvmSlice
/TvmBuilder
- all data bits and references of theTvmSlice
or theTvmBuilder
are appended to theTvmBuilder
, not in a reference asTvmCell
. To storeTvmSlice
/TvmBuilder
in the references use <TvmBuilder>.storeRef().mapping
- stored according to the TL-B scheme ofHashmapE
: if map is empty, then stored as a binary zero, otherwise as a binary one and the dictionaryHashmap
in a reference.array
- stored as a 32 bit value - size of the array and aHashmapE
that contains all values of the array.optional
- stored as a binary zero if theoptional
doesn't contain value. Otherwise, stored as a binary one and the cell with serialized value in a reference.struct
- stored in the order of its members in the builder. Make sure the entire struct fits into the builder.
Note: there is no gap or offset between two consecutive data assets stored in the TvmBuilder
.
See TVM to read about notation for bit strings.
Example:
uint8 a = 11;
int16 b = 22;
TvmBuilder builder;
builder.store(a, b, uint(33));
See also: <TvmSlice>.load().
<TvmBuilder>.storeQ(T value) returns (bool ok);
Same as <TvmBuilder>.store() but returns the success flag. It does not throw exceptions.
Supported types:
uintN
/intN
/bytesN
bool
ufixedMxN
/fixedMxN
address
/contract
TvmCell
/bytes
/string
TvmSlice
/TvmBuilder
(1)
<TvmBuilder>.storeZeroes(uint10 n);
(2)
<TvmBuilder>.storeOnes(uint10 n);
(3)
<TvmBuilder>.storeSame(uint10 n, uint1 value);
(1) Stores n
binary zeroes into the TvmBuilder
.
(2) Stores n
binary ones into the TvmBuilder
.
(3) Stores n
binary value
s (0 ≤ value ≤ 1) into the TvmBuilder
.
See also: <TvmSlice>.loadZeroes(), <TvmSlice>.loadOnes() and <TvmSlice>.loadSame().
<TvmBuilder>.storeInt(int257 value, uint9 bitSize);
Stores a signed integer value with given bitSize in the TvmBuilder
.
<TvmBuilder>.storeUint(uint256 value, uint9 bitSize);
Stores an unsigned integer value with given bitSize in the TvmBuilder
.
<TvmBuilder>.storeIntLE2(int16)
<TvmBuilder>.storeIntLE4(int32)
<TvmBuilder>.storeIntLE8(int64)
<TvmBuilder>.storeUintLE2(uint16)
<TvmBuilder>.storeUintLE4(uint32)
<TvmBuilder>.storeUintLE8(uint64)
Stores the little-endian integer.
<TvmBuilder>.storeRef(TvmBuilder b);
<TvmBuilder>.storeRef(TvmCell c);
<TvmBuilder>.storeRef(TvmSlice s);
Stores TvmBuilder b
/TvmCell c
/TvmSlice s
in the reference of the TvmBuilder
.
A mutable sequence of characters. StringBuilder
allows creating a string from bytes1
and string
in a gas-efficient way. Example:
StringBuilder b;
b.append(bytes1("-"));
b.append(bytes1("0"), 10);
b.append("1234");
string s = b.toString(); // s == "-00000000001234"
(1)
<StringBuilder>.append(bytes1);
(2)
<StringBuilder>.append(bytes1, uint31 n);
(3)
<StringBuilder>.append(string);
(1) Appends bytes1
to the sequence.
(2) Appends bytes1
n
times to the sequence.
(3) Appends string
to the sequence.
<StringBuilder>.toString();
Returns a string representing the data in this sequence.
The template optional type manages an optional contained value, i.e. a value that may or may not be present.
There are many ways to set a value:
optional(uint) opt;
opt.set(11); // just sets value
opt = 22; // just sets value, too
opt.get() = 33; // if 'opt' has value, then set value. Otherwise throws an exception.
optional(uint) another = 44;
opt = another;
<optional(T)>.hasValue() returns (bool);
Checks whether the optional
contains a value.
<optional(T)>.get() returns (T);
Returns the contained value, if the optional
contains one. Otherwise, throws an exception.
<optional(T)>.getOr(T default) returns (T);
Returns the contained value, if the optional
contains one. Otherwise, returns default
.
<optional(T)>.getOrDefault() returns (T);
Returns the contained value, if the optional
contains one. Otherwise, returns the default value for T
type.
<optional(T)>.set(Type value);
Replaces content of the optional
with value.
Keyword null
is a constant that is used to indicate an optional type with uninitialized state.
Example:
optional(uint) x = 123;
x = null; // reset value
The variant
type acts like a union for the most common solidity data types. Supported only uint
so far.
<variant>.isUint() returns (bool)
Checks whether <variant>
holds uint
type.
Converts <variant>
to uint
type if it's possible. Otherwise, throws an exception with code 77
.
vector(T)
is a template container type for storing values of a same type, pretty much like dynamic-sized array.
Two major differences are that vector(T)
:
- is much more efficient than a dynamic-sized array;
- has a lifespan of a smart-contract execution, so it can't be neither passed nor returned as an external function call parameter, nor stored in a state variable.
Note: vector
implementation based on TVM Tuple
type, and it has a limited length of 255 values.
Example:
vector(int) arr;
arr.push(100); // arr == [100]
arr.push(200); // arr == [100, 200]
arr.push(300); // arr == [100, 200, 300]
uint8 len = arr.length(); // len == 3
int value1 = arr[1]; // value1 == 200
arr[1] = 222; // arr == [100, 222, 300]
int last = arr.last(); // last == 300, arr == [100, 222, 300]
last = arr.pop(); // last == 300, arr == [100, 222]
last = arr.pop(); // last == 222, arr == [100]
last = arr.pop(); // last == 100, arr == []
bool isEmpty = arr.empty(); // isEmpty == true
<vector(T)>.push(T element);
Appends element to the vector
.
<vector(T)>.pop() returns (T);
Pops the last value from the vector
and returns it.
<vector(T)>.last() returns (T);
Returns the last value from the vector
.
<vector(T)>.operator[](uint index) returns (T);
Returns the value located at the index
position. If index
is not within the range of the container, an exception is thrown.
<vector(T)>.length() returns (uint8);
Returns length of the vector
.
<vector(T)>.empty() returns (bool);
Checks whether the vector
is empty.
stack
represents a last-in-first-out (LIFO) stack of items. The usual push and pop operations are provided, as well as a method to peek at the top item on the stack, a method to test for whether the stack is empty.
stack(int) st;
st.push(100);
st.push(200);
bool isEmpty = st.empty(); // isEmpty == false
int item = st.top(); // item == 200, st == [100, 200]
st.top() += 25; // st == [100, 225]
item = st.top(); // item == 225, st == [100, 225]
item = st.pop(); // item == 225, st == [100]
item = st.pop(); // item == 100, st == []
isEmpty = st.empty(); // isEmpty == true
<stack(T)>.push(T item)
Pushes an item onto the top of this stack.
<stack(T)>.pop() returns (T)
Removes the item at the top of this stack and returns that item as the value of this function.
<stack(T)>.top() returns (ref T)
Returns reference at the item at the top of this stack without removing it from the stack. Example:
stack(int) st;
st.push(200);
st.top() += 25; // st == [225]
int item = st.top(); // item = 225, st == [225]
<stack(T)>.empty() returns (bool)
Checks whether the stack
is empty.
<stack(T)>.sort(function(Type, Type) internal pure returns(bool) isLess)
Sorts the specified stack into ascending order. Example:
struct Point {
int x;
int y;
}
function less(Point a, Point b) private pure returns(bool) {
return a.x < b.x || a.x == b.x && a.y < b.y;
}
function testPoints() public pure {
stack(Point) st;
st.push(Point(20, 40));
st.push(Point(10, 10));
st.push(Point(20, 30));
st.sort(less);
Point p;
p = st.pop(); // p == Point(10, 10)
p = st.pop(); // p == Point(20, 30)
p = st.pop(); // p == Point(20, 40)
}
<stack(T)>.reverse()
Reverses the order of the elements in the specified stack. Example:
stack(int) st;
st.push(100);
st.push(200);
st.push(300);
int value = st.top(); // value == 300
st.reverse();
value = st.pop(); // value == 100
value = st.pop(); // value == 200
value = st.pop(); // value == 300
Executes a for
loop over a range. Used as a more readable equivalent to the traditional for
loop
operating over a range of values, such as all elements in an array or mapping.
for ( range_declaration : range_expression ) loop_statement
range_expression
is calculated only once, and the result is copied. Then iteration goes over the copy of
the array or mapping.
uint[] arr = ...;
uint sum = 0;
for (uint val : arr) { // iteration over the array
sum += val;
}
bytes byteArray = "Hello!";
for (byte b : byteArray) { // iteration over the byte array
}
mapping(uint32 => uint) map = ...;
uint keySum = 0;
uint valueSum = 0;
for ((uint32 key, uint value) : map) { // iteration over the mapping
keySum += key;
valueSum += value;
}
Key or value can be omitted if you iterate over a mapping:
mapping(uint32 => uint) map = ...;
uint keySum = 0;
for ((uint32 key, ) : map) { // value is omitted
keySum += key;
}
uint valueSum = 0;
for ((, uint value) : map) { // key is omitted
valueSum += value;
}
repeat (exression) statement
Allows repeating the block of code several times. exression
evaluates only one time.
uint a = 0;
repeat(10) {
++a;
}
// a == 10
Capabilities required: CapsTvmBugfixes2022
.
The try
statement allows you to define a block of code to be tested for errors while it is executed. The
catch
statement allows you to define a block of code to be executed, if an error occurs in the try block.
catch
block gets two parameters of type variant and uint16, which contain exception argument and code respectively.
Example:
TvmBuilder builder;
uint c = 0;
try {
c = a + b;
require(c != 42, 100, 22);
require(c != 43, 100, 33);
builder.store(c);
} catch (variant value, uint16 errorCode) {
uint errorValue;
if (value.isUint()) {
errorValue = value.toUint();
}
if (errorCode == 100) {
if (errorValue == 22) {
// it was line: `require(c != 42, 100, 22);`
} else if (errorValue == 33) {
// it was line: `require(c != 43, 100, 33);`
}
} else if (errorCode == 8) {
// Cell overflow
// It was line: `builder.store(c);`
} else if (errorCode == 4) {
// Integer overflow
// It was line: `c = a + b;`
}
}
You can pass either of the parameters:
uint c;
try {
c = a + b;
} catch (variant , uint16 errorCode) {
if (errorCode == 4) {
}
}
Or you can pass all parameters:
uint c;
try {
c = a + b;
} catch {
}
unchecked {
/* */
}
Turns off reverting on over- and underflow for arithmetic operations in unchecked
block. It is up to programmer to control range of integer types if they use unchecked
blocks. For example:
function f(uint8 a, uint8 b) pure private returns (uint8) {
unchecked {
uint8 c = a - b;
return c;
}
}
function g(uint8 a, uint8 b) pure private returns (uint8) {
uint8 c = a - b;
return c;
}
The call to f(2, 3)
will return -1
, while g(2, 3)
will cause a failing assertion.
See also: pragma ignoreIntOverflow.
int
/ uint
: Signed and unsigned integers of various sizes.
Keywords uint1
to uint256
in steps of 1 (unsigned of 1 up to 256 bits) and int1
to int257
. uint
and int
are aliases for uint256
and int257
, respectively.
Operators:
- Comparison:
<=
,<
,==
,!=
,>=
,>
(evaluate tobool
) - Bit operators:
&
,|
,^
(bitwise exclusive or),~
(bitwise negation) - Shift operators:
<<
(left shift),>>
(right shift) - Arithmetic operators:
+
,-
, unary-
,*
,/
,%
(modulo),**
(exponentiation)
<Integer>.cast(T) returns (T)
Convert <Integer>
to T type. T
is integer type. Type of <Integer>
and T
must have same sign or bit-size. Never throws an exception. For example:
uint8 a = 255;
uint4 b = a.cast(uint4); // b == 15
uint8 a = 255;
int8 b = a.cast(int8); // b == -1
uint8 a = 255;
int4 b = a.cast(uint4).cast(int4); // b == -1
uint8 a = 255;
// int4 b = a.cast(int4); // compilation fail
Note: conversion via T(x)
throws an exception if x
does not fit into T
type. For example:
uint8 a = 10;
uint4 b = uint4(a); // OK, a == 10
uint8 a = 100;
uint4 b = uint4(a); // throws an exception because type(uint4).max == 15
int8 a = -1;
uint8 b = uint8(a); // throws an exception because type(uint8).min == 0
bitSize(int x) returns (uint16)
uBitSize(uint x) returns (uint16)
bitSize
computes the smallest c
≥ 0 such that x
fits into a c
-bit signed integer
(−2c−1 ≤ x < 2c−1).
uBitSize
computes the smallest c
≥ 0 such that x
fits into a c
-bit unsigned integer
(0 ≤ x < 2c).
Example:
uint16 s = bitSize(12); // s == 5
uint16 s = bitSize(1); // s == 2
uint16 s = bitSize(-1); // s == 1
uint16 s = bitSize(0); // s == 0
uint16 s = uBitSize(10); // s == 4
uint16 s = uBitSize(1); // s == 1
uint16 s = uBitSize(0); // s == 0
Operations with qintN
/ quintN
return NaN
instead of throwing integer overflow exceptions if the results do not fit in type, or if one of their arguments is a NaN
. Default value for qintN
/ quintN
is 0
, for qbool
- false
.
qint
/ quint
: Signed and unsigned integers of various sizes.
Keywords quint1
to quint256
in steps of 1 (unsigned of 1 up to 256 bits) and qint1
to qint257
. quint
and qint
are aliases for quint256
and qint257
, respectively.
Operators:
- Comparison:
<=
,<
,==
,!=
,>=
,>
(evaluate to qbool) - Bit operators:
&
,|
,^
(bitwise exclusive or),~
(bitwise negation) - Shift operators:
<<
(left shift),>>
(right shift) - Arithmetic operators:
+
,-
, unary-
,*
,/
,%
(modulo),**
(exponentiation)
If one operand of bitwise “or” (|
) is equal to −1, the result
is always −1, even if the other argument is a NaN
;
If one operand of bitwise “and” (&
) is equal to 0, the result
is always 0, even if the other argument is a NaN
;
If one operand of ==
(!=
) is equal to NaN
, the result
is always NaN
.
All math namespace functions support quiet arithmetic.
qbool
can have 3 values: true
, false
and NaN
.
Operators:
!
(logical negation)&&
(logical conjunction, “and”)||
(logical disjunction, “or”)==
(equality)!=
(inequality)
The operators ||
and &&
apply the common short-circuiting rules. This means that in the expression f(x) || g(y)
, if f(x)
evaluates to true, g(y)
will not be evaluated even if it may have side-effects.
If one operand of logical “or” (|
) is equal to true
, the result
is always true
, even if the other argument is a NaN
;
If one operand of logical “and” (&
) is equal to false
, the result
is always false
, even if the other argument is a NaN
;
If one operand of ==
(!=
) is equal to NaN
, the result
is always NaN
.
function f(quint32 a, quint32 b, quint32 c, quint32 d) private {
qbool less = a * b < c * d;
if (less.isNaN()) {
// ...
} else {
bool l = less.get();
// ...
}
}
The NaN
constant returns a nan (Not a Number) value. This value is not a legal number. Example:
qint32 x = NaN;
qbool y = NaN;
<T>.isNaN() returns (bool)
Checks whether <T>
is NaN
. T
is qintN
, quintN
or qbool
. Example:
function checkOverflow(quint32 a, quint32 b) private pure returns(bool) {
quint32 s = a + b;
return s.isNaN();
}
<T>.get() returns (T2)
Returns "non-quiet" integer. If <T>
is NaN
, then throws an exception. T
is qintN
, quintN
or qbool
. Example:
function f(quint32 a, quint32 b) private pure {
quint32 s = a + b;
if (!s.isNaN()) {
uint32 ss = s.get();
// ...
}
}
<T>.getOr(T2 default) returns (T2)
Returns "non-quiet" integer. If <T>
is NaN
, then returns default
. T
is qintN
, quintN
or qbool
. Example:
function f(quint32 a, quint32 b) private pure {
quint32 s = a + b;
uint32 ss = s.getOr(42); // ss is equal to `a + b` or 42
// ...
}
<T>.getOrDefault() returns (T2)
Returns "non-quiet" integer. If <T>
is NaN
, then returns default value. T
is qintN
, quintN
or qbool
. Example:
function f(quint32 a, quint32 b) private pure {
quint32 s = a + b;
uint32 ss = s.getOrDefault(); // ss is equal to `a + b` or 0
// ...
}
<T>.toOptional() returns (optional(T2))
Returns optional integer. If <T>
is NaN
, then returns null. T
is qintN
, quintN
or qbool
. Example:
function f(quint32 a, quint32 b) private pure {
quint32 s = a + b;
optional(uint32) ss = s.toOptional(); // ss is equal to `a + b` or null
// ...
}
varint
/varint16
/varint32
/varuint
/varuint16
/coins
/varuint32
are kinds of Integer
types. But they are serialized/deserialized according to their TLB schemes.
These schemes are effective if you want to store or send integers, and they usually have small size.
Use these types only if you are sure.
varint
is equal to varint32
, varint32
- int248
, varint16
- int120
.
varuint
is equal to varuint32
, varuint32
- uint248
, varuint16
- uint120
.
coins
is an alias for varuint16
.
Example:
mapping(uint => varint) m_map; // use `varint` as mapping value only if values have small size
m_map[10] = 15;
Operators:
- Comparison:
<=
,<
,==
,!=
,>=
,>
(evaluate tobool
) - Bit operators:
&
,|
,^
(bitwise exclusive or),~
(bitwise negation) - Shift operators:
<<
(left shift),>>
(right shift) - Arithmetic operators:
+
,-
, unary-
,*
,/
,%
(modulo),**
(exponentiation)
Structs are custom defined types that can group several variables.
struct Stakes {
uint total;
mapping(uint => uint) stakes;
}
// create struct with default values of its members
Stakes stakes;
// create struct using parameters
mapping(uint => uint) values;
values[100] = 200;
Stakes stakes = Stakes(200, values);
// create struct using named parameters
Stakes stakes = Stakes({stakes: values, total: 200});
<struct>.unpack() returns (TypeA /*a*/, TypeB /*b*/, ...);
Unpacks all members stored in the struct
.
Example:
struct MyStruct {
uint a;
int b;
address c;
}
function f() pure public {
MyStruct s = MyStruct(1, -1, address(2));
(uint a, int b, address c) = s.unpack();
}
An array literal is a comma-separated list of one or more expressions, enclosed in square brackets.
For example: [100, 200, 300]
.
Initializing constant state variable:
uint[] constant fib = [uint(2), 3, 5, 8, 12, 20, 32];
uint[] arr; // create 0-length array
uint[] arr = new uint[](0); // create 0-length array
uint constant N = 5;
uint[] arr = new uint[](N); // create 5-length array
uint[] arr = new uint[](10); // create 10-length array
Note: If N
is constant expression or integer literal, then the complexity of array creation -
O(1)
. Otherwise, O(N)
.
<array>.empty() returns (bool);
Returns status flag whether the array
is empty (its length is 0).
Example:
uint[] arr;
bool b = arr.empty(); // b == true
arr.push(...);
bool b = arr.empty(); // b == false
Variables of the bytesN
types can be explicitly converted to bytes
. Note: it costs ~500 gas.
bytes3 b3 = 0x112233;
bytes b = bytes(b3);
bytes
is an array of byte
. It is similar to byte[]
, but they are encoded in different ways.
Example of bytes
initialization:
// initialised with string
bytes a = "abzABZ0129";
// initialised with hex data
bytes b = hex"01239abf";
bytes
can be converted to TvmSlice
. Warning: if length of the array is greater than 127, then extra bytes are stored in the first reference of the slice. Use <TvmSlice>.loadRef() to load that extra bytes.
<bytes>.empty() returns (bool);
Returns status flag whether the bytes
is empty (its length is 0).
<bytes>.operator[](uint index) returns (byte);
Returns the byte located at the index position.
Example:
bytes byteArray = "abba";
int index = 0;
byte a0 = byteArray[index]; // a0 = 0x61
<bytes>.operator[](uint from, uint to) returns (bytes);
Returns the slice of bytes
[from, to), including from byte and
excluding to.
Example:
bytes byteArray = "01234567890123456789";
bytes slice = byteArray[5:10]; // slice == "56789"
slice = byteArray[10:]; // slice == "0123456789"
slice = byteArray[:10]; // slice == "0123456789"
slice = byteArray[:]; // slice == "01234567890123456789"
<bytes>.length returns (uint)
Returns length of the bytes
array.
<bytes>.dataSize(uint n) returns (uint /*cells*/, uint /*bits*/, uint /*refs*/);
Same as <TvmCell>.dataSize().
<bytes>.dataSizeQ(uint n) returns (optional(uint /*cells*/, uint /*bits*/, uint /*refs*/));
Same as <TvmCell>.dataSizeQ().
<bytes>.append(bytes tail);
Modifies the bytes
by concatenating tail data to the end of the bytes
.
bytes byteArray = "1234";
bytes4 bb = byteArray;
bytes
can be converted to bytesN
.
If bytes
object has less than N bytes, extra bytes are padded with zero bits.
TVM Solidity compiler expands string
type with the following functions:
Note: Due to VM restrictions string length can't exceed 1024 * 127 = 130048
bytes.
string
can be converted to TvmSlice
.
<string>.empty() returns (bool);
Returns status flag whether the string
is empty (its length is 0).
<string>.byteLength() returns (uint32);
Returns byte length of the string
data.
(1)
<string>.substr(uint pos) returns (string);
(2)
<string>.substr(uint pos, uint count) returns (string);
(1) Returns a string that is a substring of this string. The substring begins with the character at the specified pos
and extends to the end of this string.
(2) Returns a string that is a substring of this string. The substring begins with the character at the specified pos
and copies at most count
characters from the source string. If the string is shorter, as many characters as possible are used.
Throws an exception if pos > string.byteLength()
.
string text = "0123456789";
string a = text.substr(1, 2); // a = "12"
string b = text.substr(6); // b = "6789"
string c = text.substr(6, 100); // c = "6789"
string d = text.substr(777); // throws an exception
Note: if Array Slices are used (they are written as x[start:end]
), then 0 <= start <= end <= byteLength()
.
string str = "01234567890";
string c = text.substr(6, 100); // c = "6789"
string d = text[6:106]; // throws an exception
<string>.append(string tail);
Appends the tail string
to the string
.
<string>.operator+(string) returns (string);
<string>.operator+(bytesN) returns (string);
Creates new string
as a concatenation of left and right arguments of the operator +. Example:
string a = "abc";
bytes2 b = "12";
string c = a + b; // "abc12"
<string>.find(bytes1 symbol) returns (optional(uint32));
<string>.find(string substr) returns (optional(uint32));
<string>.findLast(bytes1 symbol) returns (optional(uint32));
Looks for symbol (or substring) in the string
and returns index of the first (find
) or the
last (findLast
) occurrence. If there is no such symbol in the string
, an empty optional is returned.
Example:
string str = "01234567890";
optional(uint32) a = str.find(byte('0'));
bool s = a.hasValue(); // s == true
uint32 n = a.get(); // n == 0
byte symbol = 'a';
optional(uint32) b = str.findLast(symbol);
bool s = b.hasValue(); // s == false
string sub = "111";
optional(uint32) c = str.find(sub);
bool s = c.hasValue(); // s == false
<string>.dataSize(uint n) returns (uint /*cells*/, uint /*bits*/, uint /*refs*/);
Same as <TvmCell>.dataSize().
<string>.dataSizeQ(uint n) returns (optional(uint /*cells*/, uint /*bits*/, uint /*refs*/));
Same as <TvmCell>.dataSizeQ().
<string>.toUpperCase() returns (string)
<string>.toLowerCase() returns (string)
Converts string
to upper/lower case. It treats string
as ASCII string and converts only 'A'..'Z'
and 'a'..'z' symbols. Example:
string s = "Hello";
string a = s.toUpperCase(); // a == "HELLO"
string b = s.toLowerCase(); // b == "hello"
format(string template, TypeA a, TypeB b, ...) returns (string);
Builds a string
with arbitrary parameters. Empty placeholder {}
can be filled with integer
(in decimal view), address, string or fixed point number. Fixed point number is printed
based on its type (fixedMxN
is printed with N
digits after dot).
Placeholder should be specified in such formats:
"{}"
- empty placeholder"{:[0]<width>{"x","d","X","t"}}"
- placeholder for integers. Fills num with 0 if format starts with "0".
Formats integer to have specified width
. Can format integers in decimal ("d" postfix), lower hex ("x")
or upper hex ("X") form. Format "t" prints number (in nanoevers) as a fixed point sum.
Warning: this function consumes too much gas, that's why it's better not to use it onchain. Example:
string str = format("Hello {} 0x{:X} {} {}.{} evers", 123, 255, address.makeAddrStd(-33,0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF123456789ABCDE), 100500, 32);
// str == "Hello 123 0xFF -21:7fffffffffffffffffffffffffffffffffffffffffffffffff123456789abcde 100500.32 evers"
str = format("Hello {}", 123); // str == "Hello 123"
str = format("Hello 0x{:X}", 123); // str == "Hello 0x7B"
str = format("{}", -123); // str == "-123"
str = format("{}", address.makeAddrStd(127,0)); // str == "7f:0000000000000000000000000000000000000000000000000000000000000000"
str = format("{}", address.makeAddrStd(-128,0)); // str == "-80:0000000000000000000000000000000000000000000000000000000000000000"
str = format("{:6}", 123); // str == " 123"
str = format("{:06}", 123); // str == "000123"
str = format("{:06d}", 123); // str == "000123"
str = format("{:06X}", 123); // str == "00007B"
str = format("{:6x}", 123); // str == " 7b"
uint128 a = 1 ever;
str = format("{:t}", a); // str == "1.000000000"
a = 123;
str = format("{:t}", a); // str == "0.000000123"
fixed32x3 v = 1.5;
str = format("{}", v); // str == "1.500"
fixed256x10 vv = -987123.4567890321;
str = format("{}", vv); // str == "-987123.4567890321"
bool flag = true;
str = format("Hello, {}!", flag); // str == "Hello, true!"
stoi(string inputStr) returns (optional(int) /*result*/);
Converts a string
into an integer. If string
starts with '0x' it will be converted from a hexadecimal format,
otherwise it is meant to be number in decimal format. Function returns the integer in case of success.
Otherwise, returns null
.
Warning: this function consumes too much gas, that's why it's better not to use it onchain. Example:
optional(int) res;
res = stoi("123"); // res ==123
string hexstr = "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF123456789ABCDE";
res = stoi(hexstr); // res == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF123456789ABCDE
res = stoi("0xag"); // res == null
res = stoi(""); // res == null
string s = "1";
bytes2 b = bytes2(s); // b == 0x3100
string s = "11";
bytes2 b = bytes2(s); // b = 0x3131
string
can be converted to bytesN
which causes N * 8 bits being loaded from the cell and saved to variable.
If string
object has less than N bytes, extra bytes are padded with zero bits.
address
represents different types of TVM addresses: addr_none, addr_extern,
addr_std and addr_var. TVM Solidity compiler expands address
type with the following
members and functions:
uint address_value;
address addrStd = address(address_value);
Constructs address
of type addr_std with zero workchain id and given address value.
int8 wid;
uint address;
address addrStd = address.makeAddrStd(wid, address);
Constructs address
of type addr_std with given workchain id wid and value address_value.
address addrNone = address.addrNone;
Constructs address
of type addr_none.
uint addrNumber;
uint bitCnt;
address addrExtern = address.makeAddrExtern(addrNumber, bitCnt);
Constructs address
of type addr_extern with given value with bitCnt bit-length.
<address>.wid returns (int8);
Returns the workchain id of addr_std or addr_var. Throws "range check error" exception for other address
types.
<address>.value returns (uint);
Returns the address
value of addr_std or addr_var if addr_var has 256-bit address
value. Throws "range check error" exception for other address
types.
address(this).balance returns (varuint16);
Returns balance of the current contract account in nanoevers.
address(this).currencies returns (mapping(uint32 => varuint32));
Returns currencies on the balance of the current contract account.
<address>.getType() returns (uint8);
Returns type of the address
:
0 - addr_none
1 - addr_extern
2 - addr_std
<address>.isStdZero() returns (bool);
Returns the result of comparison between this address
with zero address
of type addr_std.
<address>.isStdAddrWithoutAnyCast() returns (bool);
Checks whether this address
is of type addr_std without any cast.
<address>.isExternZero() returns (bool);
Returns the result of comparison between this address
with zero address
of type addr_extern.
<address>.isNone() returns (bool);
Checks whether this address
is of type addr_none.
<address>.unpack() returns (int32 /*wid*/, uint256 /*value*/);
Parses address
containing a valid MsgAddressInt
(addr_std
), applies rewriting
from the anycast (if exists) to the same-length prefix of the address, and returns both the
workchain wid
and the 256-bit address value
. If the address value
is not 256-bit, or if
address
is not a valid serialization of MsgAddressInt
, throws a cell deserialization
exception.
It is wrapper for opcode REWRITESTDADDR
.
Example:
(int32 wid, uint addr) = address(this).unpack();
<address>.transfer(varuint16 value, bool bounce, uint16 flag, TvmCell body, mapping(uint32 => varuint32) currencies, TvmCell stateInit);
Sends an internal outbound message to the address
. Function parameters:
value
(varuint16
) - amount of nanoevers sent attached to the message. Note: the sent value is withdrawn from the contract's balance even if the contract has been called by internal inbound message.currencies
(mapping(uint32 => varuint32)
) - additional currencies attached to the message. Defaults to an empty set.bounce
(bool
) - if it's set and transaction (generated by the internal outbound message) falls (only at the computing phase, not at the action phase!), then funds will be returned. Otherwise, (flag isn't set or transaction terminated successfully) the address accepts the funds even if the account doesn't exist or is frozen. Defaults totrue
.flag
(uint16
) - flag that used to send the internal outbound message. Defaults to0
.body
(TvmCell
) - body (payload) attached to the internal message. Defaults to an empty TvmCell.stateInit
(TvmCell
) - represents fieldinit
ofMessage X
. IfstateInit
has a wrong format, a cell underflow exception at the computing phase is thrown. See here. Normally,stateInit
is used in 2 cases: to deploy the contract or to unfreeze the contract.
All parameters can be omitted, except value
.
Possible values of parameter flag
:
0
- message carries funds equal to thevalue
parameter. Forward fee is subtracted from thevalue
.128
- message carries all the remaining balance of the current smart contract. Parametervalue
is ignored. The contract's balance will be equal to zero after the message processing.64
- carries funds equal to thevalue
parameter plus all the remaining value of the inbound message (that initiated the contract execution).
Parameter flag
can also be modified:
flag + 1
- means that the sender wants to pay transfer fees separately from contract's balance.flag + 2
- means that any errors arising while processing this message during the action phase should be ignored. But if the message has wrong format, then the transaction fails and+ 2
has no effect.flag + 32
- means that the current account must be destroyed if its resulting balance is zero. For example,flag: 128 + 32
is used to send all balance and destroy the contract.
In order to clarify flags usage see this sample.
address dest = ...;
uint128 value = ...;
bool bounce = ...;
uint16 flag = ...;
TvmCell body = ...;
mapping(uint32 => varuint32) c = ...;
TvmCell stateInit = ...;
// sequential order of parameters
addr.transfer(value);
addr.transfer(value, bounce);
addr.transfer(value, bounce, flag);
addr.transfer(value, bounce, flag, body);
addr.transfer(value, bounce, flag, body, c);
// using named parameters
destination.transfer({value: 1 ever, bounce: false, flag: 128, body: cell, currencies: c});
destination.transfer({bounce: false, value: 1 ever, flag: 128, body: cell});
destination.transfer({value: 1 ever, bounce: false, stateInit: stateInit});
See example of address.transfer()
usage:
TVM Solidity compiler expands mapping
type with the following functions. In examples
below \<map\>
defines the object of mapping(KeyType => ValueType)
type.
Address, bytes, string, bool, contract, enum, fixed bytes, integer and struct types can
be used as a KeyType
. Struct type can be used as KeyType
only if it contains only
integer, boolean, fixed bytes or enum types and fits ~1023 bit. Example of mapping that
has a struct as a KeyType
:
struct Point {
uint x;
uint y;
uint z;
}
mapping(Point => address) map;
function test(uint x, uint y, uint z, address addr) public {
Point p = Point(x, y, z);
map[p] = addr;
}
If you use mapping(KeyType => TvmSlice) map;
be sure that sum of bit-length of KeyType
and bit-length of TvmSlice
is less than 1023 bit.
Struct KeyType
can be used to sort keys of the mapping in ascending order. In the example
above addresses in the mapping are sorted by their keys. x
field of the Point struct
is used in comparison first, second is y
and the last is z
.
If bytes
, string
or TvmCell
types are used as KeyType
, then mapping
stores
only hashes of mapping keys. That's why for these types the delMin
/min
/next
and
another mapping methods return uint256
as key (not bytes
/string
/TvmCell
).
If you use mapping as an input or output param for public/external functions,
KeyType
of the mapping can only be of type address
or of int<M>
/uint<M>
types with M from 8 to 256.
See example of how to work with mappings:
Keyword emptyMap
is a constant that is used to indicate a mapping of arbitrary type without values.
Example:
struct Stakes {
uint total;
mapping(uint => uint) stakes;
}
// create struct with empty mapping `stakes`
Stakes stakes = Stakes({stakes: emptyMap, total: 200});
<map>.operator[](KeyType index) returns (ValueType);
Returns the item of ValueType
with index key or returns the default value
if key is not in the mapping.
<map>.operator[](KeyType index) returns (ValueType);
Returns the item of ValueType
with index key. Throws an exception if key
is not in the mapping.
<map>.min() returns (optional(KeyType, ValueType));
<map>.max() returns (optional(KeyType, ValueType));
Computes the minimal (maximal) key in the mapping
and returns an optional
value containing that key and the associated value. If mapping
is empty,
this function returns an empty optional
.
<map>.next(KeyType key) returns (optional(KeyType, ValueType));
<map>.prev(KeyType key) returns (optional(KeyType, ValueType));
Computes the minimal (maximal) key in the mapping
that is lexicographically
greater (less) than key and returns an optional
value containing that
key and the associated value. Returns an empty optional
if there is no such key.
If KeyType is an integer type, argument for this functions can not possibly fit KeyType
.
Example:
KeyType key;
// init key
optional(KeyType, ValueType) nextPair = map.next(key);
optional(KeyType, ValueType) prevPair = map.prev(key);
if (nextPair.hasValue()) {
(KeyType nextKey, ValueType nextValue) = nextPair.get(); // unpack optional value
// using nextKey and nextValue
}
mapping(uint8 => uint) m;
optional(uint8, uint) = m.next(-1); // ok, param for next/prev can be negative
optional(uint8, uint) = m.prev(65537); // ok, param for next/prev can not possibly fit to KeyType (uint8 in this case)
<map>.nextOrEq(KeyType key) returns (optional(KeyType, ValueType));
<map>.prevOrEq(KeyType key) returns (optional(KeyType, ValueType));
Computes the minimal (maximal) key in the mapping
that is lexicographically greater than
or equal to (less than or equal to) key and returns an optional
value containing that
key and the associated value. Returns an empty optional
if there is no such key.
If KeyType is an integer type, argument for this functions can not possibly fit KeyType
.
<map>.delMin() returns (optional(KeyType, ValueType));
<map>.delMax() returns (optional(KeyType, ValueType));
If mapping is not empty, then this function computes the minimal (maximum) key of the mapping
,
deletes that key and the associated value from the mapping
and returns an optional
value
containing that key and the associated value. Returns an empty optional
if there is no such key.
<map>.fetch(KeyType key) returns (optional(ValueType));
Checks whether key exists in the mapping
and returns an optional
with the associated value.
Returns an empty optional
if there is no such key.
<map>.exists(KeyType key) returns (bool);
Returns whether key exists in the mapping
.
<map>.empty() returns (bool);
Returns whether the mapping
is empty.
<map>.replace(KeyType key, ValueType value) returns (bool);
Sets the value associated with key only if key exists in the mapping
and
returns the success flag.
<map>.add(KeyType key, ValueType value) returns (bool);
Sets the value associated with key only if key does not exist in the mapping
.
<map>.getSet(KeyType key, ValueType value) returns (optional(ValueType));
Sets the value associated with key, but also returns an optional
with the
previous value associated with the key, if any. Otherwise, returns an empty optional
.
<map>.getAdd(KeyType key, ValueType value) returns (optional(ValueType));
Sets the value associated with key, but only if key does not exist in the mapping
.
Returns an optional
with the old value without changing the dictionary if that value exists
in the mapping
, otherwise returns an empty optional
.
<map>.getDel(KeyType key) returns (optional(ValueType));
Deletes the key from the mapping
map and returns an optional
with the corresponding value. Returns an empty optional if the key does not exist.
<map>.getReplace(KeyType key, ValueType value) returns (optional(ValueType));
Sets the value associated with key, but only if key exists in the mapping
.
On success, returns an optional
with the old value associated with the key.
Otherwise, returns an empty optional
.
fixed
/ ufixed
: Signed and unsigned fixed point number of various sizes. Keywords ufixedMxN
and fixedMxN
, where M
represents the number of bits taken by the type and N
represents how
many decimal points are available. M
must be divisible by 8 and goes from 8 to 256 bits. N
must
be between 0 and 80, inclusive. ufixed
and fixed
are aliases for ufixed128x18
and
fixed128x18
, respectively.
Operators:
- Comparison:
<=
,<
,==
,!=
,>=
,>
(evaluate tobool
) - Arithmetic operators:
+
,-
, unary-
,*
,/
,%
(modulo) - Math operations: math.min(), math.max(), math.minmax(), math.abs(), math.divr(), math.divc()
(1)
<map>.keys() returns (KeyType[]);
(2)
<map>.values() returns (ValueType[]);
(1) Returns all mapping's keys/values.
(2) Returns all values of the mapping as an array.
Note: these functions iterate over the whole mapping, thus the cost is proportional to the mapping's size.
mapping(uint16 => uint8) map;
map[11] = 10;
map[22] = 20;
map[33] = 30;
uint16[] keys = map.keys(); // keys == [11, 22, 33]
uint8[] values = map.values(); // values == [10, 20, 30]
Function types are the types of functions. Variables of function type can be assigned from functions and function parameters of function type can be used to pass functions to and return functions from function calls.
If unassigned variable of function type is called, then exception with code 65 is thrown.
function getSum(int a, int b) internal pure returns (int) {
return a + b;
}
function getSub(int a, int b) internal pure returns (int) {
return a - b;
}
function process(int a, int b, uint8 mode) public returns (int) {
function (int, int) returns (int) fun;
if (mode == 0) {
fun = getSum;
} else if (mode == 1) {
fun = getSub;
}
return fun(a, b); // if `fun` isn't initialized, then exception is thrown
}
A user-defined value type allows creating a zero cost abstraction over an elementary value type. This is similar to an alias, but with stricter type requirements.
More information can be found here.
You can define an operator for user-defined type. See here.
In case of exception state variables of the contract are reverted to the state before
tvm.commit() or to the state of the contract before it was called.
Use error codes that are greater than 100 because other error codes can be
reserved.
Note: if a nonconstant error code is passed as the function argument and the error code
is less than 2, then the error code will be set to 100.
(1)
require(bool condition, [uint errorCode = 100, [Type exceptionArgument]]);
(2)
require(bool condition, string text);
require
function can be used to check the condition and throw an exception if the condition
is not met.
(1) Takes condition and optional parameters: error code and the object of any type.
(2) Takes condition and error text. Error code will be equal to 100.
Example:
uint a = 5;
require(a == 5); // ok
require(a == 6); // throws an exception with code 100
require(a == 6, 101); // throws an exception with code 101
require(a == 6, 101, "a is not equal to six"); // throws an exception with code 101 and string
require(a == 6, 101, a); // throws an exception with code 101 and number a
require(a == 6, "a is not equal to six"); // throws an exception with code 100 and string
revert(uint errorCode = 100, [Type exceptionArgument]);
revert function can be used to throw exceptions. The function takes an optional error code (unsigned integer) and the object of any type.
Example:
uint a = 5;
revert(); // throw exception 100
revert(101); // throw exception 101
revert(102, "We have a some problem"); // throw exception 102 and string
revert(101, a); // throw exception 101 and number a
Libraries are similar to contracts, but they can't have state variables
and can't inherit nor be inherited. Libraries can be seen as implicit
base contracts of the contracts that use them. They will not be
explicitly visible in the inheritance hierarchy, but calls of library
functions look just like calls of functions of explicit base contracts
(using qualified access like LibName.func(a, b, c)
). There is also
another way to call library function: obj.func(b, c)
.
For now libraries are stored as a part of the code of the contact that
uses libraries. In the future, it can be changed.
Example of using library in the manner LibName.func(a, b, c)
:
// file MathHelper.sol
pragma solidity >= 0.72.0;
// Library declaration
library MathHelper {
// State variables are forbidden in library but constants are not
uint constant MAX_VALUE = 300;
// Library function
function sum(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
require(c < MAX_VALUE);
return c;
}
}
// file MyContract.sol
pragma solidity >= 0.72.0;
import "MathHelper.sol";
contract MyContract {
uint s;
function addValue(uint value) public returns (uint) {
s = MathHelper.sum(s, value);
return s;
}
}
In TVM Solidity arguments of a function call passed by value not by
reference. It's effective for numbers and even for huge arrays.
See (TVM - A.2.3.2).
But if a library function is called like obj.func(b, c)
, then the
first argument obj
is passed by reference. It's similar to
the self
variable in Python.
The directive using A for B; can be used to attach library functions
(from the library A
) to any type (B
) in the context of the contract.
These functions will receive the object they were called for as their
first parameter.
The effect of using A for *;
is that the functions from the library
A
are attached to all types.
Example of using library in the manner obj.func(b, c)
:
// file ArrayHelper.sol
pragma solidity >= 0.72.0;
library ArrayHelper {
// Delete value from the `array` at `index` position
function del(uint[] array, uint index) internal pure {
for (uint i = index; i + 1 < array.length; ++i){
array[i] = array[i + 1];
}
array.pop();
}
}
// file MyContract.sol
pragma solidity >= 0.72.0;
import "ArrayHelper.sol";
contract MyContract {
// Attach library function `del` to the type `uint[]`
using ArrayHelper for uint[];
uint[] array;
constructor() {
array = [uint(100), 200, 300];
}
function deleteElement(uint index) public {
// Library function call via object.
// Note: library function `del` has 2 arguments:
// array is passed by reference and index is passed by value
array.del(index);
}
}
Free functions can be called via object as well as library functions. Use directive using A for B;. Example:
pragma tvm-solidity >= 0.72.0;
// Delete value from the `array` at `index` position
function del(uint[] array, uint index) {
for (uint i = index; i + 1 < array.length; ++i){
array[i] = array[i + 1];
}
array.pop();
}
contract MyContract {
// Attach function `del` to the type `uint[]`
using {del} for uint[];
uint[] public array;
constructor() {
array = [uint(100), 200, 300];
}
function deleteElement(uint index) public {
// Free function call via object.
// Note: free function `del` has 2 arguments:
// array is passed by reference and index is passed by value
array.del(index);
}
}
pragma
keyword is used to enable certain compiler features or checks.
A pragma directive is always local to a source file, so you have to add
the pragma to all your files if you want to enable it in your whole project.
If you import another file, the pragma from that file is not
automatically applied to the importing file.
pragma tvm-solidity >= 0.35.5; // Check if the compiler version is greater or equal than 0.35.5
pragma tvm-solidity ^ 0.35.5; // Check if the compiler version is greater or equal than 0.35.5 and less than 0.36.0
pragma tvm-solidity < 0.35.5; // Check if the compiler version is less than 0.35.5
pragma tvm-solidity >= 0.35.5 < 0.35.7; // Check if the compiler version is equal to either 0.35.5 or 0.35.6
Used to restrict source file compilation to the particular compiler versions.
pragma copyleft <type>, <wallet_address>;
Capabilities required: CapCopyleft
.
Parameters:
<type>
(uint8
) - copyleft type.<wallet_address>
(uint256
) - author's wallet address in masterchain.
If contract has the copyleft
pragma, it means that after each transaction some part of validator's fee
is transferred to <wallet_address>
according to the <type>
rule.
For example:
pragma copyleft 0, 0x2cfbdc31c9c4478b61472c72615182e9567595b857b1bba9e0c31cd9942f6ca41;
pragma ignoreIntOverflow;
All arithmetic operations revert on over- and underflow by default. This pragma turns off such behavior. It is up to programmer to control range of integer types if they use the pragma.
For example:
pragma ignoreIntOverflow;
uint8 a = 2;
uint8 b = 3;
uint8 c = a - b; // c == -1, no exception thrown
See also: unchecked block.
pragma AbiHeader notime;
pragma AbiHeader pubkey;
pragma AbiHeader expire;
Defines headers that are used in external messages:
notime
- disablestime
abi header, which is enabled by default. Abi headertime
–uint64
local time when message was created, used for replay protectionpubkey
(uint256
) - optional public key that the message can be signed with.expire
(uint32
) - time when the message should be meant as expired.
Note:
Defined headers are listed in *.abi.json
file in header
section.
See also: Contract execution, afterSignatureCheck, msg.pubkey() and tvm.pubkey(). To read more about these fields and ABI follow this link. Here is example of message expiration time usage.
pragma msgValue <value>;
Allows specifying default value in nanoevers attached to the internal outbound messages used to call another contract. If it's not specified, this value is set to 10 000 000 nanoevers.
Example:
pragma msgValue 123456789;
pragma msgValue 1e8;
pragma msgValue 10 ever;
pragma msgValue 10_000_000_123;
pragma upgrade func;
pragma upgrade oldsol;
Defines that code is compiled with special selector that is needed to upgrade FunC/Solidity contracts.
You can decode state variables using ever-cli. See ever-cli decode account --help
.
See also: abi.decodeData().
For constant
variables, the value has to be a compile time constant and this value is
substituted where the variable is used. The value has to be assigned where the variable is declared.
Example:
contract MyContract {
uint constant cost = 100;
uint constant cost2 = cost + 200;
address constant dest = address.makeAddrStd(-1, 0x89abcde);
}
Static state variables are used in the contract initial state generation. Such variables can be set while deploying contract from contract (onchain) or by tvm-linker (offchain). Example:
contract C {
uint static a; // ok
uint static b = 123; // error
}
See also:
nostorage
state variables are not saved in the contract's storage. They have default values at the
beginning of each transaction.
contract C {
uint nostorage m_value;
function f() public {
// here m_value == 0
++m_value;
// here m_value == 1
}
}
For each public state variable, a getter function is generated. Generated function has the same name and return type as the public variable. This function can be called only locally. Public state variables are useful, because you don't need to write functions that return a particular state variable manually.
Example:
contract C {
uint public a;
uint public static b; // it's ok. Variable is both public and static.
}
receive
function is called in two cases:
- msg.body (or message body) is empty.
- msg.body starts with 32-bit zero. Then message body may contain data, for example string with comment.
If in the contract there is no receive
function, then the contract has an implicit empty receive
function.
// file sink.sol
contract Sink {
uint public counter = 0;
uint public msgWithPayload = 0;
receive() external {
++counter;
// if the inbound internal message has payload, then we can get it using `msg.body`
TvmSlice s = msg.body;
if (!s.empty()) {
++msgWithPayload;
}
}
}
// file bomber.sol
contract Bomber {
// This function send evers 3 times to the Sink contract. Sink's function receive will handle
// that messages.
function f(address addr) pure public {
tvm.accept();
addr.transfer({value: 1 ever}); // message's body is empty
TvmBuilder b;
addr.transfer({value: 1 ever, body: b.toCell()}); // message's body is empty, too
b.store(uint32(0), "Thank you for the coffee!");
// body of the message contains 32-bit zero number and the string
addr.transfer({value: 20 ever, body: b.toCell()});
}
}
fallback
function is called on receiving an inbound internal/external message in such cases:
- The message contains a function id that the contract doesn't contain.
- Bit-length of the message is from 1 to 31 (including).
- Bit-length of the message is equal to zero, but the message contains reference(s).
Note: if the message has correct function id but invalid encoded function parameters, then the transaction fail with an exception (e.g. cell underflow exception).
If in the contract there is no fallback function, then contract has implicit fallback function that throws exception.
Example:
// file ContractA.sol
contract ContractA {
uint public counter = 0;
function f(uint a, uint b) public pure { /*...*/ }
fallback() external {
++counter;
}
}
// file ContractB.sol
import "ContractA.sol";
import "ContractAnother.sol";
contract ContractB {
// Let's condider that `addr` is ContractA's address. 4 messages are send. ContractA's fallback
// function will handle that messages except the last one.
function g(address addr) public pure {
tvm.accept();
// The message contains a function id that the contract doesn't contain.
// There is wrong casting to ContractAnother. `addr` is ContractA's address.
ContractAnother(addr).sum{value: 1 ever}(2, 2);
{
TvmBuilder b;
b.storeUnsigned(1, 1);
// Bit-length of the message is equal to 20 bits.
addr.transfer({value: 2 ever, body: b.toCell()});
}
{
TvmBuilder b;
b.storeRef(b);
// Bit-length of the message is equal to zero but the message contains one reference.
addr.transfer({value: 1 ever, body: b.toCell()});
}
TvmBuilder b;
uint32 id = abi.functionId(ContractA.f);
b.store(id, uint(2));
// ContractA's fallback function won't be called because the message body doesn't contain
// the second ContractA.f's parameter. It will cause cell underflow exception in ContractA.
addr.transfer({value: 1 ever, body: b.toCell()});
}
}
onBounce(TvmSlice body) external {
/*...*/
}
onBounce
function is executed when contract receives a bounced inbound internal message.
The message is generated by the network if the contract sends an internal message with bounce: true
and either
- called contract doesn't exist;
- called contract fails at the storage/credit/computing phase (not at the action phase!)
The message is generated only if the remaining message value is enough for sending one back.
body
depends on TVM realisation and settings:
body
can be empty.- If
CapBounceMsgBody
capability is set, thenbody
contains at most 256 data bits of the original message (without references). Note: the function id takes32
bits and the function's parameters can take at most224
bits. - If
CapBounceMsgBody
andCapFullBodyInBounced
capabilities are set, thenbody
is the same as in the second option, butbody
contains the full original message in the first reference.
If onBounce
function is not defined, then the contract does
nothing on receiving a bounced message.
If the onBounce
function throws an exception, then another bounced messages are not generated.
Example of how to use onBounce
function for option 2:
Example of getting function ID if CapBounceMsgBody
and CapFullBodyInBounced
capabilities are set:
onBounce(TvmSlice body) external {
TvmSlice fullBody = body.loadRef().toSlice();
uint32 functionId = fullBody.load(uint32);
}
onTickTock
function is executed on tick/tock transaction.
These transactions are automatically generated for certain special accounts.
See (TBLKCH - 4.2.4.)
For tick transactions isTock is false, for tock transactions - true.
Prototype:
onTickTock(bool isTock) external {
/*...*/
}
onCodeUpgrade
function can have an arbitrary set of arguments and should be
executed after tvm.setcode() function call. In this function
tvm.resetStorage() should be called if the set of state
variables is changed in the new version of the contract. This function implicitly
calls tvm.commit(). onCodeUpgrade
function does not return value. onCodeUpgrade
function
finishes TVM execution with exit code 0.
Prototype:
function onCodeUpgrade(...) private {
/*...*/
}
Function onCodeUpgrade
had function id = 2 (for compiler <= 0.65.0). Now, it has another id, but you can set
functionID(2)
in new contracts for the onCodeUpgrade
to upgrade old ones.
See example of how to upgrade code of the contract:
It's good to pass TvmCell cell
to the public function that calls onCodeUpgrade(TvmCell cell, ...)
function. TvmCell cell
may contain some data that may be useful for the new contract.
// old contract
// Public function that changes the code and takes some cell
function updateCode(TvmCell newcode, TvmCell cell) public pure checkPubkeyAndAccept {
tvm.setcode(newcode);
tvm.setCurrentCode(newcode);
// pass cell to new contract
onCodeUpgrade(cell);
}
function onCodeUpgrade(TvmCell cell) private pure {
}
// new contract
function onCodeUpgrade(TvmCell cell) private pure {
// new code can use cell that was passed from the old version of the contract
}
function afterSignatureCheck(TvmSlice body, TvmCell message) private inline returns (TvmSlice) {
/*...*/
}
afterSignatureCheck
function is used to define custom replay protection function instead of the
default one.
NB: Do not use tvm.commit() or tvm.accept() in this function as it called before the constructor call.
time and expire header fields can be used for replay protection and, if set, they should be read in afterSignatureCheck
.
See also: Contract execution.
See an example of how to define this function:
Function mutability shows how this function treats state variables. Possible values of the function mutability:
pure
- function neither reads nor assigns state variables;view
- function may read but doesn't assign state variables;- default - function may read and/or assign state variables.
Example:
contract Test {
uint a;
event MyEvent(uint val);
// pure mutability
function f() public pure {
emit MyEvent(123);
}
// view mutability
function g() public view {
emit MyEvent(a);
}
// default mutability (not set)
function e(uint newA) public {
a = newA;
}
}
inline
specifier instructs the compiler to insert a copy of the private function
body into each place where the function is called.
Keyword can be used only for private and internal functions.
Example:
// This function is called as a usual function.
function getSum(uint a, uint b) public returns (uint) {
return sum(a, b);
}
// Code of this function is inserted in place of the call site.
function sum(uint a, uint b) private inline returns (uint) {
return a + b;
}
To make inline assembler you should mark free function as assembly
. Function body must contain lines of assembler code separated by commas.
It is up to user to set correct mutability (pure
, view
or default), return parameters of the function and so on.
function checkOverflow(uint a, uint b) assembly pure returns (bool) {
"QADD",
"ISNAN",
"NOT",
}
contract Contract {
function f(uint a, uint b) private {
bool ok = checkOverflow(a, b);
if (ok) {
uint c = a + b;
}
}
}
You can use inline assembler to support new opcodes in experimental or another implementations of TVM.
function incomingValue() assembly pure returns (uint) {
".blob xF82B", // it's opcode INCOMINGVALUE
}
functionID
keyword allows assigning function identifier explicitly.
Each public function has a unique 32-bit identifier (id). id 0 is reserved for receive function.
In case functionID
is not defined explicitly, it is calculated as a hash of the function signature.
In general, there is no purpose to set the function id manually.
Example:
function f() public pure functionID(123) {
/*...*/
}
Keywords externalMsg
and internalMsg
specify which messages the function can handle.
If the function marked by keyword externalMsg
is called by internal message, the function throws an
exception with code 71.
If the function marked by keyword internalMsg
is called by external message, the function throws
an exception with code 72.
Example:
function f() public externalMsg { // this function receives only external messages
/*...*/
}
// Note: keyword `external` specifies function visibility
function ff() external externalMsg { // this function also receives only external messages
/*...*/
}
function g() public internalMsg { // this function receives only internal messages
/*...*/
}
// These function receives both internal and external messages.
function fun() public { /*...*/ }
emit
statement sends an external outbound message. Use {dest: ...}
to set destination address.
The address must be of addr_extern type.
If option dest
is not used, the destination address is set to addr_none.
Note: fee for creating the external outbound message is withdrawn from the contract's balance even if the contract has been called by internal inbound message.
Example:
event SomethingIsReceived(uint a, uint b, uint sum);
...
address addr = address.makeAddrExtern(...);
emit SomethingIsReceived{dest: addr}(2, 8, 10); // dest address is set
emit SomethingIsReceived(10, 15, 25); // dest address == addr_none
return
statement has different effects depending on:
- visibility of the function;
- type of the inbound message;
- presence of the
responsible
modifier.
In private
or internal
visible functions, return
assigns the return variables to the specified parameters;
In public
or external
visible functions, return
statement:
- on an external inbound message, generates an external outbound message with a destination address set to the source address of the inbound (external) message. All arguments of the return statement are ignored.
- on an external inbound message:
- if the function is marked as
responsible
, generates an internal outbound message; - otherwise has no effect.
- if the function is marked as
value
, bounce
, flag
and currencies
options are used to create the message. Some options can
be omitted. See <address>.transfer() where these options and their default
values are described.
Note: if the function f
below is called with n = 5
and internal/external message must be
generated, then only one message is sent with result 120
(120 = 5 * 4 * 3 * 2 * 1).
Hint: Use {value: 0, bounce: false, flag: 64}
for responsible
function.
Because flag: 0
is used by default.
See also: External function calls.
function f(uint n) public pure {
return n <= 1 ? 1 : n * f(n - 1);
}
function f(uint n) public responsible pure {
return{value: 0, bounce: false, flag: 64} n <= 1 ? 1 : n * f(n - 1);
}
TVM Solidity compiler allows specifying different parameters of the outbound internal message that
is sent via external function call. Note, all external function calls are asynchronous, so
callee function will be called after termination of the current transaction.
value
, currencies
, bounce
, flag
and stateInit
options can be set. See <address>.transfer()
where these options are described.
Note: if value
isn't set, then the default value is equal to 0.01 ever, or 10^7 nanoever. It's equal
to 10_000 units of gas in workchain.
If the callee function returns some value and marked as responsible
, then callback
option must be set.
This callback function will be called by another contract. Remote function will pass its return
values as function arguments for the callback function. That's why types of return values of the
callee function must be equal to function arguments of the callback function.
If the function marked as responsible
, then field answerId
appears in the list of input parameters of the
function in *abi.json
file. answerId
is function id that will be called.
Example of the external call of the function that returns nothing:
interface IContract {
function f(uint a) external;
}
contract Caller {
function callExt(address addr) public {
IContract(addr).f(123); // attached default value: 0.01 ever
IContract(addr).f{value: 10 ever}(123);
IContract(addr).f{value: 10 ever, flag: 3}(123);
IContract(addr).f{value: 10 ever, bounce: true}(123);
IContract(addr).f{value: 1 micro, bounce: false, flag: 128}(123);
mapping(uint32 => varuint32) cc;
cc[12] = 1000;
IContract(addr).f{value: 10 ever, currencies:cc}(123);
}
}
Example of the external call of the function that returns some values:
contract RemoteContract {
// Note this function is marked as responsible to call callback function
function getCost(uint x) public pure responsible returns (uint) {
uint cost = x == 0 ? 111 : 222;
// return cost and set option for outbound internal message.
return{value: 0, bounce: false, flag: 64} cost;
}
}
contract Caller {
function test(address addr, uint x) public pure {
// `getCost` returns result to `onGetCost`
RemoteContract(addr).getCost{value: 1 ever, callback: Caller.onGetCost}(x);
}
function onGetCost(uint cost) public {
// Check if msg.sender is expected address
// we get cost value, we can handle this value
}
}
*.abi.json
for responsible
function getCost
:
{
"name": "getCost",
"inputs": [
{"name":"answerId","type":"uint32"},
{"name":"x","type":"uint256"}
],
"outputs": [
{"name":"value0","type":"uint256"}
]
}
See also:
- Example of callback usage: 24_SquareProvider
- Example of callback usage: 4.1_CentralBank and 4.1_CurrencyExchange.sol
- return
As in classic Solidity delete
operation assigns the initial value for the type to a variable.
Delete operation can be applied not only to variables itself, but to its fields or index values.
Example:
int a = 5;
delete a; // a == 0
uint[] arr;
arr.push(11);
arr.push(22);
arr.push(33);
delete arr[1];
// arr[0] == 11
// arr[1] == 0
// arr[2] == 33
delete arr; // arr.length == 0
mapping(uint => uint) l_map;
l_map[1] = 2;
delete l_map[1];
bool e = l_map.exists(1); // e == false
l_map[1] = 2;
delete l_map;
bool e = l_map.exists(1); // e == false
struct DataStruct {
uint m_uint;
bool m_bool;
}
DataStruct l_struct;
l_struct.m_uint = 1;
delete l_struct; // l_struct.m_uint == 0
TvmBuilder b;
uint i = 0x54321;
b.store(i);
TvmCell c = b.toCell();
delete c;
TvmCell empty; // tvm.hash(empty) == tvm.hash(c)
b.store(c);
TvmSlice slice = b.toSlice();
uint16 bits = slice.bits(); // bits == 256
uint8 refs = slice.refs(); // refs == 1
delete slice;
uint16 bits = slice.bits(); // bits == 256
uint8 refs = slice.refs(); // refs == 1
uint16 bits = b.bits(); // bits == 256
uint8 refs = b.refs(); // refs == 1
delete b;
uint16 bits = b.bits(); // bits == 256
uint8 refs = b.refs(); // refs == 1
The expression type(T)
can be used to retrieve information about the type T.
The following properties are available for an integer, variable integer and enum type T
:
type(T).min
- the smallest value representable by typeT
.type(T).max
- the largest value representable by typeT
.
msg.sender (address)
Returns:
- sender of the message for internal message.
- address(0) for external message.
- address(0) for tick/tock transaction.
msg.value (varuint16)
Returns:
- Balance of the inbound message in nanoevers for internal message.
- 0 for external message.
- Undefined value for tick/tock transaction.
msg.currencies (mapping(uint32 => varuint32))
Collections of arbitrary currencies contained in the balance of the inbound message.
msg.pubkey() returns (uint256);
Returns public key that is used to check the message signature. If the message isn't signed, then it's equal to 0
.
See also: Contract execution, pragma AbiHeader.
Returns flag whether the contract is called by internal message, external message or by tick/tock transactions.
msg.createdAt (uint32)
Returns the field created_at of the external inbound message.
msg.data (TvmCell)
Returns the whole message.
msg.forwardFee (varuint16)
Returns:
- the forward fee for the internal inbound message.
0
for the external inbound message.
msg.importFee (varuint16)
Returns:
- the field import_fee for external inbound message. Note: field
import_fee
is set offchain by user as they want and does not reflect the real import fee of the message. 0
for the internal inbound message.
msg.body (TvmSlice)
Returns the payload (message body) of an inbound message.
msg.hasStateInit (bool)
Whether the internal/external inbound message contains field stateInit
.
Returns undefined value for tick/tock transaction. See TL-B scheme of Message X
.
tvm.accept();
Executes TVM instruction "ACCEPT" (TVM - A.11.2). This instruction sets current gas limit to its maximal allowed value. This action is required to process external messages that bring no value.
See example of how to use this function:
tvm.setGasLimit(uint g);
Executes TVM instruction "SETGASLIMIT" (TVM - A.11.2).
Sets current gas limit gl to the minimum of g and gm, and resets the gas
credit gc to zero. If the gas consumed so far (including the present instruction) exceeds
the resulting value of gl, an (unhandled) out of gas exception is thrown before setting
new gas limits. Notice that tvm.setGasLimit(...)
with an argument g ≥ 263 - 1 is
equivalent to tvm.accept()
.
tvm.setGasLimit()
is similar to tvm.accept()
. tvm.accept()
sets gas limit gl to
the maximum possible value (depends on the network configuration parameters, usually is equal to
1_000_000 units of gas). tvm.setGasLimit()
is generally used for accepting external messages and restricting
max possible gas consumption. It may be used to protect from flood by "bad" owner
in a contract that is used by multiple users. Let's consider some scenario:
- Check whether msg.pubkey() != 0 and msg.pubkey() belongs to the list of trusted public keys;
- Check whether
m_floodCounter[msg.pubkey()] < 5
where m_floodCounter is count of pending operations ofmsg.pubkey()
user. tvm.setGasLimit(75_000);
accept external message and set gas limit to 75_000.++m_floodCounter[msg.pubkey()];
increase count of pending operations for current users.tvm.commit();
save current state if it needs- Do other things.
So if some user's public key will be stolen, then a hacker can spam with external messages and
burn at most 5 * 75_000
units of gas instead of 5 * 1_000_000
, because we use tvm.setGasLimit()
instead
of tvm.accept()
.
tvm.buyGas(uint value);
Computes the amount of gas that can be bought for value
nanoevers, and sets gl
accordingly in the same way as tvm.setGasLimit().
tvm.commit();
Creates a "check point" of the state variables (by copying them from c7 to c4) and register c5. If the contract throws an exception at the computing phase, then the state variables and register c5 will roll back to the "check point", and the computing phase will be considered "successful". If contract doesn't throw an exception, it has no effect.
tvm.rawCommit();
Same as tvm.commit() but doesn't copy the state variables from c7 to c4. It's a wrapper
for opcode COMMIT
. See TVM.
Note: Don't use tvm.rawCommit()
after tvm.accept()
in processing external messages because
you don't save from c7 to c4 the hidden state variable timestamp
that is used for replay protection.
tvm.getData() returns (TvmCell);
Note: Function is experimental.
A dual of the tvm.setData()
function. It returns value of the c4
register. Obtaining a raw storage
cell can be useful when upgrading a new version of the contract that introduces an altered data layout.
Manipulation with a raw storage cell requires understanding of the way the compiler stores the data.
Refer to the description of tvm.setData()
below to get more details.
Note: state variables and replay protection timestamp stored in the data cell have the same values
that were before the transaction. See tvm.commit() to learn about register c4
update.
tvm.setData(TvmCell data);
Note: Function is experimental.
Stores cell data
in the register c4
. Mind that after returning from a public function all state variables
from c7
are copied to c4
and tvm.setData
will have no effect. Example hint, how to set c4
:
TvmCell data = ...;
tvm.setData(data); // set register c4
tvm.rawCommit(); // save register c4 and c5
revert(200); // throw the exception to terminate the transaction
Be careful with the hidden state variable timestamp
and think about possibility of external
messages replaying.
tvm.log(string log);
logtvm(string log);
Dumps log
string. This function is a wrapper for TVM instructions
PRINTSTR
(for constant literal strings shorter than 16 symbols) and
STRDUMP
(for other strings). logtvm
is an alias for tvm.log(string)
. Example:
tvm.log("Hello, world!");
logtvm("99_Bottles");
string s = "Some_text";
tvm.log(s);
Note: For long strings dumps only the first 127 symbols.
tvm.hexdump(T a);
tvm.bindump(T a);
Dumps cell data or integer. Note that for cells this function dumps data only
from the first cell. T
must be an integer type or TvmCell.
Example:
TvmBuilder b;
b.storeUnsigned(0x9876543210, 40);
TvmCell c = b.toCell();
tvm.hexdump(c);
tvm.bindump(c);
uint a = 123;
tvm.hexdump(a);
tvm.bindump(a);
int b = -333;
tvm.hexdump(b);
tvm.bindump(b);
Expected output for the example:
CS<9876543210>(0..40)
CS<10011000011101100101010000110010000100001>(0..40)
7B
1111011
-14D
-101001101
tvm.setcode(TvmCell newCode);
This command creates an output action that would change this smart contract
code to that given by the TvmCell
newCode (this change will take effect only
after the successful termination of the current run of the smart contract).
See example of how to use this function:
tvm.configParam(uint8 paramNumber) returns (TypeA a, TypeB b, ...);
Executes TVM instruction "CONFIGPARAM" (TVM - A.11.4. - F832). This command returns the value of the global configuration parameter with integer index paramNumber. Argument should be an integer literal. Supported paramNumbers: 1, 15, 17, 34.
tvm.rawConfigParam(uint8 paramNumber) returns (TvmCell cell, bool status);
Executes TVM instruction "CONFIGPARAM" (TVM - A.11.4. - F832).
Returns the value of the global configuration parameter with
integer index paramNumber as a TvmCell
and a boolean status.
tvm.rawReserve(uint value, uint8 flag);
tvm.rawReserve(uint value, mapping(uint32 => varuint32) currency, uint8 flag);
Creates an output action that reserves reserve nanoevers. It is roughly equivalent to create an outbound message carrying reserve nanoevers to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. It's a wrapper for opcodes "RAWRESERVE" and "RAWRESERVEX". See TVM.
Let's denote:
original_balance
is balance of the contract before the computing phase that is equal to balance of the contract before the transaction minus storage fee. Note:original_balance
doesn't includemsg.value
andoriginal_balance
is not equal toaddress(this).balance
.remaining_balance
is contract's current remaining balance at the action phase after some handled actions and before handing the "rawReserve" action.
Let's consider how much nanoevers (reserve) are reserved in all cases of flag:
-
0 ->
reserve = value
nanoevers. -
1 ->
reserve = remaining_balance - value
nanoevers. -
2 ->
reserve = min(value, remaining_balance)
nanoevers. -
3 = 2 + 1 ->
reserve = remaining_balance - min(value, remaining_balance)
nanoevers. -
4 ->
reserve = original_balance + value
nanoevers. -
5 = 4 + 1 ->
reserve = remaining_balance - (original_balance + value)
nanoevers. -
6 = 4 + 2 ->
reserve = min(original_balance + value, remaining_balance) = remaining_balance
nanoevers. -
7 = 4 + 2 + 1 ->
reserve = remaining_balance - min(original_balance + value, remaining_balance)
nanoevers. -
12 = 8 + 4 ->
reserve = original_balance - value
nanoevers. -
13 = 8 + 4 + 1 ->
reserve = remaining_balance - (original_balance - value)
nanoevers. -
14 = 8 + 4 + 2 ->
reserve = min(original_balance - value, remaining_balance)
nanoevers. -
15 = 8 + 4 + 2 + 1 ->
reserve = remaining_balance - min(original_balance - value, remaining_balance)
nanoevers.
All other values of flag
are invalid.
To make it clear, let's consider the order of reserve
calculation:
- if
flag
has bit+8
, thenvalue = -value
. - if
flag
has bit+4
, thenvalue += original_balance
. - Check
value >= 0
. - if
flag
has bit+2
, thenvalue = min(value, remaining_balance)
. - if
flag
has bit+1
, thenvalue = remaining_balance - value
. reserve = value
.- Check
0 <= reserve <= remaining_balance
.
Example:
tvm.rawReserve(1 ever, 4 + 8);
See also: 23_rawReserve.sol
tvm.initCodeHash() returns (uint256 hash)
Returns the initial code hash that contract had when it was deployed.
Capabilities required: CapInitCodeHash
.
tvm.hash(TvmCell cellTree) returns (uint256);
tvm.hash(string data) returns (uint256);
tvm.hash(bytes data) returns (uint256);
tvm.hash(TvmSlice data) returns (uint256);
Executes TVM instruction "HASHCU" or "HASHSU" (TVM - A.11.6. - F900).
It computes the representation hash of a given argument and returns
it as a 256-bit unsigned integer. For string
and bytes
it computes
hash of the tree of cells that contains data but not data itself.
See sha256 to count hash of data.
Example:
uint256 hash = tvm.hash(TvmCell cellTree);
uint256 hash = tvm.hash(string);
uint256 hash = tvm.hash(bytes);
(1)
tvm.checkSign(uint256 dataHash, uint256 SignHighPart, uint256 SignLowPart, uint256 pubkey) returns (bool);
(2)
tvm.checkSign(uint256 dataHash, TvmSlice signature, uint256 pubkey) returns (bool);
(3)
tvm.checkSign(TvmSlice data, TvmSlice signature, uint256 pubkey) returns (bool);
Executes TVM instruction "CHKSIGNU" (TVM - A.11.6. - F910) for options 1 and 2. This command checks the Ed25519-signature of the dataHash using public key pubkey. Signature is represented by two uint256 SignHighPart and SignLowPart in the first option and by the slice signature in the second option. In the third option executes TVM instruction "CHKSIGNS" (TVM - A.11.6. - F911). This command checks Ed25519-signature of the data using public key pubkey. Signature is represented by the slice signature.
If CapSignatureWithId
capability is set, then TVM use some predefined ID during signature check. Usually ID is global_id
that can be found in the last block for example.
Example:
// option 1
uint256 dataHash;
uint256 SignHighPart;
uint256 SignLowPart;
uint256 pubkey;
bool signatureIsValid = tvm.checkSign(dataHash, SignHighPart, SignLowPart, pubkey);
// option 2
uint256 dataHash;
TvmSlice signature;
uint256 pubkey;
bool signatureIsValid = tvm.checkSign(dataHash, signature, pubkey);
// option 3
TvmSlice data;
TvmSlice signature;
uint256 pubkey;
bool signatureIsValid = tvm.checkSign(data, signature, pubkey);
Either code
or stateInit
option must be set when you deploy a contract
from contract via keyword new
. stateInit
is a tree of cells that contains
original state of the contract. stateInit
contains data
, code
and another members.
See also (TBLKCH - A.2.3.2) to read about stateInit
.
Use stateInit
option if you have the created account state (maybe offchain or
onchain) and use code
if you want to create account state in the new
expression.
Note: Address of the new account is calculated as a hash of the stateInit
.
Constructor function parameters don't influence the address. See
New contract address problem.
Step-by-step description how to deploy contracts from the contract here.
Examples:
stateInit
defines the origin state of the new account.
TvmCell stateInit = ...;
address newWallet = new SimpleWallet{value: 1 ever, stateInit: stateInit}(arg0, arg1, ...);
code
option defines the code of the new contract.
TvmCell code = ...;
address newWallet = new SimpleWallet{value: 1 ever, code: code}(arg0, arg1, ...);
The following options can only be used with the code
option:
pubkey
(uint256
) - defines the public key of the new contract.varInit
(initializer list
) - used to set static variables of the new contract.splitDepth
(uint8
) - splitting depth.0 <= splitDepth <= 31
. By default, it has no value.
Example of usage of these options:
// file SimpleWallet.sol
...
contract SimpleWallet {
address static m_owner;
uint static m_value;
...
}
// file containing a contract that deploys a SimpleWallet
TvmCell code = ...;
address newWallet = new SimpleWallet{
value: 1 ever,
code: code,
pubkey: 0xe8b1d839abe27b2abb9d4a2943a9143a9c7e2ae06799bd24dec1d7a8891ae5dd,
splitDepth: 15,
varInit: {m_owner: address(this), m_value: 15}
}(arg0, arg1, ...);
The following options can be used with both stateInit
and code
:
value
(varuint16
) - funds attached to the outbound internal message, that creates new account. This value must be set.currencies
(mapping(uint32 => varuint32)
) - currencies attached to the outbound internal message. Defaults to an empty set.bounce
(bool
) - if it's set and deploy falls (only at the computing phase, not at the action phase!), then funds will be returned. Otherwise, (flag isn't set or deploying terminated successfully) the address accepts the funds. Defaults totrue
.wid
(uint8
) - workchain id of the new account address. Defaults to0
.flag
(uint16
) - flag used to send the outbound internal message. Defaults to0
. Possible values of theflag
are described here: <address>.transfer().
TvmCell stateInit = ...;
address newWallet = new SimpleWallet{
stateInit: stateInit,
value: 1 ever,
wid: -1,
flag: 0
}(arg0, arg1, ...);
You can also deploy the contract via <address>.transfer().
Just set the option stateInit
.
If the contract does not have constructor explicitly and does not have state variables with initialisation, then in *.abi.json
file there is no constructor
function and no _constructorFlag
field.
For example: 1_Accumulator_no_ctor.sol and 1_Accumulator_no_ctor.abi.json. To deploy this contractor by external message with help ever-cli
, use parameter method
for deploy
and deployx
commands:
ever-cli deploy --method add '{"delta": 123}' ...
To deploy a contractor by internal message, use option stateInit
for External function calls. See deployNoConstructor
and deployNoConstructor2
functions 11_ContractDeployer.sol as samples of deploying 11_Waller_no_constructor.sol.
Address of the new account is calculated as a hash of the stateInit
.
Parameters of the constructor don't influence the address. The problem
is that hacker can deploy the contract with your stateInit
before you
with malicious constructor parameters.
Let's consider how to protect against this problem:
- Constructor is called by external message. We must Check if we didn't forget to set the public key in the contract and the inbound message is signed by that key. If hacker doesn't have your private key, then he can't sign message to call the constructor. See constructor of WalletProducer.
- Constructor is called by internal message.
We should define static variable in the new contract that will contain
address of the creator. Address of the creator will be a part of the
stateInit
. And in the constructor we must check address of the message sender. See functiondeployWallet
how to deploy contract.
See constructor of SimpleWallet.
If some contract should deploy plenty of contracts (with some contract's public key), then it's a good idea to declare static variable in the deployed contract. This variable can contain some sequence number. It will allow each new contact to have uniquestateInit
. See SimpleWallet.
Note: contract's public key (tvm.pubkey()
) is a part ofstateInit
.
tvm.code() returns (TvmCell);
Returns contract's code. Capabilities required: CapMycode
.
See SelfDeployer.
tvm.pubkey() returns (uint256);
Returns contract's public key, stored in contract data. If key is not set, function returns 0.
tvm.setPubkey(uint256 newPubkey);
Set new contract's public key. Contract's public key can be obtained from tvm.pubkey
.
tvm.setCurrentCode(TvmCell newCode);
Changes this smart contract current code to that given by Cell newCode. Unlike tvm.setcode() this function changes code of the smart contract only for current TVM execution, but has no effect after termination of the current run of the smart contract.
See example of how to use this function:
tvm.resetStorage();
Resets all state variables to their default values.
tvm.exit();
tvm.exit1();
Functions are used to save state variables and quickly terminate execution of the smart contract.
Exit code of the compute phase is equal to zero/one for tvm.exit
/tvm.exit1
.
Example:
function g0(uint a) private {
if (a == 0) {
tvm.exit();
}
//...
}
function g1(uint a) private {
if (a == 0) {
tvm.exit1();
}
//...
}
tvm.sendrawmsg(TvmCell msg, uint8 flag);
Send the internal/external message msg
with flag
. It's a wrapper for opcode
SENDRAWMSG
(TVM - A.11.10).
Internal message msg
can be generated by abi.encodeIntMsg().
Possible values of flag
are described here: <address>.transfer().
Note: make sure that msg
has a correct format and follows the TL-B scheme of Message X
.
For example:
TvmCell msg = ...
tvm.sendrawmsg(msg, 2);
If the function is called by external message and msg
has a wrong format (for example, the field
init
of Message X
is not valid), then the transaction will be replayed despite the usage of flag 2.
It will happen because the transaction will fail at the action phase.
Operations on a pairing friendly BLS12-381 curve. BLS values are represented in TVM in the following way:
- G1-points and public keys: 48-byte slice.
- G2-points and signatures: 96-byte slice.
- Elements of field FP: 48-byte slice.
- Elements of field FP2: 96-byte slice.
- Messages: slice. Number of bits should be divisible by 8.
When input value is a point or a field element, the slice may have more than 48/96 bytes. In this case only the first 48/96 bytes are taken. If the slice has less bytes (or if message size is not divisible by 8), cell underflow exception is thrown.
Capabilities required: CapTvmV20
.
bls.verify(TvmSlice pubkey, TvmSlice message, TvmSlice sign) returns (bool)
Checks BLS signature. Returns true
on success, false
otherwise. Example:
TvmSlice pubkey = "b65cfaf56cebd6083320bf9a1c2010d4775310c5e7b348546dce0f62aa1ad0c29e15a58e251582faa7879d74e9d4034b";
TvmSlice message = TvmSlice(bytes("Hello, BLS verify!"));
TvmSlice sign = "aa652737cad33a9b332300ecd53f1995e5d6c6ff5eb233c04b2e32ca1169524fee64d58575cb42a1a34e1bf3a61c550814d0147b2b82a668ef7c917c756e489e8ff57d64efbbf533d7995db28377d6442ec952268a2bf30d5770d4e8a9d56f9c";
bool ok = bls.verify(pubkey, message, sign);
(1)
bls.aggregate(vector(TvmSlice) signs) returns (TvmSlice sign)
(2)
bls.aggregate(TvmSlice sign0, TvmSlice sign1, ...) returns (TvmSlice sign)
(1) Aggregates signatures if signs.length() > 0
. Throw exception if signs.empty()
or if some signs[i]
is not a valid signature.
(2) Same as (1) but takes TvmSlice
's.
Example:
vector(TvmSlice) signs;
signs.push("8b1eac18b6e7a38f2b2763c9a03c3b6cff4110f18c4d363eec455463bd5c8671fb81204c4732406d72468a1474df6133147a2240f4073a472ef419f23011ee4d6cf02fceb844398e33e2e331635dace3b26464a6851e10f6895923c568582fbd");
signs.push("94ec60eb8d2b657dead5e1232b8f9cc0162467b08f02e252e97622297787a74b6496607036089837fe5b52244bbbb6d00d3d7cc43812688451229d9e96f704401db053956c588203ba7638e8882746c16e701557f34b0c08bbe097483aec161e");
signs.push("8cdbeadb3ee574a4f796f10d656885f143f454cc6a2d42cf8cabcd592d577c5108e4258a7b14f0aafe6c86927b3e70030432a2e5aafa97ee1587bbdd8b69af044734defcf3c391515ab26616e15f5825b4b022a7df7b44f65a8792c54762e579");
TvmSlice sign = bls.aggregate(signs);
TvmSlice sign0 = "8b1eac18b6e7a38f2b2763c9a03c3b6cff4110f18c4d363eec455463bd5c8671fb81204c4732406d72468a1474df6133147a2240f4073a472ef419f23011ee4d6cf02fceb844398e33e2e331635dace3b26464a6851e10f6895923c568582fbd";
TvmSlice sign1 = "94ec60eb8d2b657dead5e1232b8f9cc0162467b08f02e252e97622297787a74b6496607036089837fe5b52244bbbb6d00d3d7cc43812688451229d9e96f704401db053956c588203ba7638e8882746c16e701557f34b0c08bbe097483aec161e";
TvmSlice sign2 = "8cdbeadb3ee574a4f796f10d656885f143f454cc6a2d42cf8cabcd592d577c5108e4258a7b14f0aafe6c86927b3e70030432a2e5aafa97ee1587bbdd8b69af044734defcf3c391515ab26616e15f5825b4b022a7df7b44f65a8792c54762e579";
TvmSlice sign = bls.aggregate(sign0, sign1, sign2);
(1)
bls.fastAggregateVerify(vector(TvmSlice) pubkeys, TvmSlice message, TvmSlice singature) returns (bool ok)
(2)
bls.fastAggregateVerify(TvmSlice pubkey0, TvmSlice pubkey1, ..., TvmSlice message, TvmSlice singature) returns (bool ok)
(1) Checks aggregated BLS signature for pubkeys
and message
. Returns true
on success, false
otherwise. Return false
if pubkeys.empty()
.
(2) Same as (1) but takes TvmSlice
's.
Example:
vector(TvmSlice) pubkeys;
pubkeys.push("a44184a47ad3fc0069cf7a95650a28af2ed715beab28651a7ff433e26c0fff714d21cc5657367bc563c6df28fb446d8f");
pubkeys.push("832c0eca9f8cae87a1c6362838b34723cf63a1f69e366d64f3c61fc237217c4bea601cfbf4d6c18849ed4f9487b4a20c");
pubkeys.push("9595aa3c5cb3d7c763fa6b52294ebde264bdf49748efbbe7737c35532db8fabc666bb0d186f329c8bdafddfbdcbc3ca6");
TvmSlice message = TvmSlice(bytes("Hello, BLS fast aggregate and verify!"));
TvmSlice singature = "8420b1944c64f74dd67dc9f5ab210bab928e2edd4ce7e40c6ec3f5422c99322a5a8f3a8527eb31366c9a74752d1dce340d5a98fbc7a04738c956e74e7ba77b278cbc52afc63460c127998aae5aa1c3c49e8c48c30cc92451a0a275a47f219602";
bool ok = bls.fastAggregateVerify(pubkeys, message, singature);
TvmSlice pk0 = "a44184a47ad3fc0069cf7a95650a28af2ed715beab28651a7ff433e26c0fff714d21cc5657367bc563c6df28fb446d8f";
TvmSlice pk1 = "832c0eca9f8cae87a1c6362838b34723cf63a1f69e366d64f3c61fc237217c4bea601cfbf4d6c18849ed4f9487b4a20c";
TvmSlice pk2 = "9595aa3c5cb3d7c763fa6b52294ebde264bdf49748efbbe7737c35532db8fabc666bb0d186f329c8bdafddfbdcbc3ca6";
TvmSlice message = TvmSlice(bytes("Hello, BLS fast aggregate and verify!"));
TvmSlice singature = "8420b1944c64f74dd67dc9f5ab210bab928e2edd4ce7e40c6ec3f5422c99322a5a8f3a8527eb31366c9a74752d1dce340d5a98fbc7a04738c956e74e7ba77b278cbc52afc63460c127998aae5aa1c3c49e8c48c30cc92451a0a275a47f219602";
bool ok = bls.fastAggregateVerify(pk0, pk1, pk2, message, singature);
(1)
bls.aggregateVerify(vector(TvmSlice, TvmSlice) pubkeysMessages, TvmSlice singature) returns (bool ok)
(2)
bls.aggregateVerify(TvmSlice pubkey0, TvmSlice pubkey1, ..., TvmSlice message0, TvmSlice message1, ..., TvmSlice singature) returns (bool ok)
(1) Checks aggregated BLS signature for key-message pairs pubkeysMessages
. Returns true
on success, false
otherwise. Returns false
if pubkeysMessages.empty()
.
(2) Same as (1) but takes TvmSlice
's.
vector(TvmSlice, TvmSlice) pubkeysMessages;
TvmSlice pubkey0 = "b75f0360095de73c4790f803153ded0f3e6aefa6f0aac8bfd344a44a3de361e3f6f111c0cf0ad0c4a0861492f9f1aeb1";
TvmSlice message0 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 0!"));
pubkeysMessages.push(pubkey0, message0);
TvmSlice pubkey1 = "a31e12bb4ffa75aabbae8ec2367015ba3fc749ac3826539e7d0665c285397d02b48414a23f8b33ecccc750b3afffacf6";
TvmSlice message1 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 1!"));
pubkeysMessages.push(pubkey1, message1);
TvmSlice pubkey2 = "8de5f18ca5938efa896fbc4894c6044cdf89e778bf88584be48d6a6235c504cd45a44a68620f763aea043b6381add1f7";
TvmSlice message2 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 2!"));
pubkeysMessages.push(pubkey2, message2);
TvmSlice singature = "8b8238896dfe3b02dc463c6e645e36fb78add51dc8ce32f40ecf60a418e92762856c3427b672be67278b5c4946b8c5a30fee60e5c38fdb644036a4f29ac9a039ed4e3b64cb7fef303052f33ac4391f95d482a27c8341246516a13cb72e58097b";
bool ok = bls.aggregateVerify(pubkeysMessages, singature);
TvmSlice pubkey0 = "b75f0360095de73c4790f803153ded0f3e6aefa6f0aac8bfd344a44a3de361e3f6f111c0cf0ad0c4a0861492f9f1aeb1";
TvmSlice message0 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 0!"));
TvmSlice pubkey1 = "a31e12bb4ffa75aabbae8ec2367015ba3fc749ac3826539e7d0665c285397d02b48414a23f8b33ecccc750b3afffacf6";
TvmSlice message1 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 1!"));
TvmSlice pubkey2 = "8de5f18ca5938efa896fbc4894c6044cdf89e778bf88584be48d6a6235c504cd45a44a68620f763aea043b6381add1f7";
TvmSlice message2 = TvmSlice(bytes("Hello, BLS fast aggregate and verify 2!"));
TvmSlice singature = "8b8238896dfe3b02dc463c6e645e36fb78add51dc8ce32f40ecf60a418e92762856c3427b672be67278b5c4946b8c5a30fee60e5c38fdb644036a4f29ac9a039ed4e3b64cb7fef303052f33ac4391f95d482a27c8341246516a13cb72e58097b";
bool ok = bls.aggregateVerify(pubkey0, message0, pubkey1, message1, pubkey2, message2, singature);
bls.g1Zero() returns (TvmSlice)
bls.g2Zero() returns (TvmSlice)
Returns zero point in G1/G2.
bls.g1Zero(TvmSlice x) returns (bool isZero)
bls.g2Zero(TvmSlice x) returns (bool isZero)
Checks that G1/G2 point x
is equal to zero.
bls.g1Add(TvmSlice a, TvmSlice b) returns (TvmSlice res)
bls.g2Add(TvmSlice a, TvmSlice b) returns (TvmSlice res)
Addition on G1/G2.
bls.g1Sub(TvmSlice a, TvmSlice b) returns (TvmSlice res)
bls.g2Sub(TvmSlice a, TvmSlice b) returns (TvmSlice res)
Subtraction on G1/G2.
bls.g1Neg(TvmSlice x) returns (TvmSlice res)
bls.g2Neg(TvmSlice x) returns (TvmSlice res)
Negation on G1/G2.
bls.g1Mul(TvmSlice x, int s) returns (TvmSlice res)
bls.g2Mul(TvmSlice x, int s) returns (TvmSlice res)
Multiplies G1/G2 point x
by scalar s
. Any s
is valid, including negative.
bls.g1Mul(TvmSlice x) returns (bool ok)
bls.g2Mul(TvmSlice x) returns (bool ok)
Checks that slice x
represents a valid element of G1/G2.
bls.r() returns (uint255)
Pushes the order of G1 and G2 (approx. 2^255). It's 52435875175126190479447740508185965837690552500527637822603658699938581184513.
(1)
bls.g1MultiExp(vector(TvmSlice, int) x_s) returns (TvmSlice)
bls.g2MultiExp(vector(TvmSlice, int) x_s) returns (TvmSlice)
(2)
bls.g1MultiExp(TvmSlice x0, int s0, TvmSlice x1, int s1, ...) returns (TvmSlice)
bls.g2MultiExp(TvmSlice x0, int s0, TvmSlice x1, int s1, ...) returns (TvmSlice)
(1) Calculates x_1*s_1+...+x_n*s_n
for G1/G2 points x_i
and scalars s_i
. Returns zero point if n==0
. Any s_i
is valid, including negative.
(2) Same as (1) but takes TvmSlice
's and int
's.
TvmSlice a = bls.mapToG1("7abd13983c76661a98659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92");
TvmSlice b = bls.mapToG1("7abd13983c76661118659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92");
TvmSlice c = bls.mapToG1("7abd13983c76661118659da83066c71bd658100020c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92");
vector(TvmSlice, int) values;
values.push(a, 2);
values.push(b, 5);
values.push(c, 13537812947843);
TvmSlice res = bls.g1MultiExp(values);
TvmSlice aa = bls.g1Mul(a, 2);
TvmSlice bb = bls.g1Mul(b, 5);
TvmSlice cc = bls.g1Mul(c, 13537812947843);
TvmSlice res2 = bls.g1Add(bls.g1Add(aa, bb), cc);
require(res == res2);
TvmSlice a = bls.mapToG1("7abd13983c76661a98659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92");
TvmSlice b = bls.mapToG1("7abd13983c76661118659da83066c71bd6581baf20c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92");
TvmSlice c = bls.mapToG1("7abd13983c76661118659da83066c71bd658100020c82c825b007bf8057a258dc53f7a6d44fb6fdecb63d9586e845d92");
TvmSlice res = bls.g1MultiExp(a, 2, b, 5, c, 13537812947843);
TvmSlice aa = bls.g1Mul(a, 2);
TvmSlice bb = bls.g1Mul(b, 5);
TvmSlice cc = bls.g1Mul(c, 13537812947843);
TvmSlice res2 = bls.g1Add(bls.g1Add(aa, bb), cc);
require(res == res2);
T
is an integer, variable integer, qintN and quintN or fixed point type in the math.*
functions where applicable.
Fixed point type is not applicable for math.modpow2()
, math.muldiv[r|c]()
, math.muldivmod()
and math.divmod()
.
math.min(T a, T b, ...) returns (T);
math.max(T a, T b, ...) returns (T);
Returns the minimal (maximal) value of the passed arguments.
math.minmax(T a, T b) returns (T /*min*/, T /*max*/);
Returns minimal and maximal values of the passed arguments.
Example:
(uint a, uint b) = math.minmax(20, 10); // (a, b) == (10, 20)
math.abs(T1 val) returns (T2);
Computes the absolute value of the given integer. Throws an exception if absolute value of val
does not fit into T2.
Example:
int256 a = -100;
uint255 b = math.abs(a); // b == 100
int256 a = type(int256).min;
uint255 b = math.abs(a); // throws an exception
math.modpow2(T value, uint power) returns (T);
Computes the value mod 2^power
. Note: power
should be a constant integer.
Example:
uint a = math.modpow2(21, 4); // a == 5 because 21 % (2**4) == 21 % 16 == 5
uint constant pow = 10;
uint val = 1026;
uint b = math.modpow2(val, pow); // b == 2 because 1026 % (2**10) == 1026 % 1024 == 2
math.divc(T a, T b) returns (T);
math.divr(T a, T b) returns (T);
Returns result of the division of two integers. The return value is rounded. ceiling and nearest modes are used for divc
and divr
respectively. See also: Division and rounding.
Example:
int c = math.divc(10, 3); // c == 4
int c = math.divr(10, 3); // c == 3
fixed32x2 a = 0.25;
fixed32x2 res = math.divc(a, 2); // res == 0.13
math.divmod(T a, T b) returns (T /*result*/, T /*remainder*/);
This function divides the first number by the second and returns the result and the remainder. Result is rounded to the floor.
Example:
uint a = 11;
uint b = 3;
(uint d, uint r) = math.divmod(a, b); // (d, r) == (3, 2)
int e = -11;
int f = 3;
(int h, int p) = math.divmod(e, f); // (h, p) == (-3, 2)
math.muldiv(T a, T b, T c) returns (T);
math.muldivr(T a, T b, T c) returns (T);
math.muldivc(T a, T b, T c) returns (T);
Multiplies two values and then divides the result by a third value. The return value is rounded. floor, ceiling and nearest modes are used for muldiv
,
muldivc
and muldivr
respectively. See also: Division and rounding.
Example:
uint res = math.muldiv(3, 7, 2); // res == 10
uint res = math.muldivr(3, 7, 2); // res == 11
uint res = math.muldivc(3, 7, 2); // res == 11
math.muldivmod(T a, T b, T c) returns (T /*quotient*/, T /*remainder*/);
This instruction multiplies first two arguments, divides the result by third argument and returns the result and the remainder. Intermediate result is stored in the 514 bit buffer, and the final result is rounded to the floor.
Example:
uint a = 3;
uint b = 2;
uint c = 5;
(uint d, uint r) = math.muldivmod(a, b, c); // (d, r) == (1, 1)
int e = -1;
int f = 3;
int g = 2;
(int h, int p) = math.muldivmod(e, f, g); // (h, p) == (-2, 1)
math.mulmod(T a, T b, T c) returns (T /*remainder*/);
Same as math.muldivmod() but returns only remainder. Example:
uint constant P = 2**255 - 19;
function f() public pure {
uint a = rnd.next(P);
uint b = rnd.next(P);
uint c = math.mulmod(a, b, P);
//...
}
math.sign(T val) returns (int2);
Returns:
- -1 if
val
is negative; - 0 if
val
is zero; - 1 if
val
is positive.
Example:
int8 sign = math.sign(-100); // sign == -1
int8 sign = math.sign(100); // sign == 1
int8 sign = math.sign(0); // sign == 0
tx.logicaltime (uint64);
Returns the logical time of the current transaction.
tx.storageFee (varuint16);
Returns the storage fee paid in the current transaction. Capabilities required: CapStorageFeeToTvm
.
block.timestamp (uint32);
Returns the current Unix time. Unix time is the same for the all transactions from one block.
block.logicaltime (uint64);
Returns the starting logical time of the current block.
The pseudorandom number generator uses the random seed. The
initial value of the random seed before a smart contract execution in
TVM compatible blockchains is a hash of the smart contract address and the global
block random seed. If there are several runs of the same smart contract
inside a block, then all of these runs will have the same random seed.
This can be fixed, for example, by running rnd.shuffle()
(without
parameters) each time before using the pseudorandom number generator.
(1)
rnd.next() returns (uint);
(2)
rnd.next(T limit) returns (T);
Generates a new pseudo-random number.
(1) Returns uint256
number.
(2) If the first argument limit > 0
, then function returns the value in the
range 0..limit-1
. Else if limit < 0
, then the returned value lies in range
limit..-1
. Else if limit == 0
, then it returns 0
.
Example:
// (1)
uint256 r0 = rnd.next(); // 0..2^256-1
// (2)
uint8 r1 = rnd.next(100); // 0..99
int8 r2 = rnd.next(int8(100)); // 0..99
int8 r3 = rnd.next(int8(-100)); // -100..-1
rnd.getSeed() returns (uint256);
Returns the current random seed.
rnd.setSeed(uint256 x);
Sets the random seed to x
.
(1)
rnd.shuffle(uint someNumber);
(2)
rnd.shuffle();
Randomizes the random seed.
(1) Mixes the random seed and someNumber
. The result is set as the random seed.
(2) Mixes the random seed and the logical time of the current transaction. The result is set as the random seed.
Example:
(1)
uint256 someNumber = ...;
rnd.shuffle(someNumber);
(2)
rnd.shuffle();
(1)
abi.encode(TypeA a, TypeB b, ...) returns (TvmCell /*cell*/);
(2)
abi.decode(TvmCell cell, (TypeA, TypeB, ...)) returns (TypeA /*a*/, TypeB /*b*/, ...);
abi.encode
creates cell
from the values.
abi.decode
decodes the cell
and returns the values. Note: all types must be set in abi.decode
.
Otherwise, abi.decode
throws an exception.
Example:
// pack the values to the cell
TvmCell cell = abi.encode(uint(1), uint(2), uint(3), uint(4));
// unpack the cell
(uint a, uint b, uint c, uint d) = abi.decode(cell, (uint, uint, uint, uint));
// a == 1
// b == 2
// c == 3
// d == 4
abi.encodeData({uint256 pubkey, contract Contract, varInit: {VarName0: varValue0, ...}});
Generates data
field of the StateInit
(TBLKCH - 3.1.7.). Parameters are the same as in
abi.encodeStateInit().
// SimpleWallet.sol
contract SimpleWallet {
uint static m_id;
address static m_creator;
// ...
}
// Usage
TvmCell data = abi.encodeData({
contr: SimpleWallet,
varInit: {m_id: 1, m_creator: address(this)},
pubkey: 0x3f82435f2bd40915c28f56d3c2f07af4108931ae8bf1ca9403dcf77d96250827
});
TvmCell code = ...;
TvmCell stateInit = abi.encodeStateInit({code: code, data: data});
// Note, the code above can be simplified to:
TvmCell stateInit = abi.encodeStateInit({
code: code,
contr: SimpleWallet,
varInit: {m_id: 1, m_creator: address(this)},
pubkey: 0x3f82435f2bd40915c28f56d3c2f07af4108931ae8bf1ca9403dcf77d96250827
});
abi.encodeOldDataInit() returns (TvmCell);
Same as abi.encodeData() but generate data in the format that was used in the compiler < 0.72.0. This function can be used to deploy old contracts (that was compiled < 0.72.0) from new ones. Example:
File with old contracts that was compiled by compiler < 0.72.0:
pragma tvm-solidity >= 0.72.0; // set new version
contract SimpleContractA {
uint static m_x0;
uint static m_x1;
}
// Remove all code from old contracts but state variables and contructor declaration
contract SimpleContractB is SimpleContractA {
uint static m_x2;
constructor(string name) {
}
}
File with new contracts:
pragma tvm-solidity >= 0.72.0;
contract ContractCreator {
function deploy(uint pubkey, TvmCell code) public pure returns (address) {
TvmCell data = abi.encodeOldDataInit({
pubkey: pubkey,
varInit: {
m_x0: 100,
m_x1: 200,
m_x2: 300
},
contr: SimpleContractB
});
TvmCell stateInit = abi.encodeStateInit({
code: code,
data: data
});
SimpleContractB addr = new SimpleContractB{
wid: 0,
value: 1 ever,
stateInit: stateInit,
flag: 1
}("Hello, world!");
return addr;
}
}
abi.decodeData(ContractName, TvmSlice) returns (uint256 /*pubkey*/, uint64 /*timestamp*/, bool /*constructorFlag*/, Type1 /*var1*/, Type2 /*var2*/, ...);
Loads state variables from TvmSlice
that is obtained from the field data
of stateInit
.
Example:
contract A {
uint a = 111;
uint b = 22;
uint c = 3;
uint d = 44;
address e = address(12);
address f;
}
contract B {
function f(TvmCell data) public pure {
TvmSlice s = data.toSlice();
(uint256 pubkey, uint64 timestamp, bool flag,
uint a, uint b, uint c, uint d, address e, address f) = abi.decodeData(A, s);
// pubkey - pubkey of the contract A
// timestamp - timestamp that used for replay protection
// flag - always is equal to true
// a == 111
// b == 22
// c == 3
// d == 44
// e == address(12)
// f == address.addrNone
// s.empty()
}
}
// 1)
abi.encodeStateInit(TvmCell code, TvmCell data) returns (TvmCell stateInit);
// 2)
abi.encodeStateInit(TvmCell code, TvmCell data, uint8 splitDepth) returns (TvmCell stateInit);
// 3)
abi.encodeStateInit({TvmCell code, TvmCell data, uint8 splitDepth,
uint256 pubkey, Contract contr, varInit: {VarName0: varValue0, ...}});
Generates a StateInit
(TBLKCH - 3.1.7.) from code
and data
TvmCell
s.
Member splitDepth
of the tree of cell StateInit
:
- is not set. Has no value.
- is set.
0 <= splitDepth <= 31
- Arguments can also be set with names.
List of possible names:
code
(TvmCell
) - defines the code field of theStateInit
. Must be specified.data
(TvmCell
) - defines the data field of theStateInit
. Conflicts withpubkey
andvarInit
. Can be omitted, in this case data field would be built frompubkey
andvarInit
.splitDepth
(uint8
) - splitting depth.0 <= splitDepth <= 31
. Can be omitted. By default, it has no value.pubkey
(uint256
) - defines the public key of the new contract. Conflicts withdata
. Can be omitted, default value is 0.varInit
(initializer list
) - used to set static variables of the contract. Conflicts withdata
and requirescontr
to be set. Can be omitted.contr
(contract
) - defines the contract whoseStateInit
is being built. Mandatory to be set if the optionvarInit
is specified.
Examples of this function usage:
contract A {
uint static var0;
address static var1;
}
contract C {
function f() public pure {
TvmCell code;
TvmCell data;
uint8 depth;
TvmCell stateInit = abi.encodeStateInit(code, data);
stateInit = abi.encodeStateInit(code, data, depth);
}
function f1() public pure {
TvmCell code;
TvmCell data;
uint8 depth;
uint pubkey;
uint var0;
address var1;
TvmCell stateInit1 = abi.encodeStateInit({code: code, data: data, splitDepth: depth});
stateInit1 = abi.encodeStateInit({code: code, splitDepth: depth, varInit: {var0: var0, var1: var1}, pubkey: pubkey, contr: A});
stateInit1 = abi.encodeStateInit({varInit: {var0: var0, var1: var1}, pubkey: pubkey, contr: A, code: code, splitDepth: depth});
stateInit1 = abi.encodeStateInit({contr: A, varInit: {var0: var0, var1: var1}, pubkey: pubkey, code: code, splitDepth: depth});
stateInit1 = abi.encodeStateInit({contr: A, varInit: {var0: var0, var1: var1}, pubkey: pubkey, code: code});
stateInit1 = abi.encodeStateInit({contr: A, varInit: {var0: var0, var1: var1}, code: code, splitDepth: depth});
}
}
abi.stateInitHash(uint256 codeHash, uint256 dataHash, uint16 codeDepth, uint16 dataDepth) returns (uint256);
Calculates hash of the stateInit for given code and data specifications.
Example:
TvmCell code = ...;
TvmCell data = ...;
uint codeHash = tvm.hash(code);
uint dataHash = tvm.hash(data);
uint16 codeDepth = code.depth();
uint16 dataDepth = data.depth();
uint256 hash = abi.stateInitHash(codeHash, dataHash, codeDepth, dataDepth);
See also internal doc to read more about this function mechanics.
abi.encodeBody(function, arg0, arg1, arg2, ...) returns (TvmCell);
abi.encodeBody(function, callbackFunction, arg0, arg1, arg2, ...) returns (TvmCell);
abi.encodeBody(contract, arg0, arg1, arg2, ...) returns (TvmCell);
Constructs a message body for the function call. Body can be used as a payload for <address>.transfer().
If the function is responsible
, callbackFunction must be set.
Example:
contract Remote {
constructor(uint x, uint y, uint z) public { /* */ }
function func(uint256 num, int64 num2) public pure { /* */ }
function getCost(uint256 num) public responsible pure returns (uint128) { /* */ }
}
// deploy the contract
TvmCell body = abi.encodeBody(Remote, 100, 200, 300);
addr.transfer({value: 10 ever, body: body, stateInit: stateInit });
// call the function
TvmCell body = abi.encodeBody(Remote.func, 123, -654);
addr.transfer({value: 10 ever, body: body});
// call the responsible function
TvmCell body = abi.encodeBody(Remote.getCost, onGetCost, 105);
addr.transfer({value: 10 ever, body: body});
See also:
// Loads parameters of the public/external function without "responsible" attribute
abi.decodeFunctionParams(functionName) returns (TypeA /*a*/, TypeB /*b*/, ...);
// Loads parameters of the public/external function with "responsible" attribute
abi.decodeFunctionParams(functionName) returns (uint32 callbackFunctionId, TypeA /*a*/, TypeB /*b*/, ...);
// Loads constructor parameters
abi.decodeFunctionParams(ContractName) returns (TypeA /*a*/, TypeB /*b*/, ...);
Loads parameters of the function or constructor (if contract type is provided). This function is usually used in onBounce function.
See example of how to use onBounce function:
abi.codeSalt(TvmCell code) returns (optional(TvmCell) optSalt);
If code contains salt, then optSalt contains one. Otherwise, optSalt doesn't contain any value.
abi.setCodeSalt(TvmCell code, TvmCell salt) returns (TvmCell newCode);
Inserts salt into code and returns new code newCode.
// id of public function
abi.functionId(functionName) returns (uint32);
// id of public constructor
abi.functionId(ContractName) returns (uint32);
Returns the function id (uint32) of a public/external function or constructor.
Example:
contract MyContract {
constructor(uint a) public {
}
/*...*/
}
function f() public pure returns (uint) {
/*...*/
}
function getConstructorID() public pure returns (uint32) {
uint32 functionId = abi.functionId(MyContract);
return functionId;
}
function getFuncID() public pure returns (uint32) {
uint32 functionId = abi.functionId(f);
return functionId;
}
}
See example of how to use this function:
abi.encodeIntMsg({
dest: address,
value: uint128,
call: {function, [callbackFunction,] arg0, arg1, arg2, ...},
bounce: bool,
currencies: mapping(uint32 => varuint32),
stateInit: TvmCell
})
returns (TvmCell);
Generates an internal outbound message that contains a function call. The result TvmCell
can be used to send a
message using tvm.sendrawmsg(). If the function
is responsible
, then
callbackFunction
parameter must be set.
dest
, value
and call
parameters are mandatory. Another parameters can be omitted. See
<address>.transfer() where these options and their default values are
described.
See also:
- sample 22_sender.sol
- abi.encodeBody()
All gosh.*
functions are experimental features and are available only in certain blockchain
networks in which CapDiff
capability is set.
(1)
gosh.diff(string oldText, string newText) returns (string patch)
(2)
gosh.zipDiff(bytes oldText, bytes newText) returns (bytes patch)
(1) Calculates patch between oldText
and newText
.
(2) It's the same as gosh.diff
but it calculates patch
between compressed strings.
string oldText = ...;
string newText = ...;
string patch = gosh.diff(oldText, newText);
gosh.applyPatch, gosh.applyPatchQ, gosh.applyZipPatch, gosh.applyZipPatchQ, gosh.applyZipBinPatch and gosh.applyZipBinPatchQ
(1)
gosh.applyPatch(string oldText, string patch) returns (string newText)
gosh.applyPatchQ(string oldText, string patch) returns (optional(string) newText)
(2)
gosh.applyBinPatch(bytes oldText, bytes patch) returns (bytes newText)
gosh.applyBinPatchQ(bytes oldText, bytes patch) returns (optional(bytes) newText)
(3)
gosh.applyZipPatch(bytes oldText, bytes patch) returns (bytes newText)
gosh.applyZipPatchQ(bytes oldText, bytes patch) returns (optional(bytes) newText)
(4)
gosh.applyZipBinPatch(bytes oldText, bytes patch) returns (bytes newText)
gosh.applyZipBinPatchQ(bytes oldText, bytes patch) returns (optional(bytes) newText)
(1) Applies patch
to the oldText
. If it's impossible (bad patch), gosh.applyPatch
throws an exception with type check
error code (-8) butgosh.applyPatchQ
returns null
.
(2) These are the same as gosh.applyPatch
/gosh.applyPatchQ
but these functions are applied to binary arrays.
(3) These are the same as gosh.applyPatch
/gosh.applyPatchQ
but these functions are applied to compressed strings.
(4) These are the same as gosh.applyPatch
/gosh.applyPatchQ
but these functions are applied to compressed binary arrays.
string oldText = ...;
string patch = ...;
string newText = gosh.applyPatch(oldText, patch);
gosh.zip(string text) returns (bytes zip)
gosh.unzip(bytes zip) returns (optional(string) text)
gosh.zip
converts the text
to compressed bytes
. gosh.unzip
reverts such compression.
Exponentiation **
is only available for unsigned types in the exponent. The resulting type of an
exponentiation is always equal to the type of the base. Please take care that it is large enough to
hold the result and prepare for potential assertion failures or wrapping behaviour.
Note that 0**0
throws an exception.
Example:
uint b = 3;
uint32 p = 4;
uint res = b ** p; // res == 81
selfdestruct(address dest_addr);
Creates and sends the message that carries all the remaining balance of the current smart contract and destroys the current account.
See example of how to use the selfdestruct
function:
(1)
sha256(TvmSlice slice) returns (uint256)
(2)
sha256(bytes b) returns (uint256)
(3)
sha256(string str) returns (uint256)
- Computes the SHA-256 hash. If the bit-length of
slice
is not divisible by eight, throws a cell underflow exception. References ofslice
are not used to compute the hash. Only data bits located in the root cell ofslice
are used. - Computes the SHA-256 hash only for the first 127 bytes. If
bytes.length > 127
, thenb[128], b[129], b[130] ...
elements are ignored. - Same as for
bytes
: only the first 127 bytes are taken into account.
See also tvm.hash() to compute representation hash of the whole tree of cells.
gasToValue(uint128 gas) returns (uint128 value)
gasToValue(uint128 gas, int8 wid) returns (uint128 value)
Returns worth of gas in workchain wid.
Throws an exception if wid is not equal to 0
or -1
.
If wid
is omitted than used the contract's wid
.
valueToGas(uint128 value) returns (uint128 gas)
valueToGas(uint128 value, int8 wid) returns (uint128 gas)
Counts how much gas could be bought on value nanoevers in workchain wid.
Throws an exception if wid is not equal to 0
or -1
.
If wid
is omitted than used the contract's wid
.
gasleft() returns (uint64)
Capabilities required: CapsTvmBugfixes2022
.
Returns the remaining gas.
Rust implementation of TVM has capabilities. Capabilities are flags that can be set to turn on
some features or behavior of TVM. Full list of capabilities can be found in enum GlobalCapabilities
in ever-block repo.
Set capabilities store in 8th parameter of the global config of the blockchain. To get it you can use command:
ever-cli --json getconfig 8
Name | Code | Definition |
---|---|---|
Stack underflow | 2 | Not enough arguments in the stack for a primitive |
Stack overflow | 3 | More values have been stored on a stack than allowed by this version of TVM |
Integer overflow | 4 | Integer does not fit into expected range (by default −2256 ≤ x < 2256), or a division by zero has occurred |
Range check error | 5 | Integer out of expected range |
Invalid opcode | 6 | Instruction or its immediate arguments cannot be decoded |
Type check error | 7 | An argument to a primitive is of incorrect value type |
Cell overflow | 8 | Error in one of the serialization primitives |
Cell underflow | 9 | Deserialization error |
Dictionary error | 10 | Error while deserializing a dictionary object |
Unknown error | 11 | Unknown error, may be thrown by user programs |
Fatal error | 12 | Thrown by TVM in situations deemed impossible |
Out of gas | -14 | Thrown by TVM when the remaining gas (gr) becomes negative. This exception cannot be caught and leads to an immediate termination of TVM |
See also: TVM - 4.5.7
Smart-contract written in TVM Solidity can throw runtime errors while execution.
Solidity runtime error codes:
- 40 - External inbound message has an invalid signature. See tvm.pubkey() and msg.pubkey().
- 50 - Array index or index of <mapping>.at() is out of range.
- 51 - Contract's constructor has already been called.
- 52 - Replay protection exception. See
timestamp
in pragma AbiHeader. - 54 -
<array>.pop
call for an empty array. - 57 - External inbound message is expired. See
expire
in pragma AbiHeader. - 58 - External inbound message has no signature but has public key. See
pubkey
in pragma AbiHeader. - 60 - Inbound message has wrong function id. In the contract there are no functions with such function id and there is no fallback function that could handle the message. See fallback.
- 61 - Deploying
StateInit
has no public key indata
field. - 62 - Reserved for internal usage.
- 63 - See <optional(T)>.get().
- 67 - See gasToValue and valueToGas.
- 68 - There is no config parameter 20 or 21.
- 69 - Zero to the power of zero calculation (
0**0
in TVM Solidity style or0^0
). - 70 -
string
methodsubstr
was called with substr longer than the whole string. - 71 - Function marked by
externalMsg
was called by internal message. - 72 - Function marked by
internalMsg
was called by external message. - 76 - Public function was called before constructor.
- 77 - It's impossible to convert
variant
type to target type. See variant.toUint(). - 78 - There's no private function with the function id.
- 79 - You are deploying contract that uses pragma upgrade func/oldsol. Use the
- 80 - See <T>.get().
Let consider we have x
and y
and we want to divide x
by y
. Compute the quotient q
and the
remainder r
of the division of x
by y
: x = y*q + r
where |r| < |y|
.
In TVM there are 3 options of rounding:
- floor - quotient
q
is rounded to −∞.q = ⌊x/y⌋
,r
has the same sign asy
. This rounding option is used for operator/
. Example:
int res = int(-2) / 3; // res == -1
int res = int(-5) / 10; // res == -1
int res = int(5) / 10; // res == 0
int res = int(15) / 10; // res == 1
- ceiling - quotient
q
is rounded to +∞.q = ⌈x/y⌉
,r
andy
have opposite signs. Example:
int res = math.divc(-2, 3); // res == 0
int res = math.divc(-5, 10); // res == 0
int res = math.divc(5, 10); // res == 1
int res = math.divc(15, 10); // res == 2
- nearest - quotient
q
is rounded to the nearest number.q = ⌊x/y + 1/2⌋
and|r| ≤ |y|/2
. Example:
int res = math.divr(-2, 3); // res == -1
int res = math.divr(-5, 10); // res == 0
int res = math.divr(5, 10); // res == 1
int res = math.divr(15, 10); // res == 2
Before executing any contract function special code is executed. In *.code
file there are two special
functions: main_internal
and main_external
that run on internal and external messages
respectively. These functions initialize some internal global variables and call contract
function of special function like receive
, fallback
, onBounce
, onTickTock
, etc.
Before calling contract's function main_external
does:
- Checks the message signature. Let's consider how the signature is checked:
- If signature exists and
pubkey
header isn't defined, thentvm.pubkey()
is used for checking. - If signature isn't exists and
pubkey
header isn't defined, then signature isn't checked. - If signature exists,
pubkey
header is defined andpubkey
isn't exists in the message, thentvm.pubkey()
is used for checking. - If signature exists,
pubkey
header is defined andpubkey
exists in the message, thenmsg.pubkey()
is used for checking. - If signature isn't exists,
pubkey
header is defined andpubkey
exists in the message, then an exception with code 58 is thrown.
- If signature exists and
- Replay protection:
- time header exists (
pragma AbiHeader notime
is not used), then the contract checks whetheroldTime
<time
<now * 1000 + 30 minutes
. If it's true, thenoldTime
is updated by newtime
. Otherwise, an exception is thrown. - there is
afterSignatureCheck
(despite usage oftime
), then make your own replay protection.
- time header exists (
- Message expiration:
expire
exists and there is noafterSignatureCheck
, then the contract checks whetherexpire
>now
.- there is
afterSignatureCheck
(despite usage ofexpire
), then make your own check.
See also: pragma AbiHeader, afterSignatureCheck.
Try to reduce count of []
operations for mappings and arrays. For example:
Struct Point {
uint x;
uint y;
uint z;
}
Point[] points;
Here we have 3 []
operations:
points[0].x = 5;
points[0].y = 10;
points[0].z = -5;
We can use a temp variable:
Point p = points[0];
p.x = 5;
p.y = 10;
p.z = -5;
points[0] = p;