Skip to main content

Deploying Contracts

Learn how to deploy ink! smart contracts to GLIN Network.

Prerequisites

Before deploying contracts, you need:

  • ✅ GLIN SDK installed (TypeScript or Rust)
  • ✅ An ink! contract compiled to .contract file
  • ✅ Account with sufficient balance for gas fees
  • ✅ Connection to GLIN Network
New to ink! contracts?

If you're new to ink! smart contracts, check out the ink! documentation to learn how to write and compile contracts.

What You'll Learn

  • 📦 How to deploy contracts to GLIN Network
  • ⛽ Gas estimation and optimization
  • 🔍 Verifying deployment success
  • 🐛 Troubleshooting common issues

Deploy Your First Contract

Step 1: Compile Your Contract

First, compile your ink! contract:

# Navigate to your contract directory
cd my-contract

# Build the contract
cargo contract build --release

This generates three files in target/ink/:

  • my_contract.contract - Contract bundle (use this for deployment)
  • my_contract.wasm - Raw WebAssembly
  • metadata.json - Contract metadata

Step 2: Deploy the Contract

deploy.ts
import { GlinClient, Keyring } from '@glin-ai/sdk';
import { ContractPromise } from '@polkadot/api-contract';
import fs from 'fs';

async function deployContract() {
// 1. Connect to network
const client = await GlinClient.connect('wss://testnet.glin.ai');

// 2. Load deployer account
const keyring = new Keyring({ type: 'sr25519' });
const deployer = keyring.addFromUri('//Alice'); // Use your seed phrase

// 3. Load contract files
const wasm = fs.readFileSync('./target/ink/my_contract.wasm');
const metadata = JSON.parse(
fs.readFileSync('./target/ink/metadata.json', 'utf8')
);

// 4. Estimate gas
const gasLimit = client.api.registry.createType('WeightV2', {
refTime: 3_000_000_000,
proofSize: 1_000_000,
});

// 5. Deploy contract
console.log('📤 Deploying contract...');

const contract = new ContractPromise(client.api, metadata, null);

const tx = contract.tx.new(
{ gasLimit, storageDepositLimit: null },
// Constructor arguments
'Hello from GLIN!'
);

// 6. Sign and send
const result = await new Promise((resolve, reject) => {
tx.signAndSend(deployer, (result) => {
if (result.status.isInBlock) {
console.log(`✅ In block: ${result.status.asInBlock.toHex()}`);
}

if (result.status.isFinalized) {
console.log(`✅ Finalized: ${result.status.asFinalized.toHex()}`);

// Find contract address
const contractAddress = result.contract?.address.toString();

if (contractAddress) {
console.log(`📍 Contract deployed at: ${contractAddress}`);
resolve(contractAddress);
} else {
reject(new Error('Contract address not found'));
}
}

if (result.isError) {
reject(new Error('Deployment failed'));
}
});
});

return result;
}

deployContract()
.then((address) => {
console.log('🎉 Deployment successful!');
console.log(`Contract address: ${address}`);
process.exit(0);
})
.catch((error) => {
console.error('❌ Deployment failed:', error);
process.exit(1);
});

Run it:

npx tsx deploy.ts

Expected output:

🔗 Connecting to GLIN Network...
📤 Deploying contract...
✅ In block: 0x1234...
✅ Finalized: 0x5678...
📍 Contract deployed at: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
🎉 Deployment successful!

Gas Estimation

Proper gas estimation prevents failed transactions and saves fees.

estimate-gas.ts
import { GlinClient } from '@glin-ai/sdk';
import { ContractPromise } from '@polkadot/api-contract';

async function estimateDeploymentGas() {
const client = await GlinClient.connect('wss://testnet.glin.ai');

// Load contract metadata
const metadata = require('./target/ink/metadata.json');
const contract = new ContractPromise(client.api, metadata, null);

// Dry run to estimate gas
const dryRun = await client.api.call.contractsApi.instantiate(
deployer.address,
0, // value
null, // gasLimit (null = estimate)
null, // storageDepositLimit
contract.abi.constructors[0].toU8a([]), // Constructor with args
wasm
);

if (dryRun.result.isOk) {
const gasRequired = dryRun.gasRequired;

console.log('Gas estimation:');
console.log('- Reference time:', gasRequired.refTime.toString());
console.log('- Proof size:', gasRequired.proofSize.toString());

// Add 10% buffer
const gasLimit = client.api.registry.createType('WeightV2', {
refTime: gasRequired.refTime.muln(1.1),
proofSize: gasRequired.proofSize.muln(1.1),
});

return gasLimit;
} else {
throw new Error('Gas estimation failed: ' + dryRun.result.asErr);
}
}
Gas Optimization Tips
  1. Always add a buffer - Add 10-20% to estimated gas to account for state changes
  2. Use dry-run first - Test deployment without committing to chain
  3. Optimize contract code - Smaller contracts use less gas
  4. Minimize storage - Storage operations are expensive

Deploy with Constructor Arguments

Most contracts require constructor arguments:

deploy-with-args.ts
import { GlinClient, Keyring } from '@glin-ai/sdk';
import { ContractPromise } from '@polkadot/api-contract';

async function deployTokenContract() {
const client = await GlinClient.connect('wss://testnet.glin.ai');
const keyring = new Keyring({ type: 'sr25519' });
const deployer = keyring.addFromUri('//Alice');

// Load contract
const metadata = require('./target/ink/erc20.json');
const contract = new ContractPromise(client.api, metadata, null);

// Deploy with constructor arguments
const initialSupply = 1_000_000;
const tokenName = 'MyToken';
const tokenSymbol = 'MTK';

const gasLimit = client.api.registry.createType('WeightV2', {
refTime: 3_000_000_000,
proofSize: 1_000_000,
});

const tx = contract.tx.new(
{ gasLimit, storageDepositLimit: null },
initialSupply,
tokenName,
tokenSymbol
);

console.log(`📤 Deploying ERC20 token: ${tokenName} (${tokenSymbol})`);
console.log(` Initial supply: ${initialSupply}`);

const address = await new Promise((resolve, reject) => {
tx.signAndSend(deployer, (result) => {
if (result.status.isFinalized) {
const contractAddress = result.contract?.address.toString();
if (contractAddress) {
resolve(contractAddress);
} else {
reject(new Error('Contract address not found'));
}
}
if (result.isError) {
reject(new Error('Deployment failed'));
}
});
});

console.log(`✅ Token deployed at: ${address}`);
return address;
}

Verify Deployment

After deploying, verify the contract is working:

verify.ts
import { GlinClient } from '@glin-ai/sdk';
import { ContractPromise } from '@polkadot/api-contract';

async function verifyContract(contractAddress: string) {
const client = await GlinClient.connect('wss://testnet.glin.ai');

// Load contract metadata
const metadata = require('./target/ink/metadata.json');

// Connect to deployed contract
const contract = new ContractPromise(client.api, metadata, contractAddress);

// Query contract state
const { output } = await contract.query.getMessage(
contractAddress,
{ gasLimit: -1 } // -1 = unlimited for queries
);

if (output?.toHuman()) {
console.log('✅ Contract is working!');
console.log('Message:', output.toHuman());
return true;
} else {
console.log('❌ Contract verification failed');
return false;
}
}

// Usage
verifyContract('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')
.then((success) => process.exit(success ? 0 : 1));

Storage Deposit

Contracts on GLIN require a storage deposit to prevent spam:

// Calculate required storage deposit
const storageDepositLimit = client.api.registry.createType(
'Balance',
1_000_000_000_000 // 1 GLIN
);

const tx = contract.tx.new(
{
gasLimit,
storageDepositLimit // Explicitly set deposit limit
},
...constructorArgs
);

Storage deposit is refunded when the contract is removed.

Storage Deposit Calculation

The storage deposit is based on the contract's state size. For most contracts:

  • Small contracts (< 1 KB): ~0.1 GLIN
  • Medium contracts (1-10 KB): ~0.5 GLIN
  • Large contracts (> 10 KB): ~1+ GLIN

Use storageDepositLimit: null to auto-calculate, or set explicit limit.

Error Handling

Handle deployment errors gracefully:

async function deployWithErrorHandling() {
try {
const client = await GlinClient.connect('wss://testnet.glin.ai');
const keyring = new Keyring({ type: 'sr25519' });
const deployer = keyring.addFromUri('//Alice');

// Check balance first
const balance = await client.getBalance(deployer.address);
const minBalance = 10n ** 18n; // 1 GLIN

if (balance.free < minBalance) {
throw new Error(
`Insufficient balance. Need at least 1 GLIN, have ${balance.free}`
);
}

// Deploy contract
// ... deployment code ...

} catch (error) {
if (error.message.includes('Module')) {
console.error('❌ Contract execution error:', error.message);
} else if (error.message.includes('balance')) {
console.error('❌ Insufficient balance for deployment');
} else if (error.message.includes('gas')) {
console.error('❌ Gas estimation failed - contract too complex');
} else {
console.error('❌ Deployment failed:', error.message);
}

throw error;
}
}

Deployment Checklist

Before deploying to production:

  • ✅ Contract compiled with --release flag
  • ✅ Contract tested thoroughly (unit + integration tests)
  • ✅ Gas estimated and optimized
  • ✅ Constructor arguments validated
  • ✅ Storage deposit calculated
  • ✅ Deployer account has sufficient balance
  • ✅ Network endpoint is correct (testnet vs mainnet)
  • ✅ Deployment script has error handling
  • ✅ Contract address saved for future reference
  • ✅ Deployment verified with test query

Deployment Scripts

Complete TypeScript Deployment Script

scripts/deploy.ts
import { GlinClient, Keyring } from '@glin-ai/sdk';
import { ContractPromise } from '@polkadot/api-contract';
import fs from 'fs';
import path from 'path';

interface DeploymentConfig {
network: 'local' | 'testnet' | 'mainnet';
contractPath: string;
constructorArgs: any[];
deployerSeed: string;
}

async function deploy(config: DeploymentConfig) {
// Network endpoints
const endpoints = {
local: 'ws://localhost:9944',
testnet: 'wss://testnet.glin.ai',
mainnet: 'wss://rpc.glin.ai',
};

console.log(`🚀 Deploying to ${config.network}...`);

// 1. Connect
const client = await GlinClient.connect(endpoints[config.network]);
const keyring = new Keyring({ type: 'sr25519' });
const deployer = keyring.addFromUri(config.deployerSeed);

console.log(`👤 Deployer: ${deployer.address}`);

// 2. Check balance
const balance = await client.getBalance(deployer.address);
console.log(`💰 Balance: ${balance.free.toString()} GLIN`);

if (balance.free < 10n ** 18n) {
throw new Error('Insufficient balance (need at least 1 GLIN)');
}

// 3. Load contract
const wasm = fs.readFileSync(
path.join(config.contractPath, 'my_contract.wasm')
);
const metadata = JSON.parse(
fs.readFileSync(
path.join(config.contractPath, 'metadata.json'),
'utf8'
)
);

console.log(`📦 Contract size: ${wasm.length} bytes`);

// 4. Estimate gas
console.log('⛽ Estimating gas...');
const gasLimit = client.api.registry.createType('WeightV2', {
refTime: 3_000_000_000,
proofSize: 1_000_000,
});

// 5. Deploy
const contract = new ContractPromise(client.api, metadata, null);
const tx = contract.tx.new(
{ gasLimit, storageDepositLimit: null },
...config.constructorArgs
);

console.log('📤 Deploying contract...');

const contractAddress = await new Promise<string>((resolve, reject) => {
tx.signAndSend(deployer, (result) => {
if (result.status.isInBlock) {
console.log(`📦 In block: ${result.status.asInBlock.toHex()}`);
}

if (result.status.isFinalized) {
console.log(`✅ Finalized: ${result.status.asFinalized.toHex()}`);

const address = result.contract?.address.toString();
if (address) {
resolve(address);
} else {
reject(new Error('Contract address not found'));
}
}

if (result.isError) {
reject(new Error('Deployment failed'));
}
});
});

console.log(`\n🎉 Deployment successful!`);
console.log(`📍 Contract address: ${contractAddress}`);

// 6. Save deployment info
const deploymentInfo = {
network: config.network,
address: contractAddress,
deployer: deployer.address,
timestamp: new Date().toISOString(),
constructorArgs: config.constructorArgs,
};

fs.writeFileSync(
'deployment.json',
JSON.stringify(deploymentInfo, null, 2)
);

console.log('💾 Deployment info saved to deployment.json');

return contractAddress;
}

// Usage
deploy({
network: 'testnet',
contractPath: './target/ink',
constructorArgs: ['Hello from GLIN!'],
deployerSeed: process.env.DEPLOYER_SEED || '//Alice',
})
.then(() => process.exit(0))
.catch((error) => {
console.error('❌ Deployment failed:', error);
process.exit(1);
});

Troubleshooting

Deployment Failed: Module Error

Symptom: Module { index: 8, error: 5 }

Common causes:

  • Constructor panicked or returned an error
  • Invalid constructor arguments
  • Contract logic error

Solution: Check contract logs and ensure constructor succeeds

Out of Gas

Symptom: OutOfGas error

Solution: Increase gas limit:

const gasLimit = client.api.registry.createType('WeightV2', {
refTime: 10_000_000_000, // Increase this
proofSize: 2_000_000, // And this
});

Insufficient Balance

Symptom: InsufficientBalance error

Solution: Fund your account:

  • Testnet: Use the faucet at https://faucet.glin.ai
  • Mainnet: Transfer GLIN from another account

Contract Already Exists

Symptom: Contract code hash collision

Solution: This is very rare. If it happens, modify your contract slightly and recompile.

Next Steps

Now that you can deploy contracts:


Need help? Join our Discord or check the ink! documentation.