Developing an entire blockchain with NFT & FT deployment, baked into the source code.
Table of contents
- What is TypeScript?
- Crypto in TypeScript
- TypeScript Configuration and Issues
- Unit Testing with Jest
- Blockchain – Trustless System
- Block Class
- Genesis Block
- Blockchain Class
- Blockchain Replication
- Web Application
- Peer-to-Peer Server
- Cryptocurrency App
- Cryptocurrency Wallet
- Blockchain Wallet
- Cryptocurrency Transactions
- Since MoltenChain is a Proof-Of-Stake Blockchain our code works a little differnet...
- MAGMA NATIVE ON MOLTENCHAIN in TypeScript
- MoltenChain and MAGMA Coin initialization script
- WALLET IN TYPESCRIPT
- MOLTENCHAIN NFT DEPLOYMENT
- MOLTENCHAIN MINT FUNGIBLE TOKEN
- GRAB MNFT METADATA FROM MOLTENCHAIN
- This a rough write-up, we currently testing it and improving the code.
- Storing MoltenChain records to the cloud
- Local MoltenChain records
- MoltenChain Smart Contracts in LUA
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:
content_copy
"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(); genesisTx.id = "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 {
webSocket.send(JSON.stringify({
type:MESSAGE_TYPES.chain,
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:
this.blockchain.replaceChain(messageData.chain);
break;
case MESSAGE_TYPES.transaction:
this.tp.updateOrAddTransaction(messageData.transaction);
break;
case MESSAGE_TYPES.clear_transactions:
this.tp.clear();
break;
default:
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() {
this.id = 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...
MAGMA NATIVE ON MOLTENCHAIN in TypeScript
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.name} (${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
WALLET IN TYPESCRIPT
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); } }
MOLTENCHAIN NFT DEPLOYMENT
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}); } }
MOLTENCHAIN MINT FUNGIBLE TOKEN
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) { this.name = name; this.symbol = symbol; this.totalSupply = totalSupply; this.transactions = []; }public getName(): string { return this.name; }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 (transaction.to === 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:
GRAB MNFT METADATA FROM MOLTENCHAIN
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) { this.name = 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(Date.now(), [], '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(Date.now(), [], 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();
this.name = '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