From 5f4bbc7454b21f3ee92e0f3cb9ef8cf7789ecb24 Mon Sep 17 00:00:00 2001 From: Tal Kol Date: Tue, 3 May 2022 19:15:10 +0100 Subject: [PATCH] Improved deploy script --- build/deploy.ts | 67 ++++++++++++++++++++++++++++++-------------- build/main.deploy.ts | 5 ++++ package-lock.json | 55 ++++++++++++++++++++++++++++++++++++ package.json | 4 ++- 4 files changed, 109 insertions(+), 22 deletions(-) diff --git a/build/deploy.ts b/build/deploy.ts index cfcf5b8..2bb732a 100644 --- a/build/deploy.ts +++ b/build/deploy.ts @@ -4,17 +4,27 @@ // ./build/ - directory for build artifacts (mycontract.cell) and deploy init data scripts (mycontract.deploy.ts) // ./build/deploy.config.json - JSON config file with secret mnemonic of deploying wallet (will be created if not found) +import axios from "axios"; +import axiosThrottle from "axios-request-throttle"; +axiosThrottle.use(axios, { requestsPerSecond: 0.5 }); // required since toncenter jsonRPC limits to 1 req/sec without API key + import fs from "fs"; import path from "path"; import glob from "fast-glob"; -import { Address, Cell, CommonMessageInfo, contractAddress, fromNano, InternalMessage, SendMode, StateInit, toNano, TonClient, WalletContract, WalletV3R2Source } from "ton"; +import { Cell, CellMessage, CommonMessageInfo, contractAddress, fromNano, InternalMessage, SendMode, StateInit, toNano, TonClient, WalletContract, WalletV3R2Source } from "ton"; import { mnemonicNew, mnemonicToWalletKey } from "ton-crypto"; -import { BN } from "bn.js"; async function main() { console.log(`=================================================================`); console.log(`Deploy script running, let's find some contracts to deploy..`); + // check some global settings + if (process.env.TESTNET) { + console.log(`\n* We are deploying to 'testnet' (https://t.me/testgiver_ton_bot will give you test TON)`); + } else { + console.log(`\n* We are deploying to 'mainnet'`); + } + // make sure we have a wallet mnemonic to deploy from (or create one if not found) const deployConfigJson = `build/deploy.config.json`; let deployerMnemonic; @@ -35,7 +45,7 @@ async function main() { } // open the wallet and make sure it has enough TON - const client = new TonClient({ endpoint: "https://toncenter.com/api/v2/jsonRPC" }); + const client = new TonClient({ endpoint: `https://${process.env.TESTNET ? "testnet." : ""}toncenter.com/api/v2/jsonRPC` }); const walletKey = await mnemonicToWalletKey(deployerMnemonic.split(" ")); const walletContract = WalletContract.create(client, WalletV3R2Source.create({ publicKey: walletKey.publicKey, workchain: 0 })); console.log(` - Wallet address used for deployment is: ${walletContract.address.toFriendly()}`); @@ -60,6 +70,13 @@ async function main() { } const initDataCell = deployInit.initData() as Cell; + // prepare the init message + if (typeof deployInit.initMessage !== "function") { + console.log(` - ERROR: '${rootContract}' does not have 'initMessage()' function`); + process.exit(1); + } + const initMessageCell = deployInit.initMessage() as Cell | null; + // prepare the init code cell const cellArtifact = `build/${contractName}.cell`; if (!fs.existsSync(cellArtifact)) { @@ -68,25 +85,42 @@ async function main() { } const initCodeCell = Cell.fromBoc(fs.readFileSync(cellArtifact))[0]; - // deploy by sending an internal message to the deploying wallet - sleep(1000); // to make sure we don't fail due to throttling + // make sure the contract was not already deployed const newContractAddress = contractAddress({ workchain: 0, initialData: initDataCell, initialCode: initCodeCell }); - console.log(` - About to deploy contract to new address: ${newContractAddress.toFriendly()}`); - const seqno = await getSeqNo(client, walletContract.address); - sleep(1000); // to make sure we don't fail due to throttling - const transfer = await walletContract.createTransfer({ + console.log(` - Based on your init code+data, your new contract address is: ${newContractAddress.toFriendly()}`); + if (await client.isContractDeployed(newContractAddress)) { + console.log(` - Looks like the contract is already deployed in this address, skipping`); + continue; + } + + // deploy by sending an internal message to the deploying wallet + console.log(` - Let's deploy the contract on-chain..`); + const seqno = await walletContract.getSeqNo(); + const transfer = walletContract.createTransfer({ secretKey: walletKey.secretKey, seqno: seqno, sendMode: SendMode.PAY_GAS_SEPARATLY + SendMode.IGNORE_ERRORS, order: new InternalMessage({ to: newContractAddress, - value: new BN(0.5), + value: toNano(0.1), bounce: false, - body: new CommonMessageInfo({ stateInit: new StateInit({ data: initDataCell, code: initCodeCell }) }), + body: new CommonMessageInfo({ + stateInit: new StateInit({ data: initDataCell, code: initCodeCell }), + body: initMessageCell !== null ? new CellMessage(initMessageCell) : null, + }), }), }); await client.sendExternalMessage(walletContract, transfer); - console.log(` - Contract deployed successfully!`); + console.log(` - Deploy transaction sent successfully`); + + // make sure that the contract was deployed + console.log(` - Waiting 5 seconds to check if the contract was actually deployed..`); + await sleep(5000); + if (await client.isContractDeployed(newContractAddress)) { + console.log(` - SUCCESS! Contract deployed successfully to address: ${newContractAddress.toFriendly()}`); + } else { + console.log(` - FAILURE! Contract address still looks uninitialized: ${newContractAddress.toFriendly()}`); + } } console.log(``); @@ -99,12 +133,3 @@ main(); function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } - -async function getSeqNo(client: TonClient, walletAddress: Address) { - if (await client.isContractDeployed(walletAddress)) { - let res = await client.callGetMethod(walletAddress, "seqno"); - return parseInt(res.stack[0][1], 16); - } else { - return 0; - } -} diff --git a/build/main.deploy.ts b/build/main.deploy.ts index 79aa49e..05a6497 100644 --- a/build/main.deploy.ts +++ b/build/main.deploy.ts @@ -8,3 +8,8 @@ export function initData() { counter: 0, }); } + +// return the op that should be sent to the contract on deployment, can be "null" so send an empty message +export function initMessage() { + return main.increment(); +} diff --git a/package-lock.json b/package-lock.json index c120fa3..aa3e891 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@types/bn.js": "^5.1.0", "@types/chai": "^4.3.0", "@types/mocha": "^9.0.0", + "axios-request-throttle": "^1.0.0", "chai": "^4.3.4", "chai-bn": "^0.3.1", "fast-glob": "^3.2.11", @@ -112,6 +113,16 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "node_modules/@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=", + "deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!", + "dev": true, + "dependencies": { + "axios": "*" + } + }, "node_modules/@types/bn.js": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", @@ -242,6 +253,19 @@ "follow-redirects": "^1.14.7" } }, + "node_modules/axios-request-throttle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/axios-request-throttle/-/axios-request-throttle-1.0.0.tgz", + "integrity": "sha512-NYh7kZkgSJZyIohqrvQEzr4uygqbxXb769kbphkwgYNAJm5eDg33mqHLA2pwHKC1uqbTfNpmjmzWdUdo+ptBWg==", + "dev": true, + "dependencies": { + "@types/axios": "^0.14.0", + "promise-throttle": "^1.1.2" + }, + "peerDependencies": { + "axios": "*" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1158,6 +1182,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/promise-throttle": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/promise-throttle/-/promise-throttle-1.1.2.tgz", + "integrity": "sha512-dij7vjyXNewuuN/gyr+TX2KRjw48mbV5FEtgyXaIoJjGYAKT0au23/voNvy9eS4UNJjx2KUdEcO5Yyfc1h7vWQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1744,6 +1774,15 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=", + "dev": true, + "requires": { + "axios": "*" + } + }, "@types/bn.js": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", @@ -1847,6 +1886,16 @@ "follow-redirects": "^1.14.7" } }, + "axios-request-throttle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/axios-request-throttle/-/axios-request-throttle-1.0.0.tgz", + "integrity": "sha512-NYh7kZkgSJZyIohqrvQEzr4uygqbxXb769kbphkwgYNAJm5eDg33mqHLA2pwHKC1uqbTfNpmjmzWdUdo+ptBWg==", + "dev": true, + "requires": { + "@types/axios": "^0.14.0", + "promise-throttle": "^1.1.2" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2512,6 +2561,12 @@ "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", "dev": true }, + "promise-throttle": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/promise-throttle/-/promise-throttle-1.1.2.tgz", + "integrity": "sha512-dij7vjyXNewuuN/gyr+TX2KRjw48mbV5FEtgyXaIoJjGYAKT0au23/voNvy9eS4UNJjx2KUdEcO5Yyfc1h7vWQ==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/package.json b/package.json index edfbc22..31ad7e0 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,14 @@ "prettier": "npx prettier --write '{test,contracts,build}/**/*.{ts,js,json}'", "test": "mocha --exit test/**/*.spec.ts", "build": "ts-node ./build/build.ts", - "deploy": "ts-node ./build/deploy.ts" + "deploy": "ts-node ./build/deploy.ts", + "deploy:testnet": "export TESTNET=1 && ts-node ./build/deploy.ts" }, "devDependencies": { "@types/bn.js": "^5.1.0", "@types/chai": "^4.3.0", "@types/mocha": "^9.0.0", + "axios-request-throttle": "^1.0.0", "chai": "^4.3.4", "chai-bn": "^0.3.1", "fast-glob": "^3.2.11",