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