GithubHelp home page GithubHelp logo

tact-lang / tact Goto Github PK

View Code? Open in Web Editor NEW
264.0 264.0 53.0 22.42 MB

Tact compiler main repository

Home Page: https://tact-lang.org

License: MIT License

JavaScript 0.78% TypeScript 99.22%
compiler smart-contracts tact ton

tact's People

Contributors

aandrukhovich avatar anton-trunov avatar arterialist avatar aspite avatar byakuren-hijiri avatar ex3ndr avatar gusarich avatar hiyorimi avatar howardpen9 avatar kardanovir avatar krigga avatar logvik avatar manylov avatar nonam3e avatar novusnota avatar reveloper avatar shaharyakir avatar swiftadviser avatar tactfunc avatar talkol avatar vanishmax avatar vitorpy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tact's Issues

why not set fun

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;
}

Hackathon feedback - why can't receivers be simple functions?

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:

  • "why don't these functions have names?"
  • "how can you know which function you're calling?"

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) {
        // ...
    }
}

Map of struct compilation issue when small field size specified (PUSHSLICE:integer does not fit into cell)

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"

Relative imports don't work in upward direction

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?

Proposal: Improve TS wrapper's init type

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

image

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

image

image

I can submit PR if you like this

Rebuild FunC to support bigger contracts

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.

Support bounced(msg: CustomMessage)

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();

Unexpected Revert in TON Contract Without Error Message

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 the version ^1.1.0, here is some questions:

image

  1. What is the example for new Logical expressions here? "&& now does not execute right expression if left is false and || does not execute right expression if left is true."

For example:

......(code)
if (self.status_a == false && (self.level >= msg.target_level || self.item_index == 0) ) {
/// Logic/// 
}
......(code)

will not execute anymore?

  1. What is 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?

  1. external support is good!! How we can use it? Any example code here?

Add message type without fields

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

TypeScript wrapper class cannot run in browser

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)

Func compiler problem on Windows

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",

pow error

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

What about enums?

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

`bounced<T>` does not behave as expected

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">

Trailing comma

Currently Tact doesn't allow tailing comma in struct instantiation:

send(SendParameters{
    to: safeAddress,
    value: self.deployValue, // <--- error here
});

Implicit init function if one does not exist

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.

Handling masterchain (multiple workchains)

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

Need adding the lint check for Boolean value assignment.

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!

Compilation error with map<Address, Address>

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

Support address string literals

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

Tests fail when migrating to @ton org libraries

Overview

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.

How to reproduce

Checkout the ton-org-migration branch and run the yarn and yarn test commands.

Errors

Most of the errors are due to changes in the tact-emulator v4 (like here).

telegram-cloud-photo-size-2-5465355140270771740-x

telegram-cloud-photo-size-2-5465355140270771744-x

Potential solution

Tests and snapshots should be updated considering breaking changes between tact-emulator versions 3 and 4.

A method to serialize a FunC dictionary

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.

Make SendParameters default value = 0 and mode = SendRemainingValue + SendIgnoreErrors

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()
            });
        }
    }
}

Allow nullable types in "extends mutates" functions

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(), ...);.

How to fix "File not found: tact_Task1.headers.fc" error

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).

Add renounceOwnership to OwnableTransferable

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

Do we need binary & and | operations

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.