Developing an entire blockchain with NFT & FT deployment, baked into the source code.

Developing an entire blockchain with NFT & FT deployment, baked into the source code.

During our research into going as a native cryptocurrency, with an independent blockchain, we were looking at Ark. Ark allows a developer to create a blockchain and coin with their ecosystem, but due to the unstable nature of the crypto market and the fact that the Ark team, keeps changing things we decided that the path we were taking was wrong.

What is TypeScript?

TypeScript adds additional syntax to JavaScript to support a tighter integration with your editor. Catch errors early in your editor.

TypeScript code converts to JavaScript, which runs anywhere JavaScript runs: In a browser, on Node.js or Deno and in your apps.

TypeScript understands JavaScript and uses type inference to give you great tooling without additional code.

Crypto in TypeScript

The following Node modules were used:

  • nodemon for automatic reloading of project on every save (npm i nodemon –save-dev)

  • jest for unit testing (npm i jest –save-dev)

  • @types-jest (npm i @types/jest) for importing Jest symbols into TypeScript unit tests

  • typescript for writing code in TypeScript (npm i -g typescript)

  • tslint (npm i tslint -g)

  • crypto-js (cryptographic package for generating SHA-256 hashes) (npm i crypto-js –save)

  • @types/crypto-js (npm i @types/crypto-js –save) for importing crypto-js symbols into TypeScript

  • express as a HTTP web server (npm i express –save)

  • @types/express for importing express symbols into TypeScript (npm i @types/express –save-dev)

  • body-parser as middleware for parsing HTTP requests and receiving POST requests in JSON format (npm i body-parser –save)

  • ws, a Node.js web socket library (npm i ws –save)

  • @types/ws – type definitions for ws (npm i @types/ws –save)

  • elliptic – for using elliptic curve cryptography to generate public-private keypairs (npm i elliptic –save)

  • uuid – for generating unique transaction ID’s (npm i uuid –save)

  • @types/uuid – type definitions for uuid (npm i @types/uuid –save)

  • +FT & NFT deployement baked directly into MoltenChain

  • VolcanicWallet functions baked directly into MoltenChain

  • DirectStorage API baked directly into MoltenChain

  • MAGMAPay API baked directly into MoltenChain

TypeScript Configuration and Issues

TypeScript compiler options were set to be more strict to make the code more type safe. Specifically, the “noImplicitAny” was set to “true” to make the compiler force the use of explicit types throughout the code.

tsconfig.json used for this project.

{ "compilerOptions": { "strictNullChecks": true, "noImplicitAny": true, "moduleResolution": "node", "target": "es5", "lib": [ "es2015", "dom" ], "sourceMap": true }, "include": [ "src/**/*", "test/**/*", ], }

The compiler target in tsconfig.json was set to “es5”. When I tried to set it to “es6” I had many problems running Jest tests because Node does not support es6 imports and I was getting the runtime error: “SyntaxError: Unexpected token import” when importing a module like this:

import { Block } from "../../src/blockchain/block"

// So then I tried setting the compile target in tsconfig.json to “es5” but that would result in a compilation error in one of the ws (web sockets) type definition file (web sockets):

node_modules/@types/ws/index.d.ts:181:18 - error TS2304: Cannot find name 'Set'

// So I was stuck and tried to find a solution. I looked at using Babel to transpile from ES6 to ES5 as well as using

node --experimental-modules file.mjs

// as outlined in Stack Overflow question: Node.js – SyntaxError: Unexpected token import .

// I finally found a more simple solution on this Stack Overflow answer that talks about using the “lib” compiler option in tsconfig.json:

"lib": ["es2015", "dom"]

// which referenced the Angular TypeScript documentation that stated:

TypeScript includes a special declaration file called lib.d.ts. This file contains the ambient declarations for various common JavaScript constructs present in JavaScript runtimes and the DOM.

Based on the --target, TypeScript adds additional ambient declarations like Promise if the target is es6.

Since the QuickStart is targeting es5, you can override the list of declaration files to be included:

"lib": ["es2015", "dom"]
Thanks to that, you have all the es6 typings even when targeting es5.

Unit Testing with Jest

This blockchain was thoroughly unit tested using the Jest testing framework. All Jest unit tests are in the Github test folder and are appended with a “.test” to correspond with the file they are testing. For example, transaction.test.ts contains the unit tests for transaction.ts.

Jest needs to be configured to run in Node mode instead of its default “jsdom” or browser mode. The jest section in package.json, is:

"jest": {
"transform": {},
"testRegex": "/test/.*\.test\.(js)$",
"testEnvironment": "node"

Blockchain – Trustless System

A blockchain is a public and immutable shared ledger that stores all transactions for an application, such as a cryptocurrency like Bitcoin and Ethereum. For a cryptocurrency application, the ledger stores all transactions such as who transferred funds to who, similar to a bank ledger. Each set of transactions is represented as a block on the blockchain. A block can store multiple transactions, as shown in the demo video.

Once a transaction has been committed (mined) to the blockchain (ledger), it can’t be edited or removed. Also, all nodes on the blockchain network keep a replicated copy of the blockchain so there is no central place where the block chain is stored. When a new block is added to the blockchain, all nodes in the blockchain network are updated to have the newest version of the blockchain.

Every block in the chain is linked to the previous block by storing the hash value of the previous block, which creates a chain of blocks.

Instead of a central trusted authority like a bank, the blockchain network itself acts as the trusted authority because of its built in trust and security features. This eliminates the need for middle men such as banks and doesn’t have a single point of failure. An attacker would have to take over a large segment of a blockchain network in order to compromise it whereas a bank robber only needs to rob a single bank to steal a lot of money.

Block Class

The fundamental object of the blockchain is the block, which represents an individual link in the blockchain. The blockchain stores the following properties:

  • current hash (based on the timestamp, hash of previous block and transactions)

  • hash of the previous block

  • timestamp

  • data to store (cryptocurrency transactions but can store generic data, as well)

  • nonce value (for mining new blocks)

  • difficulty value (for mining new blocks)

The code for the Block class is in src/blockchain/block.ts. This snippet shows the above properties:

export default class Block {

timestamp: number;

lastHash: string;

hash: string;

//for cryptocurrency - transactions are the data in each block //but the blockchain can hold any data data: any; nonce: number;

difficulty: number;


Genesis Block

The Genesis Block is the very first block in a blockchain – a way to start a blockchain. Since there’s no preceding block, the genesis block uses hard coded dummy values for its previous hash value. This way, when the second block gets added to the blockchain, it will set the value of its previous hash to the hash of the genesis block. Since the genesis block is hard coded, we make it a static method of the Block class, so it’s accessible without instantiating a Block object.

The code for the genesis block is in src/blockchain/block.ts:

/** * First block of the blockchain. */ static getGenesisBlock(): Block { let genesisTx: Transaction = new Transaction(); = "genesis"; genesisTx.txInput = new TransactionInput(0, "-----"); return new Block(0, '-----', 'f1r5t-ha4h', genesisTx, 0, config.DIFFICULTY); }

Blockchain Class

The Blockchain class is responsible for

  • storing the list of blocks on the blockchain

  • adding new blocks to the chain

  • validating new blockschains

  • replacing current blockchain with a new one (when synchronizing with other nodes)

The code for the blockchain class is in src/blockchain/index.ts. The following code snippet shows the storage of an array of blocks within the Blockchain class, which illustrates that when a new blockchain is created, it comes pre-loaded with a single block – the genesis block:

export default class Blockchain { chain: Block []; constructor() { this.chain = [Block.getGenesisBlock()]; } }

Blockchain Replication

Every node on the network needs to have the same copy of the blockchain. So what happens when a new block gets mined on one node? Now the system has to replicate the new block to all the other nodes on the network so every node is in sync and has the same copy of the blockchain.

Since the blockchain needs to be replicated among all nodes in the network, the question arises – what happens if two nodes mine a block at the same time? This is when there’s a fork in the blockchain and the system needs to reconcile the fork and merge all the new blocks.

When a fork occurs, some nodes will get replicated from node A and some will get replicated from node B which will create the following scenario:

We will use the rule that the longest chain will be accepted as the main chain. If there is a conflict between multiple nodes mining blocks at the same time, eventually, one chain will be longer and that will be accepted as the master blockchain. The other blocks that were mined on the shorter chain will be incorporated into the main blockchain.

Web Application

The web application expose a series of JSON endpoints that will allow each node on the network to interact with the blockchain with its own HTTP server:

  • GET /blocks – view node’s copy of the full blockchain

  • GET /balance – view node’s wallet balance

  • GET /public-key – view node’s public key (to know the wallet address to transfer cryptocurency to)

  • GET /transactions – view node’s copy of the transaction pool (transactions that haven’t been mined)

  • POST /mine – to add a generic new block to the chain (for adding non cryptocurrency blocks to the blockchain)

  • POST /mine-transactions – to add cryptocurrency transactions to the blockchain

  • POST /transact – add cryptocurrency transaction to the transaction pool

The code for the web application is in src/app/index.ts:

Peer-to-Peer Server

Each node on the blockchain network needs the ability to communicate with other nodes (peers) on the network in order to:

  • get an updated blockchain from its peers

  • for broadcasting to other nodes when it has an updated version of the blockchain

Each peer-to-peer server uses web sockets instead of HTTP to communicate with each node.

Blockchain synchronization between nodes is done by sending a synchronization message to other nodes as soon as new block is mined on any node. However, before a node replaces its blockchain from an update it received from one of its peers, it needs to check that the updated chain is longer than its current chain and that the new blockchain is valid (by validating its hashes). For new nodes that just join the network, they get an updated version of the blockchain from one of its peers.

The peer-to-peer server is also responsible for synchronizing and clearing the transaction pool between nodes.

The code for the peer-to-peer server is in src/app/p2p-server.ts, which contains the code for blockchain synchronization between nodes:

 * Convenience method for sending blockchain on a socket.
 * Specifies that message contains blockchain by setting "type" parameter.
 * @param webSocket The WebScoket to send the blockchain on.
sendChain(webSocket: WebSocket): void {
    chain: this.blockchain.chain

The peer-to-peer server is also responsible for processing incoming messages from other nodes and determining what to do, based on the message type. There are three message command types:

  • replace the current blockchain with a new blockchain

  • update or add a new transaction in the transaction pool

  • clear the transaction pool

Code snippet for processing incoming messages:

 * Process incoming message, based on the message type.
 * @param socket Socket of incoming message.
messageHandler(socket: WebSocket): void {
  socket.on("message", message => {
    const messageData = JSON.parse(message.toString());

    switch(messageData.type) {
      case MESSAGE_TYPES.chain:
      case MESSAGE_TYPES.transaction:;
      case MESSAGE_TYPES.clear_transactions:;
        throw new Error("Undefined message type: " + messageData.type);

Cryptocurrency App

On top of our blockchain, we have a cryptocurrency app that leverages the blockchain to allow transferring digital currency between individuals. Our cryptocurrency app consists of:

  • Wallet object for each user

  • Public-private key pair for digital signatures and verifying signatures from other people

  • Transaction objects to represent the currency exchange between parties

Cryptocurrency Wallet

Our cryptocurrency app uses a wallet to store a user’s balance and the public-private key pair. The private key is used to generate digital signatures and the public key is used to verify signatures from other people. The public address of the wallet is just the public key. The key pair is generated with secp256k1 elliptical algorithm, the same algorithm used by Bitcoin.

The wallet class is implemented in src/wallet/index.ts. Here is a snippet that shows the wallet properties and constructor. Note that each wallet starts with a pre-set balance.

export default class Wallet {
    balance: number;
    keypair: any;
    publicKey: any;
    static bcWallet: Wallet;

    //for balance re-calculation - need to know from where to start recalculating
    lastBlockTimestamp: number;

    //when last did balance recalculation, this was the last block
    lastBlockBalanceCalc: number;

    constructor() {
        this.balance = config.INITIAL_BALANCE;
        this.keypair = ChainUtil.genKeyPair();
        this.publicKey = this.keypair.getPublic().encode("hex");
        this.lastBlockTimestamp = 0;
        this.lastBlockBalanceCalc = 0;

Blockchain Wallet

The blockchain wallet is implemented in the Wallet class as a Singleton so the same wallet instance can be reused. The blockchain wallet code is in src/wallet/index.ts:

 * Uses Singleton pattern to retrieve the special Blockchain Wallet.
 * Creates it only one time.
static getBlockchainWallet():Wallet {
  if(!Wallet.bcWallet) {
    Wallet.bcWallet = new this();
    Wallet.bcWallet.publicKey = config.BLOCKCHAIN_WALLET_ADDRESS;
  return Wallet.bcWallet;

Cryptocurrency Transactions

Each block in the blockchain will store one or more cryptocurrency transactions. Since the blockchain is a permanent record of all transactions, each user can see a complete history of their transactions on the blockchain. Each transaction object consists of:

  • a unique ID to identify the transaction object (to make transactions easier to find in large collections)

  • a single Transaction Input object

  • an array of Transaction Output objects

Code for the Transaction class is in src/wallet/transaction.ts. Here is a snippet of the properties of the Transaction class:

export default class Transaction {
    id: string;
    txInput: TransactionInput;
    txOutputs: TransactionOutput[];

    constructor() { = ChainUtil.genID();
        this.txOutputs = [];

Each Transaction Input object contains:

  • the sender’s address (which is their public key)

  • sender’s starting balance before the transaction

  • timestamp

  • sender’s signature of the transaction

Code for the Transaction Input class is in src/wallet/transaction-input.ts:

export default class TransactionInput {
    amount: number;
    address: string;
    timestamp: number;
    signature: string;
    constructor(amount: number, address: string) {
        this.amount = amount;
        this.address = address;

Each Transaction Output object contains:

  • recipient of transaction (i.e. the other wallet)

  • amount of currency transferred to recipient

Code for the Transaction Output class is in src/wallet/transaction-output.ts:

export default class TransactionOutput {
    amount: number;
    address: string;

Since MoltenChain is a Proof-Of-Stake Blockchain our code works a little differnet...


MoltenChain and MAGMA Coin initialization script

import { Block, Blockchain, Transaction } from "./blockchain";

class MAGMA extends Token { constructor(name: string, symbol: string, totalSupply: number) { super(name, symbol, totalSupply); }

static createNewToken(name: string, symbol: string, totalSupply: number) { return new MAGMA(name, symbol, totalSupply); } }

class MoltenChain extends Blockchain { constructor() { super(); }

static createNewBlockchain() { return new MoltenChain(); }

static createNewTransaction(sender: string, receiver: string, amount: number) { return new Transaction(sender, receiver, amount); }

static createNewBlock(prevHash: string, transactions: Transaction[]) { return new Block(prevHash, transactions); } }

const proofOfStake = (blockchain: MoltenChain, token: MAGMA) => { const currentBlock = blockchain.getLatestBlock(); const validators = token.getTopHolders();

let selectedValidator: string; let maxStake = 0;

validators.forEach(validator => { const stake = token.balanceOf(validator); if (stake > maxStake) { selectedValidator = validator; maxStake = stake; } });

if (selectedValidator) { currentBlock.validator = selectedValidator; } };

const createNewMAGMAToken = (name: string, symbol: string, totalSupply: number) => { const magmaToken = MAGMA.createNewToken(name, symbol, totalSupply); console.log(Successfully created new MAGMA token: ${} (${magmaToken.symbol}) with total supply of ${magmaToken.totalSupply}); return magmaToken; };

const createNewMoltenChainBlockchain = () => { const moltenChain = MoltenChain.createNewBlockchain(); console.log(Successfully created new MoltenChain blockchain.); return moltenChain; };

const createNewTransaction = (sender: string, receiver: string, amount: number, moltenChain: MoltenChain, magmaToken: MAGMA) => { magmaToken.transfer(sender, receiver, amount); const transaction = MoltenChain.createNewTransaction(sender, receiver, amount); moltenChain.addTransaction(transaction); console.log(Successfully added new transaction to MoltenChain blockchain: ${JSON.stringify(transaction)}); return transaction; };

const createNewBlock = (moltenChain: MoltenChain, magmaToken: MAGMA) => { const prevHash = moltenChain.getLatestBlock().hash; const transactions = moltenChain.pendingTransactions; proofOfStake(moltenChain, magmaToken); const block = MoltenChain.createNewBlock(prevHash, transactions); moltenChain.addBlock(block); console.log(Successfully added new block to MoltenChain blockchain: ${JSON.stringify(block)}); return


import { MoltenChain, MAGMA } from "./moltenChain";

class Wallet { private balance: number; private moltenChain: MoltenChain; private address: string;

constructor(address: string, moltenChain: MoltenChain) { this.balance = 0; this.moltenChain = moltenChain; this.address = address; }

public async createNewToken(proofOfStake: number): Promise { if (this.balance < proofOfStake) { throw new Error("Insufficient funds to create new token"); } this.balance -= proofOfStake; return this.moltenChain.createNewToken(this.address, proofOfStake); }

public async transferToken(token: MAGMA, recipient: string, amount: number): Promise { if (this.balance < amount) { throw new Error("Insufficient funds to transfer token"); } this.balance -= amount; return this.moltenChain.transferToken(token, this.address, recipient, amount); }

public async getBalance(): Promise { return this.moltenChain.getBalance(this.address); } }


define the MAGMA cryptocurrency and MoltenChain blockchain class MAGMA { constructor() { this.balance = 0; } }

class MoltenChain { constructor() { this.chain = []; this.currentBlock = { nonce: 0, data: [], previousHash: '' }; this.difficulty = 3; this.pendingTransactions = []; }

// proof of stake protocol to create a non fungible token createNonFungibleToken(data: any, stake: MAGMA) { // check if stake is sufficient if (stake.balance < this.difficulty) { console.log('Insufficient stake'); return; }

// create a new block with the non fungible token data let newBlock = { nonce: this.currentBlock.nonce + 1, data: data, previousHash: this.currentBlock.previousHash };

// add the new block to the chain and update the current block this.chain.push(newBlock); this.currentBlock = newBlock;

// decrease the stake balance and add the non fungible token to the pending transactions stake.balance -= this.difficulty; this.pendingTransactions.push({type: 'nonFungibleToken', data: data}); } }


import { Block, Blockchain, Transaction, FungibleToken, ProofOfStake } from 'moltenchain';class MAGMA implements FungibleToken { private name: string; private symbol: string; private totalSupply: number; private transactions: Transaction[];constructor(name: string, symbol: string, totalSupply: number) { = name; this.symbol = symbol; this.totalSupply = totalSupply; this.transactions = []; }public getName(): string { return; }public getSymbol(): string { return this.symbol; }public getTotalSupply(): number { return this.totalSupply; }public transfer(from: string, to: string, amount: number): boolean { // validate transaction if (amount <= 0 || !this.validateTransaction(from, to, amount)) { return false; }// create new transaction const transaction = new Transaction(from, to, amount); this.transactions.push(transaction); return true; }public balanceOf(address: string): number { let balance = 0; for (const transaction of this.transactions) { if (transaction.from === address) { balance -= transaction.amount; } if ( === address) { balance += transaction.amount; } } return balance; }private validateTransaction(from: string, to: string, amount: number): boolean { // check if sender has sufficient balance if (this.balanceOf(from) < amount) { return false; } return true; } }class MoltenChain extends Blockchain { private proofOfStake: ProofOfStake; private magma: MAGMA;constructor(proofOfStake: ProofOfStake, magma: MAGMA) { super(); this.proofOfStake = proofOfStake; this.magma = magma; }public createBlock(data: any): Block { // validate block with proof of stake if (!this.proofOfStake.validateBlock(data)) { throw new Error('Invalid block'); }// create new block with data and previous hash const block = new Block(data, this.getLatestBlock().hash); this.addBlock(block); return block; }public getFungibleToken(): MAGMA { return this.magma; } }const proofOfStake = new ProofOfStake(); const magma = new MAGMA('MAGMA', 'MAG', 1000000); const moltenChain = new MoltenChain(proofOfStake, magma);console.log(moltenChain.getFungibleToken().transfer('wallet1', 'wallet2', 500)); console.log(moltenChain.getFungibleToken().balanceOf('wallet1')); console.log(moltenChain.getFungibleToken().balanceOf('wallet2'));moltenChain.createBlock({ data:


import { Block, Transaction, NonFungibleToken } from 'moltenchain';

class MAGMA implements NonFungibleToken { // Non-fungible token metadata name: string; symbol: string; totalSupply: number; owner: string; metadata: any;

constructor(name: string, symbol: string, totalSupply: number, owner: string, metadata: any) { = name; this.symbol = symbol; this.totalSupply = totalSupply; this.owner = owner; this.metadata = metadata; } }

class MoltenChain { // Blockchain properties chain: Block[]; currentBlock: Block; difficulty: number;

// Proof of stake properties validators: string[]; stake: number;

constructor(difficulty: number, validators: string[], stake: number) { this.chain = [this.createGenesisBlock()]; this.currentBlock = this.chain[0]; this.difficulty = difficulty; this.validators = validators; this.stake = stake; }

// Creates the first block in the blockchain (genesis block) createGenesisBlock(): Block { return new Block(, [], '0'); }

// Returns the latest block in the blockchain getLatestBlock(): Block { return this.chain[this.chain.length - 1]; }

// Adds a new block to the blockchain addBlock(newBlock: Block): void { newBlock.previousHash = this.getLatestBlock().hash; newBlock.mineBlock(this.difficulty); this.chain.push(newBlock); }

// Validates the blockchain isChainValid(): boolean { for (let i = 1; i < this.chain.length; i++) { const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1];

// Check if block has been tampered with if (currentBlock.hash !== currentBlock.calculateHash()) { return false; }

// Check if previous block hash points to the correct block if (currentBlock.previousHash !== previousBlock.hash) { return false; } } return true; }

// Adds a new transaction to the blockchain addTransaction(transaction: Transaction): number { if (!transaction.fromAddress || !transaction.toAddress) { throw new Error('Transaction must include from and to addresses'); }

// Check if validator has sufficient stake if (this.validators.indexOf(transaction.fromAddress) === -1 || this.stake < transaction.amount) { throw new Error('Validator does not have sufficient stake'); }

// Add transaction to current block this.currentBlock.transactions.push(transaction);

// Check if block is full and create a new one if it is if (this.currentBlock.transactions.length >= this.difficulty) { this.addBlock(new Block(, [], this

This a rough write-up, we currently testing it and improving the code.

Tightening it up, even more, the smart contracts on MoltenChain are written in Lua.

import { Blockchain, Block, FungibleToken, NonFungibleToken, SmartContract, ProofOfStake } from 'moltenchain';

class MAGMA extends Blockchain {
constructor() {
super(); = 'MAGMA';
this.consensus = new ProofOfStake();

createFungibleToken(name: string, symbol: string, totalSupply: number): FungibleToken {
return new FungibleToken(name, symbol, totalSupply);

createNonFungibleToken(name: string, symbol: string, data: any): NonFungibleToken {
return new NonFungibleToken(name, symbol, data);

createSmartContract(code: string, language: string): SmartContract {
if (language !== 'lua') {
throw new Error('Only LUA is supported for smart contracts on MoltenChain');
return new SmartContract(code, language);

const magma = new MAGMA();

const myFungibleToken = magma.createFungibleToken('My Token', 'MTK', 100);
const myNonFungibleToken = magma.createNonFungibleToken('My Collectible', 'MTC', { rarity: 'rare' });
const mySmartContract = magma.createSmartContract('print("Hello, MoltenChain!")', 'lua');

Storing MoltenChain records to the cloud

import { Storage } from '@google-cloud/storage';

// Function to save records to the cloud storage for the MoltenChain blockchain async function saveRecords(records: any[], bucketName: string): Promise {

// Create a client for interacting with the Google Cloud Storage API

const storage = new Storage();

// Get a reference to the specified bucket

const bucket = storage.bucket(bucketName);

// Convert the records to a JSON string

const json = JSON.stringify(records);

// Create a new file in the bucket with the JSON data await bucket.file('molten_chain_records.json').save(json); }

// Example usage:

const records = [ { name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Charlie', age: 35 }, ]; saveRecords(records, 'molten_chain_records_bucket').then(() => { console.log('Records saved successfully'); }).catch((error) => { console.error(error); });

Local MoltenChain records

import { writeFileSync } from 'fs';

// Function to save records to an external storage location for the MoltenChain blockchain

function saveRecords(records: any[], filePath: string): void {

// Convert the records to a JSON string

const json = JSON.stringify(records);

// Save the JSON to the specified file path

writeFileSync(filePath, json); }

// Example usage:

const records = [ { name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Charlie', age: 35 }, ]; saveRecords(records, '/path/to/molten_chain_records.json');

MoltenChain Smart Contracts in LUA

Here is an example of a token-locking smart contract for MoltenChain written in LUA.

-- Smart contract for token locking mechanism on the MAGMA cryptocurrency on the MoltenChain blockchain

-- Define variables

local tokensLocked = 0

-- Number of tokens currently locked

local lockDuration = 86400 -- Duration of lock in seconds (86400 seconds = 1 day) local lockStartTime = 0 -- Timestamp of when the lock began

-- Function to lock tokens

function lockTokens(numTokens: number) if numTokens < 0 then error("Number of tokens to lock must be a positive integer") end tokensLocked = tokensLocked + numTokens lockStartTime = os.time() end

-- Function to unlock tokens

function unlockTokens(numTokens: number) if numTokens < 0 then error("Number of tokens to unlock must be a positive integer") end if numTokens > tokensLocked then error("Cannot unlock more tokens than are currently locked") end if os.time() < lockStartTime + lockDuration then error("Tokens cannot be unlocked until the lock duration has expired") end tokensLocked = tokensLocked - numTokens end

-- Function to check current number of locked tokens

function getLockedTokens() return tokensLocked end

-- Function to check lock duration

function getLockDuration() return lockDuration end

-- Function to check lock start time

function getLockStartTime() return lockStartTime end