tact-lang / tact Goto Github PK
View Code? Open in Web Editor NEWTact compiler main repository
Home Page: https://tact-lang.org
License: MIT License
Tact compiler main repository
Home Page: https://tact-lang.org
License: MIT License
receive("increment") {
self.counter = self.counter + 1;
}
become
set fun increment {
self.counter = self.counter + 1;
}
much like
get fun counter(): Int {
return self.counter;
}
We need to make our contracts upgradable. Storage loading has to be flexible.
Make StringBuilder.append
return itself, so we can write:
sb.append(self.s1).append(", your balance is: ").append(self.s5);
Users found this code confusing:
message Add {
amount: Int as uint32;
}
message Subtract {
amount: Int as uint32;
}
contract Counter {
receive(msg: Add) {
// ...
}
receive(msg: Subtract) {
// ...
}
}
Questions:
They proposed a more familiar syntax that would not bring questions and will look more like other languages:
contract Counter {
recv fun Add(amount: Int as uint32) {
// ...
}
recv fun Subtract(amount: Int as uint32) {
// ...
}
}
Following code does not compile:
import "@stdlib/deploy";
struct TokenInfo {
ticker: String;
decimals: Int as uint8; // <------ ISSUE HERE, IF WE REMOVE "as uint8", THE CODE COMPILES WITHOUT PROBLEM
}
// messages can contain maps
message Replace {
items: map<Int, Address>;
}
contract Maps with Deployable {
// maps with Int as key
mi1: map<Int, TokenInfo>;
mi2: map<Int, Bool>;
mi3: map<Int, Int>;
mi4: map<Int, Address>;
// maps with Address as key
ma1: map<Address, TokenInfo>;
ma2: map<Address, Bool>;
ma3: map<Address, Int>;
ma4: map<Address, Address>;
init(arg: map<Int, Bool>) {
// no need to initialize maps if they're empty
self.mi2 = arg;
}
receive("set keys") {
// keys are Int
self.mi1.set(17, TokenInfo{ticker: "SHIB", decimals: 9});
self.mi2.set(0x9377433ff21832, true);
self.mi3.set(pow(2,240), pow(2,230));
self.mi4.set(-900, address("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"));
// keys are Address
self.ma1.set(address("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"), TokenInfo{ticker: "DOGE", decimals: 18});
self.ma2.set(address("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI"), true);
self.ma3.set(address("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"), ton("1.23"));
self.ma4.set(address("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI"), myAddress());
}
receive("delete keys") {
// keys are Int
self.mi1.set(17, null);
self.mi2.set(0x9377433ff21832, null);
self.mi3.set(pow(2,240), null);
self.mi4.set(-900, null);
// keys are Address
self.ma1.set(address("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"), null);
self.ma2.set(address("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI"), null);
self.ma3.set(address("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"), null);
self.ma4.set(address("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI"), null);
}
receive("clear") {
self.mi1 = emptyMap();
self.mi2 = emptyMap();
self.mi3 = emptyMap();
self.mi4 = emptyMap();
self.ma1 = emptyMap();
self.ma2 = emptyMap();
self.ma3 = emptyMap();
self.ma4 = emptyMap();
}
receive(msg: Replace) {
// replace all items in the map with those coming in the message
self.mi4 = msg.items;
}
// if the key is not found, the get() method returns null
get fun oneItem(key: Int): Address? {
return self.mi4.get(key);
}
get fun itemCheck(): String {
if (self.mi1.get(17) == null) {
return "not found";
}
let item: TokenInfo = self.mi1.get(17)!!; // the !! will tell the compiler it's not null
return item.ticker;
}
// you can return maps from getters
get fun allItems(): map<Address, TokenInfo> {
return self.ma1;
}
}
Error:
> Maps: func compiler
[ 1][t 0][2023-04-03 21:38:53.793000][Fift.cpp:67] top: u,
level 1: <text interpreter continuation>
main.fif:560: PUSHSLICE:integer does not fit into cell
๐ฅ Compilation failed. Skipping packaging
Using "@tact-lang/compiler": "1.1.0-beta.8"
I am encountering issues with relative imports, which don't seem to function intuitively, especially when I try to import files from a different directory. When I import files from the same directory using the syntax import "./other_file.tact";
, everything operates smoothly. However, problems emerge when I attempt to import files from a parent or sibling directory with the syntax import "../other_file.tact";
.
I have an extensive folder structure with many contracts, and my goal is to share some utility files among them while adhering to a standard folder structure. My folder structure is organized as follows:
contracts
|--utils
|--some_file.tact
|--dex
|--dex_file.tact
I aim to maintain this structure while facilitating the sharing of utility files across different contracts. However, the relative import issues are preventing me from achieving this. Question is: Can we achieve such imports in tact?
At this moment, I can't find a place to trig Transfer
I have to implement it myself in my Tact code.
Maybe need add this by default.
Before 1.0.0 we want to check send command arguments, for example if we want to keep code and data separate
A comment in the source code says it's not, but someone made the compiler throw that error (there is no full repro):
I was comparing address and slice
Problem
With current implementation contract.init
always has the same Maybe<StateInit>
type. It's not clear in the code whether you can use it from the class instance. You have to use "!", or if-statements, or stateInit from other place
Proposed Solution
It would be really convenient to know for sure out of the box if contract instance has init or not. In other words if it was inited from address or from init
One way of doing it is via generic type. Pay attention that TS compiler no longer gives an error
TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript
I can submit PR if you like this
Currently, if you open the tact docs page (https://docs.tact-lang.org/language/guides/types#maps), there is almost no info about maps. Thus, it's impossible for new devs to start using them.
Please provide more examples and a list of supported methods!
This is a blocker since there are several contracts in the wild that won't compile with current WASM binaries since they use default memory settings that limit it to 16MB.
Line 65 in bbbd403
This would allow very complicated comments to be used by smart contracts
It will be nice if users did not have to parse bounced messages themselves and can instead use Tact to route automatically by type like it does with receivers
// this is func :D
bounced(msg: Slice) {
msg.skipBits(32); // 0xFFFFFFFF
let op: Int = msg.loadUint(32);
let queryId: Int = msg.loadUint(64);
let jettonAmount: Int = msg.loadCoins();
This will make error reporting more uniform
Currently missing arguments are ignored and compilation fails on func level
receive(msg: TokenBurn) {
// Check sender (ensure it's from a valid JettonDefaultWallet)
let ctx: Context = context();
let expectedWalletAddress: Address = self.getWalletAddress(msg.owner);
require(ctx.sender == expectedWalletAddress, "Invalid sender");
require(self.totalSupply - msg.amount >= 0, "Invalid total supply after burn");
self.totalSupply = self.totalSupply - msg.amount;
self.burn(msg.amount, msg.owner, msg.queryId);
}
The reason for the unexpected revert in the TON contract is that the ctx.sender does not match the wallet's address. This inconsistency between the addresses led to the failure of the operation. What's particularly concerning is that during the testing process, the revert did not trigger any error message.
Is there anyone who can discuss with me what might have happened?
Here is my tact-jetton-code repo:
https://github.com/ipromise2324/Tact-Token-Contract/blob/dd0aeecbf129c9f2e90b03eabed9cfb112f8ac21/contracts/token.tact#L67C148-L67C148
For example:
......(code)
if (self.status_a == false && (self.level >= msg.target_level || self.item_index == 0) ) {
/// Logic///
}
......(code)
will not execute anymore?
forward
here means?"forward and notify functions that can be used to send messages to other contracts using remaining value of incoming message"
From my understanding there is no any syntax as forward
then?
external
support is good!! How we can use it? Any example code here?Often we have to send messages without attributes having only opcodes, adding structure like
message(0x12312312) ExampleMessage {
//empty
}
or something like it will make code more readable
ton-emulator
dependency is not browser friendly
await SampleTactContract.fromInit(Address.parse("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"));
Uncaught ReferenceError: __dirname is not defined
at node_modules/ton-emulator/dist/bindings/EmulatorBindings.js (EmulatorBindings.js:19:30)
at __require2 (chunk-IPKTAYBB.js?v=530909c9:18:50)
at node_modules/ton-emulator/dist/emulator/ContractSystem.js (ContractSystem.js:17:28)
at __require2 (chunk-IPKTAYBB.js?v=530909c9:18:50)
at node_modules/ton-emulator/dist/index.js (index.js:5:24)
Compilation on Windows stops with this error:
$ tact --config ./tact.config.json
๐ผ Compiling project DEX...
> ๐ Enabling debug
> DexFactory: tact compiler
> DexFactory: func compiler
Func compilation error: D:\serverdoc\tact-dex\sources\output\DEX_DexFactory.code.fc:1:1: error: File not found: DEX_DexFactory.headers.fc
#include "DEX_DexFactory.headers.fc";
By some reason func compiler can't include files. All files are generated and placed in output
folder as expected. I guess this problem is related to the func compiler itself.
"@tact-lang/compiler": "^1.1.0-beta.7",
Trying to use pow()
but getting an error with func compiler.. the compiler arrives with the tact compiler, right? I don't think I have a stale dependency
> IntsBoolsAddresses: func compiler
/Users/talkol/Work/tact-lang/tact-by-example/tact-output/02-ints-bools-addresses_IntsBoolsAddresses.code.fc:6:14: error: undefined function `**`, defining a global function of unknown type
return x ** y;
^
๐ฅ Compilation failed. Skipping packaging
dump() not supported for type: Address
Many smart contracts implement finite-state machines
It can be more expressive to allow developers to encode the Int
state variable as words instead of numbers
message A {
address: Address;
amount: Int as coins;
}
bounced(src: bounced<A>) {
self.data.set(src.address, src.amount);
}
gives error
Type bounced<"A"> does not have a field named "address"
Note that the type definition is bounced<A>
and not bounced<"A">
Currently Tact doesn't allow tailing comma in struct instantiation:
send(SendParameters{
to: safeAddress,
value: self.deployValue, // <--- error here
});
Error: contract.tact:1:1: Contract HelloWorld does not have init method
Contract without an init method does not compile. Might be a good idea to create an implicit empty init method if one is not provided by the user, so code can be boilerplate free.
let i: Int = 2 ** 240;
let j: Int = 10 ** 9;
This is useful for debugging purposes
Some func contracts parse the workchain of addresses - eg. nominator-pool
This is currently not easily supported by tact stdlib
This brought a discussion whether users should be encouraged to deploy/develop contract on the masterchain since this is advanced stuff where users can easily find themselves having bugs due to the higher cost of gas (consider a jetton that needs to send a notification across chains and there's not enough gas for this message to cross)
The idea that came up is to disallow masterchain in the compiler unless an explicit flag is used so users are informed of the risk
For example:
https://t.me/tactlang/2185
I made a mistake in my code that took me two days to figure out. I was using the == operator instead of = to assign a value to a variable, but the code still compiled without any errors.
The == operator is used for equality comparisons, not assignments. The correct operator for assignment is =.
Here's an example of the mistake I made:
self.status_A == 3; // ๐ด
self.status_A = 3; // โ
The IDE should not accept the value assignment in ==
getting successfully compiled!
Code:
import "@stdlib/deploy";
contract Maps with Deployable {
ma4: map<Address, Address>;
init(arg: map<Int, Bool>) {
// no need to initialize maps if they're empty
}
receive("set keys") {
self.ma4.set(address("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI"), myAddress());
}
}
Error:
Tact compilation failed
Error: Functions "__tact_dict_set_slice_slice" wasn't rendered
at WriterContext.render (/Users/talkol/Work/tact-lang/tact-by-example/node_modules/@tact-lang/compiler/dist/generator/Writer.js:50:19)
at writeProgram (/Users/talkol/Work/tact-lang/tact-by-example/node_modules/@tact-lang/compiler/dist/generator/writeProgram.js:345:26)
at async compile (/Users/talkol/Work/tact-lang/tact-by-example/node_modules/@tact-lang/compiler/dist/pipeline/compile.js:8:18)
at async build (/Users/talkol/Work/tact-lang/tact-by-example/node_modules/@tact-lang/compiler/dist/pipeline/build.js:72:23)
at async Object.run (/Users/talkol/Work/tact-lang/tact-by-example/node_modules/@tact-lang/compiler/dist/node.js:53:21)
at async /Users/talkol/Work/tact-lang/tact-by-example/node_modules/@tact-lang/compiler/bin/tact:18:9
let a1: Address = "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N";
Useful for contracts with constant addresses
This is a little more readable than
newAddress(0, 0x83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8)
for users looking at verified sources and understanding which address they're looking at
An attempt to update the compiler to the new @ton organization npm packages in this branch failed due to incompatibilities in tact-emulator versions 3 and 4.
Checkout the ton-org-migration
branch and run the yarn
and yarn test
commands.
Most of the errors are due to changes in the tact-emulator v4 (like here).
Tests and snapshots should be updated considering breaking changes between tact-emulator versions 3 and 4.
receive() {
require(1 == 2, "condition can`t be...");
}
After compile message wrap to same symbol like this
`target can`t be more than 10`
and code died
So far I haven't managed to find a method for dictionary creation in Tact.
When using maps(asCell) and structs(toCell) they are transformed into Cells without identifiers and with ordered data.
Thus, now I can't make the dictionary, that is necessary for NFTs to be compatible with TEP 64 Standard.
I admire the reply()
stdlib function, it follows the "it just works" mentality.
Users that just want to send simple messages don't want to deal with gas values - so the defaults in reply()
are perfect!
value: 0,
mode: SendRemainingValue + SendIgnoreErrors,
I think we should extend these defaults to send()
as well.
I was writing the following example in the section about communicating between contracts in tact-by-example and without these defaults I would need to start explaining all the flags at this point. Too low level.
import "@stdlib/deploy";
message CounterValue {
value: Int as uint32;
}
contract Counter with Deployable {
val: Int as uint32;
init() {
self.val = 0;
}
receive("increment") {
self.val = self.val + 1;
reply(CounterValue{value: self.val}.toCell());
}
receive("query") {
reply(CounterValue{value: self.val}.toCell());
}
get fun value(): Int {
return self.val;
}
}
message Reach {
counter: Address;
target: Int as uint32;
}
contract BulkAdder with Deployable {
target: Int as uint32;
init() {
self.target = 0;
}
receive(msg: Reach) {
self.target = msg.target;
send(SendParameters{
to: msg.counter,
value: 0, // yuck
mode: SendRemainingValue + SendIgnoreErrors, // yuck
body: "query".asComment()
});
}
receive(msg: CounterValue) {
if (msg.value < self.target) {
send(SendParameters{
to: sender(),
value: 0, // yuck
mode: SendRemainingValue + SendIgnoreErrors, // yuck
body: "increment".asComment()
});
}
}
}
Right now compiler doesn't check if contract state was united successfully
stdlib need a gas-efficient logarithm calculation
TVM dictionaries are nullable cells. To work with them, methods need to accept Cell?
, and it's currently impossible to mark such function "extends mutates", leading to constructions like dict = udict_store_ref(dict, 256, "name".sha256(), ...);
.
I'm looking to publish my tact
traits on npm and then reuse them in other repos by importing from node_modules
We can compare slices, so we should be able to compare Strings
Some beginner Tact programmers face the following error message:
File not found: tact_Task1.headers.fc
#include "tact_Task1.headers.fc";
๐ฅ Compilation failed. Skipping packaging
Error: Could not compile tact
error Command failed with exit code 1.
The docs (getting started guide / troubleshooting page) should mention how to fix it.
Apparently, this can happen if the dependencies are installed using npm install
while to yarn build
is used to build the project (yarn install
should have been used).
It's a good idea to standardize what it means to transfer ownership to nobody
Probably transfer to newAddress(0,0) - but we don't want users to think about these implementation details
The following code should work:
const ConstMsg: String = "ABC";
receive(ConstMsg) {}
Currently it report error Syntax error: expected ":"
i1: Int = 1234 & 0xff;
This is low level stuff so not sure it's a good idea
Need to research Solidity contracts and see if there are good use-cases for this that can't be handled with simple Bools
Right now to use it you need to import both which is not intuitive
import "@stdlib/ownable";
import "@stdlib/stoppable";
contract Counter with Stoppable {
``
s2: String = "no escaping \n \t";
Does not compile:
Syntax error: expected "\""
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.