Intelligent Contracts
Deploy Scripts

Deploy Scripts

Deploy scripts automate multi-contract deployments, constructor configuration, and follow-up writes. The CLI loads TypeScript or JavaScript files from deploy/ in filename order and passes a configured GenLayerJS client to each script.

File order

your-project/
β”œβ”€β”€ deploy/
β”‚   β”œβ”€β”€ 001_deploy_main_contract.ts
β”‚   β”œβ”€β”€ 002_deploy_helper_contract.ts
β”‚   └── 003_configure_contracts.ts
β”œβ”€β”€ contracts/
└── fee-profile.json

Use numeric prefixes because scripts execute alphabetically. A later script should not assume an earlier transaction merely returned an ID; the earlier script must wait for and verify its required lifecycle outcome.

Fee-aware deploy helper

On a fee-charging deployment, raw deployContract and writeContract calls need the estimate produced from the matching profile entry. This helper converts a JSON profile entry into live estimate options:

import type { GenLayerClient } from 'genlayer-js/types';
 
type ProfileEntry = {
  leaderTimeunitsAllocation: string;
  validatorTimeunitsAllocation: string;
  executionBudgetPerRound: string;
  totalMessageFees?: string;
  rotationsPerRound?: string;
};
 
async function quoteProfile(
  client: GenLayerClient<any>,
  entry: ProfileEntry,
  appealRounds = 1n,
) {
  const rotationsPerRound = BigInt(entry.rotationsPerRound ?? '0');
  return client.estimateTransactionFees({
    leaderTimeunitsAllocation: BigInt(entry.leaderTimeunitsAllocation),
    validatorTimeunitsAllocation: BigInt(entry.validatorTimeunitsAllocation),
    executionBudgetPerRound: BigInt(entry.executionBudgetPerRound),
    totalMessageFees: BigInt(entry.totalMessageFees ?? '0'),
    appealRounds,
    rotations: Array.from(
      { length: Number(appealRounds) + 1 },
      () => rotationsPerRound,
    ),
  });
}

The committed profile supplies measured quantities; estimateTransactionFees reads current network prices and ceilings. See Fee Profiling and Estimation.

Basic deploy script

import { readFileSync } from 'node:fs';
import { isSuccessful } from 'genlayer-js';
import type { DecodedDeployData, GenLayerClient } from 'genlayer-js/types';
import feeProfile from '../fee-profile.json';
 
export default async function main(client: GenLayerClient<any>) {
  const code = new Uint8Array(
    readFileSync(new URL('../contracts/my_contract.py', import.meta.url)),
  );
  const estimate = await quoteProfile(client, feeProfile.deploy);
 
  const txId = await client.deployContract({
    code,
    args: [],
    fees: {
      distribution: estimate.distribution,
      feeValue: estimate.feeValue,
    },
  });
 
  const transaction = await client.waitForFinalization({ hash: txId });
  if (!isSuccessful(transaction)) {
    throw new Error(
      `Deployment failed: ${transaction.statusName} / ${transaction.txExecutionResultName}`,
    );
  }
 
  const decoded = transaction.txDataDecoded as DecodedDeployData | undefined;
  const contractAddress = decoded?.contractAddress ?? transaction.recipient;
  if (!contractAddress) throw new Error('Finalized deployment has no contract address');
 
  console.log('Contract deployed', { txId, contractAddress });
  return contractAddress;
}

initializeConsensusSmartContract() is deprecated on the v2 client. The consensus deployment comes from the selected chain definition; deploy scripts should not initialize or reset it.

Multi-step configuration

Use the same rule for follow-up writes: select the method profile, estimate against the live policy, submit, and verify the execution result.

async function configureContract(
  client: GenLayerClient<any>,
  mainAddress: `0x${string}`,
  helperAddress: `0x${string}`,
) {
  const args = [helperAddress];
  const estimate = await quoteProfile(
    client,
    feeProfile.methods.setHelperContract,
  );
 
  const txId = await client.writeContract({
    address: mainAddress,
    functionName: 'setHelperContract',
    args,
    fees: {
      distribution: estimate.distribution,
      feeValue: estimate.feeValue,
    },
  });
 
  const transaction = await client.waitForFinalization({ hash: txId });
  if (!isSuccessful(transaction)) {
    throw new Error(
      `Configuration failed: ${transaction.statusName} / ${transaction.txExecutionResultName}`,
    );
  }
}

Keep all addresses and transaction IDs in the deployment output or a deployment manifest. If a process stops after submission, resume tracking the recorded transaction ID instead of blindly sending the same deploy or write again.

Run scripts

Select and verify the network before executing the directory:

genlayer network set studio-dev
genlayer network info
genlayer deploy

For stable Studio use studionet; for a local stack use localnet. Studio-dev requires the matching release-candidate CLI and SDK. See Network Configuration.

Related