Skip to main content

Transactions

Learn how to create, sign, and submit transactions to GLIN Network.

Overview

Transactions are state-changing operations that:

  • 💸 Transfer tokens between accounts
  • 📝 Execute contract methods
  • 🗳️ Participate in governance
  • ⚙️ Change system state

All transactions require gas fees paid in GLIN tokens.

Transaction Lifecycle

1. Create → 2. Sign → 3. Submit → 4. In Block → 5. Finalized

1. Create Transaction

import { GlinClient } from '@glin-ai/sdk';

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

// Create a transfer transaction
const tx = client.api.tx.balances.transfer(
recipientAddress,
1000n * 10n ** 18n // 1000 GLIN
);

2. Sign Transaction

import { Keyring } from '@glin-ai/sdk';

const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice');

// Sign the transaction
const signedTx = tx.sign(sender);

3. Submit Transaction

// Sign and submit in one step
const hash = await tx.signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

4. Wait for Finalization

await new Promise((resolve, reject) => {
tx.signAndSend(sender, ({ status, events, dispatchError }) => {
if (status.isInBlock) {
console.log(`In block: ${status.asInBlock.toHex()}`);
}

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

if (dispatchError) {
reject(new Error('Transaction failed'));
} else {
resolve(status.asFinalized);
}
}
});
});

Common Transactions

Transfer GLIN

const transfer = client.api.tx.balances.transfer(
recipientAddress,
100n * 10n ** 18n // 100 GLIN
);

await transfer.signAndSend(sender);

Batch Transactions

Execute multiple transactions atomically:

const tx1 = client.api.tx.balances.transfer(alice.address, 100n);
const tx2 = client.api.tx.balances.transfer(bob.address, 200n);
const tx3 = client.api.tx.balances.transfer(charlie.address, 300n);

// Batch all transactions
const batch = client.api.tx.utility.batch([tx1, tx2, tx3]);

await batch.signAndSend(sender);

Force Batch (Continue on Error)

// batchAll: fails if any transaction fails
const batchAll = client.api.tx.utility.batchAll([tx1, tx2, tx3]);

// forceBatch: continues even if some transactions fail
const forceBatch = client.api.tx.utility.forceBatch([tx1, tx2, tx3]);

Transaction Fees

Estimate Fees

const info = await tx.paymentInfo(sender);

console.log('Estimated fee:', info.partialFee.toString());

Add Tip

Prioritize your transaction by adding a tip:

const tip = 10n ** 16n; // 0.01 GLIN

await tx.signAndSend(sender, { tip });

Transaction Status

Track transaction status:

tx.signAndSend(sender, ({ status, events }) => {
if (status.isReady) {
console.log('🔄 Ready to be included');
}

if (status.isBroadcast) {
console.log('📡 Broadcast to network');
}

if (status.isInBlock) {
console.log('📦 Included in block');
}

if (status.isFinalized) {
console.log('✅ Finalized');

// Process events
events.forEach(({ event }) => {
console.log('Event:', event.toHuman());
});
}

if (status.isInvalid) {
console.log('❌ Invalid transaction');
}

if (status.isDropped) {
console.log('⚠️ Dropped from pool');
}
});

Error Handling

try {
await new Promise((resolve, reject) => {
tx.signAndSend(sender, ({ status, dispatchError }) => {
if (status.isFinalized) {
if (dispatchError) {
if (dispatchError.isModule) {
const decoded = client.api.registry.findMetaError(
dispatchError.asModule
);
reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs}`));
} else {
reject(new Error(dispatchError.toString()));
}
} else {
resolve(status.asFinalized);
}
}
});
});
} catch (error) {
console.error('Transaction failed:', error.message);
}

Best Practices

1. Always Wait for Finalization

// ❌ Bad - don't assume success
const hash = await tx.signAndSend(sender);
// Transaction might still fail!

// ✅ Good - wait for finalization
await new Promise((resolve) => {
tx.signAndSend(sender, ({ status }) => {
if (status.isFinalized) resolve();
});
});

2. Check Balance Before Transfer

const balance = await client.getBalance(sender.address);
const amount = 1000n * 10n ** 18n;

if (balance.free < amount) {
throw new Error('Insufficient balance');
}

3. Handle Nonce Manually (Advanced)

// Get next nonce
const nonce = await client.api.rpc.system.accountNextIndex(sender.address);

// Use specific nonce
await tx.signAndSend(sender, { nonce });

4. Set Mortality Period

// Transaction expires after 64 blocks
await tx.signAndSend(sender, { era: 64 });

Transaction Events

Process events emitted by transactions:

tx.signAndSend(sender, ({ status, events }) => {
if (status.isFinalized) {
events.forEach(({ event }) => {
if (client.api.events.system.ExtrinsicSuccess.is(event)) {
console.log('✅ Transaction succeeded');
}

if (client.api.events.system.ExtrinsicFailed.is(event)) {
console.log('❌ Transaction failed');
}

if (client.api.events.balances.Transfer.is(event)) {
const [from, to, amount] = event.data;
console.log(`💸 Transfer: ${from}${to}: ${amount}`);
}
});
}
});

Next Steps


Need help? Join our Discord