Browse Source

Support func 0.2.0 (consts, pragma version, includes) (#4)

* tests pass

* update gitignore

* removed console.log

* consts

* restore prettier + compiled file name
master
Shahar Yakir 2 years ago committed by GitHub
parent
commit
31beaeea51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      build/.gitignore
  2. 83
      build/_build.ts
  3. 16
      contracts/imports/constants.fc
  4. 24
      contracts/main.fc
  5. 67
      package-lock.json
  6. 4
      package.json
  7. 5
      test/counter.spec.ts
  8. 5
      test/deposit.spec.ts
  9. 5
      test/ownership.spec.ts
  10. 5
      tsconfig.json

3
build/.gitignore vendored

@ -1,3 +1,4 @@
*.fif
*.fc
*.cell
*.cell
*-bitcode.json

83
build/_build.ts

@ -11,10 +11,12 @@ import path from "path";
import process from "process";
import child_process from "child_process";
import glob from "fast-glob";
import { Cell } from "ton";
import semver from "semver";
async function main() {
console.log(`=================================================================`);
console.log(`Build script running, let's find some FunC contracts to compile..`);
console.log("=================================================================");
console.log("Build script running, let's find some FunC contracts to compile..");
// if we have an explicit bin directory, use the executables there (needed for glitch.com)
if (fs.existsSync("bin")) {
@ -23,12 +25,15 @@ async function main() {
}
// make sure func compiler is available
let funcVersion = "";
const minSupportFunc = "0.2.0";
try {
funcVersion = child_process.execSync("func -V").toString();
} catch (e) {}
if (!funcVersion.includes(`Func build information`)) {
console.log(`\nFATAL ERROR: 'func' executable is not found, is it installed and in path?`);
const funcVersion = child_process
.execSync("func -V")
.toString()
.match(/semantic version: v([0-9.]+)/)?.[1];
if (!semver.gte(semver.coerce(funcVersion) ?? "", minSupportFunc)) throw new Error("Nonexistent version or outdated");
} catch (e) {
console.log(`\nFATAL ERROR: 'func' with version >= ${minSupportFunc} executable is not found, is it installed and in path?`);
process.exit(1);
}
@ -37,8 +42,8 @@ async function main() {
try {
fiftVersion = child_process.execSync("fift -V").toString();
} catch (e) {}
if (!fiftVersion.includes(`Fift build information`)) {
console.log(`\nFATAL ERROR: 'fift' executable is not found, is it installed and in path?`);
if (!fiftVersion.includes("Fift build information")) {
console.log("\nFATAL ERROR: 'fift' executable is not found, is it installed and in path?");
process.exit(1);
}
@ -70,6 +75,11 @@ async function main() {
console.log(` - Deleting old build artifact '${cellArtifact}'`);
fs.unlinkSync(cellArtifact);
}
const hexArtifact = `build/${contractName}.compiled.json`;
if (fs.existsSync(hexArtifact)) {
console.log(` - Deleting old build artifact '${hexArtifact}'`);
fs.unlinkSync(hexArtifact);
}
// check if we have a tlb file
const tlbFile = `contracts/${contractName}.tlb`;
@ -87,27 +97,20 @@ async function main() {
console.log(` - Warning: TL-B file for contract '${tlbFile}' not found, are your op consts according to standard?`);
}
// create a merged fc file with source code from all dependencies
let sourceToCompile = "";
const importFiles = glob.sync([`contracts/imports/*.fc`, `contracts/imports/*.func`, `contracts/imports/${contractName}/*.fc`, `contracts/imports/${contractName}/*.func`]);
for (const importFile of importFiles) {
console.log(` - Adding import '${importFile}'`);
sourceToCompile += `${fs.readFileSync(importFile).toString()}\n`;
}
console.log(` - Adding the contract itself '${rootContract}'`);
sourceToCompile += `${fs.readFileSync(rootContract).toString()}\n`;
fs.writeFileSync(mergedFuncArtifact, sourceToCompile);
console.log(` - Build artifact created '${mergedFuncArtifact}'`);
// run the func compiler to create a fif file
console.log(` - Trying to compile '${mergedFuncArtifact}' with 'func' compiler..`);
const buildErrors = child_process.execSync(`func -APS -o build/${contractName}.fif ${mergedFuncArtifact} 2>&1 1>node_modules/.tmpfunc`).toString();
console.log(` - Trying to compile '${rootContract}' with 'func' compiler..`);
let buildErrors: string;
try {
buildErrors = child_process.execSync(`func -APS -o build/${contractName}.fif ${rootContract} 2>&1 1>node_modules/.tmpfunc`).toString();
} catch (e) {
buildErrors = e.stdout.toString();
}
if (buildErrors.length > 0) {
console.log(` - OH NO! Compilation Errors! The compiler output was:`);
console.log(" - OH NO! Compilation Errors! The compiler output was:");
console.log(`\n${buildErrors}`);
process.exit(1);
} else {
console.log(` - Compilation successful!`);
console.log(" - Compilation successful!");
}
// make sure fif build artifact was created
@ -119,7 +122,7 @@ async function main() {
}
// create a temp cell.fif that will generate the cell
let fiftCellSource = `"Asm.fif" include\n`;
let fiftCellSource = '"Asm.fif" include\n';
fiftCellSource += `${fs.readFileSync(fiftArtifact).toString()}\n`;
fiftCellSource += `boc>B "${cellArtifact}" B>file`;
fs.writeFileSync(fiftCellArtifact, fiftCellSource);
@ -128,21 +131,41 @@ async function main() {
try {
child_process.execSync(`fift ${fiftCellArtifact}`);
} catch (e) {
console.log(`FATAL ERROR: 'fift' executable failed, is FIFTPATH env variable defined?`);
console.log("FATAL ERROR: 'fift' executable failed, is FIFTPATH env variable defined?");
process.exit(1);
}
// Remove intermediary
fs.unlinkSync(fiftCellArtifact);
// make sure cell build artifact was created
if (!fs.existsSync(cellArtifact)) {
console.log(` - For some reason '${cellArtifact}' was not created!`);
process.exit(1);
} else {
console.log(` - Build artifact created '${cellArtifact}'`);
fs.unlinkSync(fiftCellArtifact);
}
fs.writeFileSync(
hexArtifact,
JSON.stringify({
hex: Cell.fromBoc(fs.readFileSync(cellArtifact))[0].toBoc().toString("hex"),
})
);
// Remove intermediary
fs.unlinkSync(cellArtifact);
// make sure hex artifact was created
if (!fs.existsSync(hexArtifact)) {
console.log(` - For some reason '${hexArtifact}' was not created!`);
process.exit(1);
} else {
console.log(` - Build artifact created '${hexArtifact}'`);
}
}
console.log(``);
console.log("");
}
main();
@ -152,7 +175,7 @@ main();
function crc32(r: string) {
for (var a, o = [], c = 0; c < 256; c++) {
a = c;
for (var f = 0; f < 8; f++) a = 1 & a ? 3988292384 ^ (a >>> 1) : a >>> 1;
for (let f = 0; f < 8; f++) a = 1 & a ? 3988292384 ^ (a >>> 1) : a >>> 1;
o[c] = a;
}
for (var n = -1, t = 0; t < r.length; t++) n = (n >>> 8) ^ o[255 & (n ^ r.charCodeAt(t))];

16
contracts/imports/constants.fc

@ -1,13 +1,13 @@
;; operations (constant values taken from crc32 on op message in the companion .tlb files and appear during build)
int op::increment() asm "0x37491f2f PUSHINT";
int op::deposit() asm "0x47d54391 PUSHINT";
int op::withdraw() asm "0x41836980 PUSHINT";
int op::transfer_ownership() asm "0x2da38aaf PUSHINT";
const op::increment = 0x37491f2f;
const op::deposit = 0x47d54391;
const op::withdraw = 0x41836980;
const op::transfer_ownership = 0x2da38aaf;
;; errors
int error::unknown_op() asm "101 PUSHINT";
int error::access_denied() asm "102 PUSHINT";
int error::insufficient_balance() asm "103 PUSHINT";
const error::unknown_op = 101;
const error::access_denied = 102;
const error::insufficient_balance = 103;
;; other
int const::min_tons_for_storage() asm "10000000 PUSHINT"; ;; 0.01 TON
const const::min_tons_for_storage = 10000000; ;; 0.01 TON

24
contracts/main.fc

@ -2,6 +2,12 @@
;; storage binary format is defined as TL-B in companion .tlb file
#pragma version >=0.2.0;
#include "imports/stdlib.fc";
#include "imports/constants.fc";
#include "imports/utils.fc";
(slice, int) load_data() inline {
var ds = get_data().begin_parse();
return (
@ -41,38 +47,38 @@
int op = in_msg_body~load_uint(32);
int query_id = in_msg_body~load_uint(64);
if (op == op::increment()) {
if (op == op::increment) {
save_data(owner_address, counter + 1);
return ();
}
if (op == op::deposit()) {
if (op == op::deposit) {
;; empty since ton received (msg_value) is added automatically to contract balance
;; ~dump msg_value; ;; an example of debug output, requires running contract in debug mode
return ();
}
if (op == op::withdraw()) {
throw_unless(error::access_denied(), equal_slices(sender_address, owner_address));
if (op == op::withdraw) {
throw_unless(error::access_denied, equal_slices(sender_address, owner_address));
int withdraw_amount = in_msg_body~load_coins();
op_withdraw(withdraw_amount, owner_address);
return ();
}
if (op == op::transfer_ownership()) {
throw_unless(error::access_denied(), equal_slices(sender_address, owner_address));
if (op == op::transfer_ownership) {
throw_unless(error::access_denied, equal_slices(sender_address, owner_address));
slice new_owner_address = in_msg_body~load_msg_addr();
save_data(new_owner_address, counter);
return ();
}
throw(error::unknown_op());
throw(error::unknown_op);
}
() op_withdraw(int withdraw_amount, slice owner_address) impure {
var [balance, _] = get_balance();
throw_unless(error::insufficient_balance(), balance >= withdraw_amount);
int return_value = min(withdraw_amount, balance - const::min_tons_for_storage());
throw_unless(error::insufficient_balance, balance >= withdraw_amount);
int return_value = min(withdraw_amount, balance - const::min_tons_for_storage);
send_grams(owner_address, return_value);
}

67
package-lock.json generated

@ -9,11 +9,15 @@
"version": "0.0.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"semver": "^7.3.7"
},
"devDependencies": {
"@swc/core": "^1.2.177",
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"@types/semver": "^7.3.9",
"axios-request-throttle": "^1.0.0",
"chai": "^4.3.4",
"chai-bn": "^0.3.1",
@ -403,6 +407,12 @@
"integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==",
"dev": true
},
"node_modules/@types/semver": {
"version": "7.3.9",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
"integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==",
"dev": true
},
"node_modules/@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
@ -1215,6 +1225,17 @@
"get-func-name": "^2.0.0"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -1568,6 +1589,20 @@
}
]
},
"node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/serialize-javascript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
@ -1900,6 +1935,11 @@
"node": ">=10"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@ -2192,6 +2232,12 @@
"integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==",
"dev": true
},
"@types/semver": {
"version": "7.3.9",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz",
"integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==",
"dev": true
},
"@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
@ -2787,6 +2833,14 @@
"get-func-name": "^2.0.0"
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -3015,6 +3069,14 @@
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
},
"serialize-javascript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
@ -3270,6 +3332,11 @@
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",

4
package.json

@ -17,6 +17,7 @@
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"@types/semver": "^7.3.9",
"axios-request-throttle": "^1.0.0",
"chai": "^4.3.4",
"chai-bn": "^0.3.1",
@ -43,5 +44,8 @@
},
"engines": {
"node": ">=16.0.0"
},
"dependencies": {
"semver": "^7.3.7"
}
}

5
test/counter.spec.ts

@ -3,18 +3,19 @@ import chaiBN from "chai-bn";
import BN from "bn.js";
chai.use(chaiBN(BN));
import * as fs from "fs";
import { Cell } from "ton";
import { SmartContract } from "ton-contract-executor";
import * as main from "../contracts/main";
import { internalMessage, randomAddress } from "./helpers";
import { hex } from "../build/main-bitcode.json";
describe("Counter tests", () => {
let contract: SmartContract;
beforeEach(async () => {
contract = await SmartContract.fromCell(
Cell.fromBoc(fs.readFileSync("build/main.cell"))[0], // code cell from build output
Cell.fromBoc(hex)[0], // code cell from build output
main.data({
ownerAddress: randomAddress("owner"),
counter: 17,

5
test/deposit.spec.ts

@ -3,18 +3,19 @@ import chaiBN from "chai-bn";
import BN from "bn.js";
chai.use(chaiBN(BN));
import * as fs from "fs";
import { Cell, toNano } from "ton";
import { SmartContract } from "ton-contract-executor";
import * as main from "../contracts/main";
import { internalMessage, randomAddress, setBalance } from "./helpers";
import { hex } from "../build/main-bitcode.json";
describe("Deposit and withdraw tests", () => {
let contract: SmartContract;
beforeEach(async () => {
contract = await SmartContract.fromCell(
Cell.fromBoc(fs.readFileSync("build/main.cell"))[0], // code cell from build output
Cell.fromBoc(hex)[0], // code cell from build output
main.data({
ownerAddress: randomAddress("owner"),
counter: 17,

5
test/ownership.spec.ts

@ -3,18 +3,19 @@ import chaiBN from "chai-bn";
import BN from "bn.js";
chai.use(chaiBN(BN));
import * as fs from "fs";
import { Cell, Slice } from "ton";
import { SmartContract } from "ton-contract-executor";
import * as main from "../contracts/main";
import { internalMessage, randomAddress } from "./helpers";
import { hex } from "../build/main-bitcode.json";
describe("Transfer ownership tests", () => {
let contract: SmartContract;
beforeEach(async () => {
contract = await SmartContract.fromCell(
Cell.fromBoc(fs.readFileSync("build/main.cell"))[0], // code cell from build output
Cell.fromBoc(hex)[0], // code cell from build output
main.data({
ownerAddress: randomAddress("owner"),
counter: 17,

5
tsconfig.json

@ -5,10 +5,11 @@
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
"skipLibCheck": true,
"resolveJsonModule": true
},
"ts-node": {
"transpileOnly": true,
"transpiler": "ts-node/transpilers/swc"
}
}
}

Loading…
Cancel
Save