diff --git a/test/good/sample-jetton.cfg.dot b/test/good/sample-jetton.cfg.dot new file mode 100644 index 00000000..8b7c8716 --- /dev/null +++ b/test/good/sample-jetton.cfg.dot @@ -0,0 +1,141 @@ +digraph "sample-jetton" { + node [shape=box]; + subgraph "cluster_SampleJetton__init_1781" { + label="SampleJetton__init_1781"; + "SampleJetton__init_1781_151" [label="self.totalSupply = 0"]; + "SampleJetton__init_1781_152" [label="self.max_supply = max_supply"]; + "SampleJetton__init_1781_154" [label="self.owner = owner"]; + "SampleJetton__init_1781_156" [label="self.mintable = true"]; + "SampleJetton__init_1781_158" [label="self.content = content",style=filled,fillcolor="#66A7DB"]; + "SampleJetton__init_1781_151" -> "SampleJetton__init_1781_152"; + "SampleJetton__init_1781_152" -> "SampleJetton__init_1781_154"; + "SampleJetton__init_1781_154" -> "SampleJetton__init_1781_156"; + "SampleJetton__init_1781_156" -> "SampleJetton__init_1781_158"; + } + subgraph "cluster_SampleJetton__receive_internal_simple_1731" { + label="SampleJetton__receive_internal_simple_1731"; + "SampleJetton__receive_internal_simple_1731_160" [label="let ctx: Context = context()"]; + "SampleJetton__receive_internal_simple_1731_161" [label="require(ctx.sender == self.owner, \"Not Owner\")"]; + "SampleJetton__receive_internal_simple_1731_163" [label="require(self.mintable, \"Can't Mint Anymore\")"]; + "SampleJetton__receive_internal_simple_1731_165" [label="self.mint(msg.receiver, msg.amount, self.owner)",style=filled,fillcolor="#66A7DB"]; + "SampleJetton__receive_internal_simple_1731_160" -> "SampleJetton__receive_internal_simple_1731_161"; + "SampleJetton__receive_internal_simple_1731_161" -> "SampleJetton__receive_internal_simple_1731_163"; + "SampleJetton__receive_internal_simple_1731_163" -> "SampleJetton__receive_internal_simple_1731_165"; + } + subgraph "cluster_SampleJetton__receive_internal_comment_1756_Mint: 100" { + label="SampleJetton__receive_internal_comment_1756_Mint: 100"; + "SampleJetton__receive_internal_comment_1756_Mint: 100_167" [label="let ctx: Context = context()"]; + "SampleJetton__receive_internal_comment_1756_Mint: 100_168" [label="require(self.mintable, \"Can't Mint Anymore\")"]; + "SampleJetton__receive_internal_comment_1756_Mint: 100_170" [label="self.mint(ctx.sender, 100, self.owner)",style=filled,fillcolor="#66A7DB"]; + "SampleJetton__receive_internal_comment_1756_Mint: 100_167" -> "SampleJetton__receive_internal_comment_1756_Mint: 100_168"; + "SampleJetton__receive_internal_comment_1756_Mint: 100_168" -> "SampleJetton__receive_internal_comment_1756_Mint: 100_170"; + } + subgraph "cluster_SampleJetton__receive_internal_comment_1779_Owner: MintClose" { + label="SampleJetton__receive_internal_comment_1779_Owner: MintClose"; + "SampleJetton__receive_internal_comment_1779_Owner: MintClose_172" [label="let ctx: Context = context()"]; + "SampleJetton__receive_internal_comment_1779_Owner: MintClose_173" [label="require(ctx.sender == self.owner, \"Not Owner\")"]; + "SampleJetton__receive_internal_comment_1779_Owner: MintClose_175" [label="self.mintable = false",style=filled,fillcolor="#66A7DB"]; + "SampleJetton__receive_internal_comment_1779_Owner: MintClose_172" -> "SampleJetton__receive_internal_comment_1779_Owner: MintClose_173"; + "SampleJetton__receive_internal_comment_1779_Owner: MintClose_173" -> "SampleJetton__receive_internal_comment_1779_Owner: MintClose_175"; + } + subgraph "cluster_JettonDefaultWallet__init_2778" { + label="JettonDefaultWallet__init_2778"; + "JettonDefaultWallet__init_2778_178" [label="self.balance = 0"]; + "JettonDefaultWallet__init_2778_179" [label="self.owner = owner"]; + "JettonDefaultWallet__init_2778_181" [label="self.master = master",style=filled,fillcolor="#66A7DB"]; + "JettonDefaultWallet__init_2778_178" -> "JettonDefaultWallet__init_2778_179"; + "JettonDefaultWallet__init_2778_179" -> "JettonDefaultWallet__init_2778_181"; + } + subgraph "cluster_JettonDefaultWallet__receive_internal_simple_2385" { + label="JettonDefaultWallet__receive_internal_simple_2385"; + "JettonDefaultWallet__receive_internal_simple_2385_183" [label="let ctx: Context = context()"]; + "JettonDefaultWallet__receive_internal_simple_2385_184" [label="require(ctx.sender == self.owner, \"Invalid sender\")"]; + "JettonDefaultWallet__receive_internal_simple_2385_186" [label="let fwdFee: Int = ctx.readForwardFee() + ctx.readForwardFee()"]; + "JettonDefaultWallet__receive_internal_simple_2385_188" [label="let final: Int = 2 * self.gasConsumption + self.minTonsForStorage + fwdFee"]; + "JettonDefaultWallet__receive_internal_simple_2385_190" [label="require(ctx.value > min(final, ton(\"0.01\")), \"Invalid value!!\")"]; + "JettonDefaultWallet__receive_internal_simple_2385_192" [label="self.balance = self.balance - msg.amount"]; + "JettonDefaultWallet__receive_internal_simple_2385_194" [label="require(self.balance >= 0, \"Invalid balance\")"]; + "JettonDefaultWallet__receive_internal_simple_2385_196" [label="let init: StateInit = initOf JettonDefaultWallet(self.master, msg.destination)"]; + "JettonDefaultWallet__receive_internal_simple_2385_198" [label="let walletAddress: Address = contractAddress(init)"]; + "JettonDefaultWallet__receive_internal_simple_2385_200" [label="send(SendParameters{to: walletAddress, value: 0, mode: SendRemainingValue, bounce: false, body: TokenTransferInternal{queryId: msg.queryId, amount: msg.amount, from: self.owner, response_destination: msg.response_destination, forward_ton_amount: msg.forward_ton_amount, forward_payload: msg.forward_payload}.toCell(), code: init.code, data: init.data})",style=filled,fillcolor="#66A7DB"]; + "JettonDefaultWallet__receive_internal_simple_2385_183" -> "JettonDefaultWallet__receive_internal_simple_2385_184"; + "JettonDefaultWallet__receive_internal_simple_2385_184" -> "JettonDefaultWallet__receive_internal_simple_2385_186"; + "JettonDefaultWallet__receive_internal_simple_2385_186" -> "JettonDefaultWallet__receive_internal_simple_2385_188"; + "JettonDefaultWallet__receive_internal_simple_2385_188" -> "JettonDefaultWallet__receive_internal_simple_2385_190"; + "JettonDefaultWallet__receive_internal_simple_2385_190" -> "JettonDefaultWallet__receive_internal_simple_2385_192"; + "JettonDefaultWallet__receive_internal_simple_2385_192" -> "JettonDefaultWallet__receive_internal_simple_2385_194"; + "JettonDefaultWallet__receive_internal_simple_2385_194" -> "JettonDefaultWallet__receive_internal_simple_2385_196"; + "JettonDefaultWallet__receive_internal_simple_2385_196" -> "JettonDefaultWallet__receive_internal_simple_2385_198"; + "JettonDefaultWallet__receive_internal_simple_2385_198" -> "JettonDefaultWallet__receive_internal_simple_2385_200"; + } + subgraph "cluster_JettonDefaultWallet__receive_internal_simple_2555" { + label="JettonDefaultWallet__receive_internal_simple_2555"; + "JettonDefaultWallet__receive_internal_simple_2555_202" [label="let ctx: Context = context()"]; + "JettonDefaultWallet__receive_internal_simple_2555_203" [label="if (ctx.sender != self.master)"]; + "JettonDefaultWallet__receive_internal_simple_2555_205" [label="let sInit: StateInit = initOf JettonDefaultWallet(self.master, msg.from)"]; + "JettonDefaultWallet__receive_internal_simple_2555_207" [label="require(contractAddress(sInit) == ctx.sender, \"Invalid sender!\")"]; + "JettonDefaultWallet__receive_internal_simple_2555_209" [label="self.balance = self.balance + msg.amount"]; + "JettonDefaultWallet__receive_internal_simple_2555_211" [label="require(self.balance >= 0, \"Invalid balance\")"]; + "JettonDefaultWallet__receive_internal_simple_2555_213" [label="if (msg.forward_ton_amount > 0)"]; + "JettonDefaultWallet__receive_internal_simple_2555_215" [label="send(SendParameters{to: self.owner, value: msg.forward_ton_amount, bounce: false, body: TokenNotification{queryId: msg.queryId, amount: msg.amount, from: msg.from, forward_payload: msg.forward_payload}.toCell()})"]; + "JettonDefaultWallet__receive_internal_simple_2555_217" [label="let msgValue: Int = self.msgValue(ctx.value)"]; + "JettonDefaultWallet__receive_internal_simple_2555_219" [label="let fwdFee: Int = ctx.readForwardFee()"]; + "JettonDefaultWallet__receive_internal_simple_2555_221" [label="msgValue = msgValue - msg.forward_ton_amount - fwdFee"]; + "JettonDefaultWallet__receive_internal_simple_2555_223" [label="if (msg.response_destination != null)"]; + "JettonDefaultWallet__receive_internal_simple_2555_225" [label="send(SendParameters{to: !!msg.response_destination, value: msgValue, bounce: false, body: TokenExcesses{queryId: msg.queryId}.toCell(), mode: SendIgnoreErrors})",style=filled,fillcolor="#66A7DB"]; + "JettonDefaultWallet__receive_internal_simple_2555_202" -> "JettonDefaultWallet__receive_internal_simple_2555_203"; + "JettonDefaultWallet__receive_internal_simple_2555_203" -> "JettonDefaultWallet__receive_internal_simple_2555_205"; + "JettonDefaultWallet__receive_internal_simple_2555_205" -> "JettonDefaultWallet__receive_internal_simple_2555_207"; + "JettonDefaultWallet__receive_internal_simple_2555_207" -> "JettonDefaultWallet__receive_internal_simple_2555_209"; + "JettonDefaultWallet__receive_internal_simple_2555_209" -> "JettonDefaultWallet__receive_internal_simple_2555_211"; + "JettonDefaultWallet__receive_internal_simple_2555_211" -> "JettonDefaultWallet__receive_internal_simple_2555_213"; + "JettonDefaultWallet__receive_internal_simple_2555_213" -> "JettonDefaultWallet__receive_internal_simple_2555_215"; + "JettonDefaultWallet__receive_internal_simple_2555_215" -> "JettonDefaultWallet__receive_internal_simple_2555_217"; + "JettonDefaultWallet__receive_internal_simple_2555_217" -> "JettonDefaultWallet__receive_internal_simple_2555_219"; + "JettonDefaultWallet__receive_internal_simple_2555_219" -> "JettonDefaultWallet__receive_internal_simple_2555_221"; + "JettonDefaultWallet__receive_internal_simple_2555_221" -> "JettonDefaultWallet__receive_internal_simple_2555_223"; + "JettonDefaultWallet__receive_internal_simple_2555_223" -> "JettonDefaultWallet__receive_internal_simple_2555_225"; + } + subgraph "cluster_JettonDefaultWallet__msgValue" { + label="JettonDefaultWallet__msgValue"; + "JettonDefaultWallet__msgValue_227" [label="let tonBalanceBeforeMsg: Int = myBalance() - value"]; + "JettonDefaultWallet__msgValue_228" [label="let storageFee: Int = self.minTonsForStorage - min(tonBalanceBeforeMsg, self.minTonsForStorage)"]; + "JettonDefaultWallet__msgValue_230" [label="value -= storageFee + self.gasConsumption"]; + "JettonDefaultWallet__msgValue_232" [label="return value",style=filled,fillcolor="#66A7DB"]; + "JettonDefaultWallet__msgValue_227" -> "JettonDefaultWallet__msgValue_228"; + "JettonDefaultWallet__msgValue_228" -> "JettonDefaultWallet__msgValue_230"; + "JettonDefaultWallet__msgValue_230" -> "JettonDefaultWallet__msgValue_232"; + } + subgraph "cluster_JettonDefaultWallet__receive_internal_simple_2700" { + label="JettonDefaultWallet__receive_internal_simple_2700"; + "JettonDefaultWallet__receive_internal_simple_2700_234" [label="let ctx: Context = context()"]; + "JettonDefaultWallet__receive_internal_simple_2700_235" [label="require(ctx.sender == self.owner, \"Invalid sender\")"]; + "JettonDefaultWallet__receive_internal_simple_2700_237" [label="self.balance = self.balance - msg.amount"]; + "JettonDefaultWallet__receive_internal_simple_2700_239" [label="require(self.balance >= 0, \"Invalid balance\")"]; + "JettonDefaultWallet__receive_internal_simple_2700_241" [label="let fwdFee: Int = ctx.readForwardFee()"]; + "JettonDefaultWallet__receive_internal_simple_2700_243" [label="require(ctx.value > fwdFee + 2 * self.gasConsumption + self.minTonsForStorage, \"Invalid value - Burn\")"]; + "JettonDefaultWallet__receive_internal_simple_2700_245" [label="send(SendParameters{to: self.master, value: 0, mode: SendRemainingValue, bounce: true, body: TokenBurnNotification{queryId: msg.queryId, amount: msg.amount, owner: self.owner, response_destination: self.owner}.toCell()})",style=filled,fillcolor="#66A7DB"]; + "JettonDefaultWallet__receive_internal_simple_2700_234" -> "JettonDefaultWallet__receive_internal_simple_2700_235"; + "JettonDefaultWallet__receive_internal_simple_2700_235" -> "JettonDefaultWallet__receive_internal_simple_2700_237"; + "JettonDefaultWallet__receive_internal_simple_2700_237" -> "JettonDefaultWallet__receive_internal_simple_2700_239"; + "JettonDefaultWallet__receive_internal_simple_2700_239" -> "JettonDefaultWallet__receive_internal_simple_2700_241"; + "JettonDefaultWallet__receive_internal_simple_2700_241" -> "JettonDefaultWallet__receive_internal_simple_2700_243"; + "JettonDefaultWallet__receive_internal_simple_2700_243" -> "JettonDefaultWallet__receive_internal_simple_2700_245"; + } + subgraph "cluster_JettonDefaultWallet__receive_bounce_2744" { + label="JettonDefaultWallet__receive_bounce_2744"; + "JettonDefaultWallet__receive_bounce_2744_247" [label="let op: Int = msg.loadUint(32)"]; + "JettonDefaultWallet__receive_bounce_2744_248" [label="let queryId: Int = msg.loadUint(64)"]; + "JettonDefaultWallet__receive_bounce_2744_250" [label="let jettonAmount: Int = msg.loadCoins()"]; + "JettonDefaultWallet__receive_bounce_2744_252" [label="require(op == 395134233 || op == 2078119902, \"Invalid bounced message\")"]; + "JettonDefaultWallet__receive_bounce_2744_254" [label="self.balance = self.balance + jettonAmount",style=filled,fillcolor="#66A7DB"]; + "JettonDefaultWallet__receive_bounce_2744_247" -> "JettonDefaultWallet__receive_bounce_2744_248"; + "JettonDefaultWallet__receive_bounce_2744_248" -> "JettonDefaultWallet__receive_bounce_2744_250"; + "JettonDefaultWallet__receive_bounce_2744_250" -> "JettonDefaultWallet__receive_bounce_2744_252"; + "JettonDefaultWallet__receive_bounce_2744_252" -> "JettonDefaultWallet__receive_bounce_2744_254"; + } + subgraph "cluster_JettonDefaultWallet__get_wallet_data" { + label="JettonDefaultWallet__get_wallet_data"; + "JettonDefaultWallet__get_wallet_data_256" [label="return JettonWalletData{balance: self.balance, owner: self.owner, master: self.master, walletCode: initOf JettonDefaultWallet(self.master, self.owner).code}",style=filled,fillcolor="#66A7DB"]; + } +} diff --git a/test/good/sample-jetton.cfg.json b/test/good/sample-jetton.cfg.json new file mode 100644 index 00000000..def82b3f --- /dev/null +++ b/test/good/sample-jetton.cfg.json @@ -0,0 +1,885 @@ +{ + "projectName": "sample-jetton", + "functions": [], + "contracts": [ + { + "name": "SampleJetton", + "methods": [ + { + "name": "SampleJetton.init_1781", + "cfg": { + "nodes": [ + { + "id": 151, + "stmtID": 1670, + "srcEdges": [], + "dstEdges": [ + 153 + ] + }, + { + "id": 152, + "stmtID": 1675, + "srcEdges": [ + 153 + ], + "dstEdges": [ + 155 + ] + }, + { + "id": 154, + "stmtID": 1680, + "srcEdges": [ + 155 + ], + "dstEdges": [ + 157 + ] + }, + { + "id": 156, + "stmtID": 1685, + "srcEdges": [ + 157 + ], + "dstEdges": [ + 159 + ] + }, + { + "id": 158, + "stmtID": 1690, + "srcEdges": [ + 159 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 153, + "src": 151, + "dst": 152 + }, + { + "id": 155, + "src": 152, + "dst": 154 + }, + { + "id": 157, + "src": 154, + "dst": 156 + }, + { + "id": 159, + "src": 156, + "dst": 158 + } + ] + } + }, + { + "name": "SampleJetton.receive_internal_simple_1731", + "cfg": { + "nodes": [ + { + "id": 160, + "stmtID": 1699, + "srcEdges": [], + "dstEdges": [ + 162 + ] + }, + { + "id": 161, + "stmtID": 1710, + "srcEdges": [ + 162 + ], + "dstEdges": [ + 164 + ] + }, + { + "id": 163, + "stmtID": 1717, + "srcEdges": [ + 164 + ], + "dstEdges": [ + 166 + ] + }, + { + "id": 165, + "stmtID": 1730, + "srcEdges": [ + 166 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 162, + "src": 160, + "dst": 161 + }, + { + "id": 164, + "src": 161, + "dst": 163 + }, + { + "id": 166, + "src": 163, + "dst": 165 + } + ] + } + }, + { + "name": "SampleJetton.receive_internal_comment_1756_Mint: 100", + "cfg": { + "nodes": [ + { + "id": 167, + "stmtID": 1737, + "srcEdges": [], + "dstEdges": [ + 169 + ] + }, + { + "id": 168, + "stmtID": 1744, + "srcEdges": [ + 169 + ], + "dstEdges": [ + 171 + ] + }, + { + "id": 170, + "stmtID": 1755, + "srcEdges": [ + 171 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 169, + "src": 167, + "dst": 168 + }, + { + "id": 171, + "src": 168, + "dst": 170 + } + ] + } + }, + { + "name": "SampleJetton.receive_internal_comment_1779_Owner: MintClose", + "cfg": { + "nodes": [ + { + "id": 172, + "stmtID": 1762, + "srcEdges": [], + "dstEdges": [ + 174 + ] + }, + { + "id": 173, + "stmtID": 1773, + "srcEdges": [ + 174 + ], + "dstEdges": [ + 176 + ] + }, + { + "id": 175, + "stmtID": 1778, + "srcEdges": [ + 176 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 174, + "src": 172, + "dst": 173 + }, + { + "id": 176, + "src": 173, + "dst": 175 + } + ] + } + } + ] + }, + { + "name": "JettonDefaultWallet", + "methods": [ + { + "name": "JettonDefaultWallet.init_2778", + "cfg": { + "nodes": [ + { + "id": 178, + "stmtID": 2216, + "srcEdges": [], + "dstEdges": [ + 180 + ] + }, + { + "id": 179, + "stmtID": 2221, + "srcEdges": [ + 180 + ], + "dstEdges": [ + 182 + ] + }, + { + "id": 181, + "stmtID": 2226, + "srcEdges": [ + 182 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 180, + "src": 178, + "dst": 179 + }, + { + "id": 182, + "src": 179, + "dst": 181 + } + ] + } + }, + { + "name": "JettonDefaultWallet.receive_internal_simple_2385", + "cfg": { + "nodes": [ + { + "id": 183, + "stmtID": 2235, + "srcEdges": [], + "dstEdges": [ + 185 + ] + }, + { + "id": 184, + "stmtID": 2246, + "srcEdges": [ + 185 + ], + "dstEdges": [ + 187 + ] + }, + { + "id": 186, + "stmtID": 2256, + "srcEdges": [ + 187 + ], + "dstEdges": [ + 189 + ] + }, + { + "id": 188, + "stmtID": 2270, + "srcEdges": [ + 189 + ], + "dstEdges": [ + 191 + ] + }, + { + "id": 190, + "stmtID": 2284, + "srcEdges": [ + 191 + ], + "dstEdges": [ + 193 + ] + }, + { + "id": 192, + "stmtID": 2295, + "srcEdges": [ + 193 + ], + "dstEdges": [ + 195 + ] + }, + { + "id": 194, + "stmtID": 2304, + "srcEdges": [ + 195 + ], + "dstEdges": [ + 197 + ] + }, + { + "id": 196, + "stmtID": 2315, + "srcEdges": [ + 197 + ], + "dstEdges": [ + 199 + ] + }, + { + "id": 198, + "stmtID": 2321, + "srcEdges": [ + 199 + ], + "dstEdges": [ + 201 + ] + }, + { + "id": 200, + "stmtID": 2384, + "srcEdges": [ + 201 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 185, + "src": 183, + "dst": 184 + }, + { + "id": 187, + "src": 184, + "dst": 186 + }, + { + "id": 189, + "src": 186, + "dst": 188 + }, + { + "id": 191, + "src": 188, + "dst": 190 + }, + { + "id": 193, + "src": 190, + "dst": 192 + }, + { + "id": 195, + "src": 192, + "dst": 194 + }, + { + "id": 197, + "src": 194, + "dst": 196 + }, + { + "id": 199, + "src": 196, + "dst": 198 + }, + { + "id": 201, + "src": 198, + "dst": 200 + } + ] + } + }, + { + "name": "JettonDefaultWallet.receive_internal_simple_2555", + "cfg": { + "nodes": [ + { + "id": 202, + "stmtID": 2393, + "srcEdges": [], + "dstEdges": [ + 204 + ] + }, + { + "id": 203, + "stmtID": 2423, + "srcEdges": [ + 204 + ], + "dstEdges": [ + 206 + ] + }, + { + "id": 205, + "stmtID": 2411, + "srcEdges": [ + 206 + ], + "dstEdges": [ + 208 + ] + }, + { + "id": 207, + "stmtID": 2422, + "srcEdges": [ + 208 + ], + "dstEdges": [ + 210 + ] + }, + { + "id": 209, + "stmtID": 2434, + "srcEdges": [ + 210 + ], + "dstEdges": [ + 212 + ] + }, + { + "id": 211, + "stmtID": 2443, + "srcEdges": [ + 212 + ], + "dstEdges": [ + 214 + ] + }, + { + "id": 213, + "stmtID": 2493, + "srcEdges": [ + 214 + ], + "dstEdges": [ + 216 + ] + }, + { + "id": 215, + "stmtID": 2492, + "srcEdges": [ + 216 + ], + "dstEdges": [ + 218 + ] + }, + { + "id": 217, + "stmtID": 2502, + "srcEdges": [ + 218 + ], + "dstEdges": [ + 220 + ] + }, + { + "id": 219, + "stmtID": 2508, + "srcEdges": [ + 220 + ], + "dstEdges": [ + 222 + ] + }, + { + "id": 221, + "stmtID": 2517, + "srcEdges": [ + 222 + ], + "dstEdges": [ + 224 + ] + }, + { + "id": 223, + "stmtID": 2554, + "srcEdges": [ + 224 + ], + "dstEdges": [ + 226 + ] + }, + { + "id": 225, + "stmtID": 2553, + "srcEdges": [ + 226 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 204, + "src": 202, + "dst": 203 + }, + { + "id": 206, + "src": 203, + "dst": 205 + }, + { + "id": 208, + "src": 205, + "dst": 207 + }, + { + "id": 210, + "src": 207, + "dst": 209 + }, + { + "id": 212, + "src": 209, + "dst": 211 + }, + { + "id": 214, + "src": 211, + "dst": 213 + }, + { + "id": 216, + "src": 213, + "dst": 215 + }, + { + "id": 218, + "src": 215, + "dst": 217 + }, + { + "id": 220, + "src": 217, + "dst": 219 + }, + { + "id": 222, + "src": 219, + "dst": 221 + }, + { + "id": 224, + "src": 221, + "dst": 223 + }, + { + "id": 226, + "src": 223, + "dst": 225 + } + ] + } + }, + { + "name": "JettonDefaultWallet.msgValue", + "cfg": { + "nodes": [ + { + "id": 227, + "stmtID": 2567, + "srcEdges": [], + "dstEdges": [ + 229 + ] + }, + { + "id": 228, + "stmtID": 2580, + "srcEdges": [ + 229 + ], + "dstEdges": [ + 231 + ] + }, + { + "id": 230, + "stmtID": 2587, + "srcEdges": [ + 231 + ], + "dstEdges": [ + 233 + ] + }, + { + "id": 232, + "stmtID": 2589, + "srcEdges": [ + 233 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 229, + "src": 227, + "dst": 228 + }, + { + "id": 231, + "src": 228, + "dst": 230 + }, + { + "id": 233, + "src": 230, + "dst": 232 + } + ] + } + }, + { + "name": "JettonDefaultWallet.receive_internal_simple_2700", + "cfg": { + "nodes": [ + { + "id": 234, + "stmtID": 2598, + "srcEdges": [], + "dstEdges": [ + 236 + ] + }, + { + "id": 235, + "stmtID": 2609, + "srcEdges": [ + 236 + ], + "dstEdges": [ + 238 + ] + }, + { + "id": 237, + "stmtID": 2620, + "srcEdges": [ + 238 + ], + "dstEdges": [ + 240 + ] + }, + { + "id": 239, + "stmtID": 2629, + "srcEdges": [ + 240 + ], + "dstEdges": [ + 242 + ] + }, + { + "id": 241, + "stmtID": 2635, + "srcEdges": [ + 242 + ], + "dstEdges": [ + 244 + ] + }, + { + "id": 243, + "stmtID": 2654, + "srcEdges": [ + 244 + ], + "dstEdges": [ + 246 + ] + }, + { + "id": 245, + "stmtID": 2699, + "srcEdges": [ + 246 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 236, + "src": 234, + "dst": 235 + }, + { + "id": 238, + "src": 235, + "dst": 237 + }, + { + "id": 240, + "src": 237, + "dst": 239 + }, + { + "id": 242, + "src": 239, + "dst": 241 + }, + { + "id": 244, + "src": 241, + "dst": 243 + }, + { + "id": 246, + "src": 243, + "dst": 245 + } + ] + } + }, + { + "name": "JettonDefaultWallet.receive_bounce_2744", + "cfg": { + "nodes": [ + { + "id": 247, + "stmtID": 2710, + "srcEdges": [], + "dstEdges": [ + 249 + ] + }, + { + "id": 248, + "stmtID": 2717, + "srcEdges": [ + 249 + ], + "dstEdges": [ + 251 + ] + }, + { + "id": 250, + "stmtID": 2723, + "srcEdges": [ + 251 + ], + "dstEdges": [ + 253 + ] + }, + { + "id": 252, + "stmtID": 2734, + "srcEdges": [ + 253 + ], + "dstEdges": [ + 255 + ] + }, + { + "id": 254, + "stmtID": 2743, + "srcEdges": [ + 255 + ], + "dstEdges": [] + } + ], + "edges": [ + { + "id": 249, + "src": 247, + "dst": 248 + }, + { + "id": 251, + "src": 248, + "dst": 250 + }, + { + "id": 253, + "src": 250, + "dst": 252 + }, + { + "id": 255, + "src": 252, + "dst": 254 + } + ] + } + }, + { + "name": "JettonDefaultWallet.get_wallet_data", + "cfg": { + "nodes": [ + { + "id": 256, + "stmtID": 2776, + "srcEdges": [], + "dstEdges": [] + } + ], + "edges": [] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/good/sample-jetton.expected.out b/test/good/sample-jetton.expected.out new file mode 100644 index 00000000..19f85bdd --- /dev/null +++ b/test/good/sample-jetton.expected.out @@ -0,0 +1,62 @@ +test/good/sample-jetton.tact:312:9: + 311 | let op: Int = msg.loadUint(32); +> 312 | let queryId: Int = msg.loadUint(64); + ^ + 313 | let jettonAmount: Int = msg.loadCoins(); +Variable is never accessed +Help: Consider removing the variable +See: https://nowarp.github.io/tools/misti/docs/detectors/NeverAccessedVariables + +test/good/sample-jetton.tact:67:9: + 66 | self.requireWallet(msg.owner); // Check wallet +> 67 | self.totalSupply = self.totalSupply - msg.amount; // Update supply + ^ + 68 | +Prefer Augmented Assignment +Help: Consider using augmented assignment instead: self.totalSupply -= msg.amount +See: https://nowarp.github.io/tools/misti/docs/detectors/PreferAugmentedAssign + +test/good/sample-jetton.tact:89:9: + 88 | require(self.totalSupply + amount <= self.max_supply, "The total supply will be overlapping."); +> 89 | self.totalSupply = self.totalSupply + amount; // Update total supply + ^ + 90 | +Prefer Augmented Assignment +Help: Consider using augmented assignment instead: self.totalSupply += amount +See: https://nowarp.github.io/tools/misti/docs/detectors/PreferAugmentedAssign + +test/good/sample-jetton.tact:213:9: + 212 | +> 213 | self.balance = self.balance - msg.amount; // Update balance + ^ + 214 | require(self.balance >= 0, "Invalid balance"); +Prefer Augmented Assignment +Help: Consider using augmented assignment instead: self.balance -= msg.amount +See: https://nowarp.github.io/tools/misti/docs/detectors/PreferAugmentedAssign + +test/good/sample-jetton.tact:244:9: + 243 | +> 244 | self.balance = self.balance + msg.amount; + ^ + 245 | require(self.balance >= 0, "Invalid balance"); // Update balance +Prefer Augmented Assignment +Help: Consider using augmented assignment instead: self.balance += msg.amount +See: https://nowarp.github.io/tools/misti/docs/detectors/PreferAugmentedAssign + +test/good/sample-jetton.tact:290:9: + 289 | +> 290 | self.balance = self.balance - msg.amount; // Update balance + ^ + 291 | require(self.balance >= 0, "Invalid balance"); +Prefer Augmented Assignment +Help: Consider using augmented assignment instead: self.balance -= msg.amount +See: https://nowarp.github.io/tools/misti/docs/detectors/PreferAugmentedAssign + +test/good/sample-jetton.tact:316:9: + 315 | +> 316 | self.balance = self.balance + jettonAmount; // Update balance + ^ + 317 | } +Prefer Augmented Assignment +Help: Consider using augmented assignment instead: self.balance += jettonAmount +See: https://nowarp.github.io/tools/misti/docs/detectors/PreferAugmentedAssign \ No newline at end of file diff --git a/test/good/sample-jetton.tact b/test/good/sample-jetton.tact new file mode 100644 index 00000000..233d773d --- /dev/null +++ b/test/good/sample-jetton.tact @@ -0,0 +1,334 @@ +import "@stdlib/ownable"; + +message Mint { + amount: Int; + receiver: Address; +} + +struct JettonData { + totalSupply: Int; + mintable: Bool; + owner: Address; + content: Cell; + walletCode: Cell; +} + +contract SampleJetton with Jetton { + totalSupply: Int as coins; + max_supply: Int as coins; + owner: Address; + content: Cell; + mintable: Bool; + + init(owner: Address, content: Cell, max_supply: Int) { + self.totalSupply = 0; + self.max_supply = max_supply; + self.owner = owner; + self.mintable = true; + self.content = content; + } + + receive(msg: Mint) { + let ctx: Context = context(); + require(ctx.sender == self.owner, "Not Owner"); + require(self.mintable, "Can't Mint Anymore"); + self.mint(msg.receiver, msg.amount, self.owner); //(to, amount, response_destination) + } + + receive("Mint: 100") { // Public Minting + let ctx: Context = context(); + require(self.mintable, "Can't Mint Anymore"); + self.mint(ctx.sender, 100, self.owner); + } + + receive("Owner: MintClose") { + let ctx: Context = context(); + require(ctx.sender == self.owner, "Not Owner"); + self.mintable = false; + } +} + +// ============================================================================================================ // +@interface("org.ton.jetton.master") +trait Jetton with Ownable { + totalSupply: Int; // Already set initially + max_supply: Int; + mintable: Bool; + owner: Address; + content: Cell; + + receive(msg: TokenUpdateContent) { + self.requireOwner(); // Allow changing content only by owner + self.content = msg.content; // Update content + } + + receive(msg: TokenBurnNotification) { + self.requireWallet(msg.owner); // Check wallet + self.totalSupply = self.totalSupply - msg.amount; // Update supply + + if (msg.response_destination != null) { // Cashback + send(SendParameters{ + to: msg.response_destination!!, + value: 0, + bounce: false, + mode: SendRemainingValue | SendIgnoreErrors, + body: TokenExcesses{ + queryId: msg.queryId + }.toCell() + }); + } + } + + // Private Methods // + // @to The Address receive the Jetton token after minting + // @amount The amount of Jetton token being minted + // @response_destination The previous owner address + fun mint(to: Address, amount: Int, response_destination: Address) { + + require(self.totalSupply + amount <= self.max_supply, "The total supply will be overlapping."); + self.totalSupply = self.totalSupply + amount; // Update total supply + + let wInit: StateInit = self.getJettonWalletInit(to); // Create message + send(SendParameters{ + to: contractAddress(wInit), + value: 0, + bounce: false, + mode: SendRemainingValue, + body: TokenTransferInternal{ + queryId: 0, + amount: amount, + from: myAddress(), + response_destination: response_destination, + forward_ton_amount: 0, + forward_payload: emptySlice() + }.toCell(), + code: wInit.code, + data: wInit.data + }); + } + + fun requireWallet(owner: Address) { + let ctx: Context = context(); + let wInit: StateInit = self.getJettonWalletInit(owner); + require(contractAddress(wInit) == ctx.sender, "Invalid sender"); + } + + virtual fun getJettonWalletInit(address: Address): StateInit { + return initOf JettonDefaultWallet(myAddress(), address); + } + + // Get Methods // + get fun get_jetton_data(): JettonData { + let code: Cell = self.getJettonWalletInit(myAddress()).code; + return JettonData{ + totalSupply: self.totalSupply, + mintable: self.mintable, + owner: self.owner, + content: self.content, + walletCode: code + }; + } + + get fun get_wallet_address(owner: Address): Address { + let wInit: StateInit = self.getJettonWalletInit(owner); + return contractAddress(wInit); + } +} + + +message(0xf8a7ea5) TokenTransfer { + queryId: Int as uint64; + amount: Int as coins; + destination: Address; + response_destination: Address?; + custom_payload: Cell?; + forward_ton_amount: Int as coins; + forward_payload: Slice as remaining; +} + +message(0x178d4519) TokenTransferInternal { + queryId: Int as uint64; + amount: Int as coins; + from: Address; + response_destination: Address?; + forward_ton_amount: Int as coins; + forward_payload: Slice as remaining; +} + +message(0x7362d09c) TokenNotification { + queryId: Int as uint64; + amount: Int as coins; + from: Address; + forward_payload: Slice as remaining; // Comment Text message when Transfer the jetton +} + +message(0x595f07bc) TokenBurn { + queryId: Int as uint64; + amount: Int as coins; + owner: Address; + response_destination: Address; +} + +message(0x7bdd97de) TokenBurnNotification { + queryId: Int as uint64; + amount: Int as coins; + owner: Address; + response_destination: Address?; +} + +message(0xd53276db) TokenExcesses { + queryId: Int as uint64; +} + +message TokenUpdateContent { + content: Cell; +} + + + +// ============================================================ // +@interface("org.ton.jetton.wallet") +contract JettonDefaultWallet { + const minTonsForStorage: Int = ton("0.01"); + const gasConsumption: Int = ton("0.01"); + + balance: Int; + owner: Address; + master: Address; + + init(master: Address, owner: Address) { + self.balance = 0; + self.owner = owner; + self.master = master; + } + + receive(msg: TokenTransfer) { // 0xf8a7ea5 + let ctx: Context = context(); // Check sender + require(ctx.sender == self.owner, "Invalid sender"); + + let fwdFee: Int = ctx.readForwardFee() + ctx.readForwardFee(); // Gas checks + let final: Int = 2 * self.gasConsumption + self.minTonsForStorage + fwdFee; + require(ctx.value > min(final, ton("0.01")), "Invalid value!!"); + + self.balance = self.balance - msg.amount; // Update balance + require(self.balance >= 0, "Invalid balance"); + + let init: StateInit = initOf JettonDefaultWallet(self.master, msg.destination); + let walletAddress: Address = contractAddress(init); + send(SendParameters{ + to: walletAddress, + value: 0, + mode: SendRemainingValue, + bounce: false, + body: TokenTransferInternal{ + queryId: msg.queryId, + amount: msg.amount, + from: self.owner, + response_destination: msg.response_destination, + forward_ton_amount: msg.forward_ton_amount, + forward_payload: msg.forward_payload + }.toCell(), + code: init.code, + data: init.data + }); + } + + receive(msg: TokenTransferInternal) { // 0x178d4519 + let ctx: Context = context(); + + if (ctx.sender != self.master) { + let sInit: StateInit = initOf JettonDefaultWallet(self.master, msg.from); + require(contractAddress(sInit) == ctx.sender, "Invalid sender!"); + } + + self.balance = self.balance + msg.amount; + require(self.balance >= 0, "Invalid balance"); // Update balance + + if (msg.forward_ton_amount > 0) { + send(SendParameters{ + to: self.owner, + value: msg.forward_ton_amount, + bounce: false, + body: TokenNotification { // 0x7362d09c - notify new owner + queryId: msg.queryId, + amount: msg.amount, + from: msg.from, + forward_payload: msg.forward_payload + }.toCell() + }); + } + + let msgValue: Int = self.msgValue(ctx.value); // Get value for gas + let fwdFee: Int = ctx.readForwardFee(); + msgValue = msgValue - msg.forward_ton_amount - fwdFee; + // msgValue = msgValue - msg.forward_ton_amount - min(fwdFee, ton("0.01")); + + if (msg.response_destination != null) { // Cashback + send(SendParameters { + to: msg.response_destination!!, + value: msgValue, + bounce: false, + body: TokenExcesses { // 0xd53276db + queryId: msg.queryId + }.toCell(), + mode: SendIgnoreErrors + }); + } + } + + get fun msgValue(value: Int): Int { + let tonBalanceBeforeMsg: Int = myBalance() - value; + let storageFee: Int = self.minTonsForStorage - min(tonBalanceBeforeMsg, self.minTonsForStorage); + value -= storageFee + self.gasConsumption; + return value; + } + + receive(msg: TokenBurn) { + let ctx: Context = context(); + require(ctx.sender == self.owner, "Invalid sender"); // Check sender + + self.balance = self.balance - msg.amount; // Update balance + require(self.balance >= 0, "Invalid balance"); + + let fwdFee: Int = ctx.readForwardFee(); // Gas checks + require(ctx.value > fwdFee + 2 * self.gasConsumption + self.minTonsForStorage, "Invalid value - Burn"); + + send(SendParameters{ // Burn tokens + to: self.master, + value: 0, + mode: SendRemainingValue, + bounce: true, + body: TokenBurnNotification{ + queryId: msg.queryId, + amount: msg.amount, + owner: self.owner, + response_destination: self.owner + }.toCell() + }); + } + + bounced(msg: Slice) { + let op: Int = msg.loadUint(32); + let queryId: Int = msg.loadUint(64); + let jettonAmount: Int = msg.loadCoins(); + require(op == 0x178d4519 || op == 0x7bdd97de, "Invalid bounced message"); + + self.balance = self.balance + jettonAmount; // Update balance + } + + get fun get_wallet_data(): JettonWalletData { + return JettonWalletData{ + balance: self.balance, + owner: self.owner, + master: self.master, + walletCode: (initOf JettonDefaultWallet(self.master, self.owner)).code + }; + } +} + +struct JettonWalletData { + balance: Int; + owner: Address; + master: Address; + walletCode: Cell; +}