aiShare Your Requirements
Ashish  Gushain Oodles

Ashish Gushain (Backend-Senior Associate Consultant L1 - Development)

Experience: 3+ yrs

Ashish Gushain is a highly experienced backend developer with over 1+ years of industry expertise in Solidity and Smart contract development. He possesses in-depth knowledge of web3, blockchain architecture, and practical experience in working with various tools and frameworks such as Nodejs, Hardhat, Remix, React, and databases such as MongoDB and Postgres. He is highly skilled in decentralized finance (DeFi) and specializes in implementing complex financial protocols on the blockchain. He has a passion for reading and constantly stays updated with the latest trends in the blockchain and cryptocurrency space.

Ashish  Gushain Oodles
Ashish Gushain
(Senior Associate Consultant L1 - Development)

Ashish Gushain is a highly experienced backend developer with over 1+ years of industry expertise in Solidity and Smart contract development. He possesses in-depth knowledge of web3, blockchain architecture, and practical experience in working with various tools and frameworks such as Nodejs, Hardhat, Remix, React, and databases such as MongoDB and Postgres. He is highly skilled in decentralized finance (DeFi) and specializes in implementing complex financial protocols on the blockchain. He has a passion for reading and constantly stays updated with the latest trends in the blockchain and cryptocurrency space.

LanguageLanguages

DotEnglish

Fluent

DotHindi

Fluent

SkillsSkills

DotAnchor

60%

DotNode Js

80%

DotMetaMask

80%

DotBlockchain

80%

DotEthers.js

80%

DotBIP39 Standards

80%

DotWeb3.js

80%

DotEtherscan

80%

DotToken V

60%

DotSolidity

100%

DotNFT Marketplace

60%

DotSolana

80%

DotMern Stack

60%

DotCoinbase API

80%

DotBot

60%

DotRust

60%

DotStellar (XLM)

60%

DotJavascript

80%
ExpWork Experience / Trainings / Internship

Jun 2022-Present

Associate Consultant - Development

Gurugram


Oodles Technologies

Gurugram

Jun 2022-Present

EducationEducation

2015-2018

Dot

Jamia Hamdard

BCA-Computer Applications

Top Blog Posts
Building a Cross-Chain NFT Bridge using Solana Wormhole The blockchain ecosystem is rapidly evolving, with numerous networks offering unique features and capabilities. However, this diversity often leads to fragmentation, where assets and applications are siloed within their respective chains. Non-fungible tokens (NFTs), which have gained immense popularity, are no exception to this challenge. To unlock the full potential of NFT development, it is crucial to enable their seamless transfer and interaction across different blockchain networks. This is where cross-chain bridges come into play.In this blog, we will explore the theoretical underpinnings of building cross-chain NFT bridges using Solana's Wormhole protocol. We'll delve into the architecture, key components, and the steps involved in creating a bridge that allows NFTs to move between Solana and other blockchain networks.Understanding the Need for Cross-Chain NFT BridgesNFTs are unique digital assets that represent ownership of a specific item or piece of content. They have found applications in art, gaming, virtual real estate, and more. However, the value and utility of NFTs are often limited to the blockchain on which they are minted. Cross-chain NFT bridges aim to overcome this limitation by enabling NFTs to be transferred and utilized across different blockchain networks.Benefits of Cross-Chain NFT Bridges:Interoperability: NFTs can be used across multiple platforms and ecosystems, increasing their utility and value.Liquidity: By enabling NFTs to move between chains, bridges can tap into larger and more diverse markets.Innovation: Developers can create cross-chain applications that leverage the unique features of multiple blockchains.Also, Read | How to Create an NFT Rental Marketplace using ERC 4907Solana Wormhole: A PrimerSolana's Wormhole is a generic message-passing protocol that facilitates communication between Solana and other blockchain networks. It acts as a bridge, allowing data and assets to be transferred across chains. Wormhole achieves this by using a set of validators (called "guardians") that observe and verify events on one chain and relay them to another.Key Features of Wormhole:Decentralized: Wormhole relies on a network of guardians to ensure the security and integrity of cross-chain transactions.Extensible: The protocol is designed to support multiple blockchain networks, making it a versatile solution for cross-chain interoperability.Efficient: Wormhole leverages Solana's high throughput and low latency to enable fast and cost-effective cross-chain transactions.Also, Explore | How to Implement an On-Chain NFT AllowlistArchitecture of a Cross-Chain NFT Bridge Using WormholeBuilding a cross-chain NFT bridge using Solana Wormhole involves several key components and steps. Let's break down the theoretical architecture:NFT Representation on the Source ChainMinting NFTs: NFTs are minted on the source blockchain (e.g., Ethereum) using a standard like ERC-721 or ERC-1155.Locking NFTs: To initiate a cross-chain transfer, the NFT is locked in a smart contract on the source chain. This ensures that the NFT cannot be transferred or sold while it is being bridged.Wormhole Message PassingCreating a Wormhole Message: Once the NFT is locked, a Wormhole message is created. This message contains essential information about the NFT, such as its metadata, ownership, and the target chain.Guardian Validation: The Wormhole guardians observe the locking event on the source chain and validate the Wormhole message. Once validated, the message is signed and relayed to the target chain.NFT Representation on the Target ChainReceiving the Wormhole Message: On the target chain (e.g., Solana), the Wormhole message is received and verified by the local Wormhole smart contract.Minting a Wrapped NFT: A wrapped NFT is minted on the target chain, representing the original NFT from the source chain. This wrapped NFT adheres to the target chain's NFT standard (e.g., SPL Token on Solana).Unlocking the Original NFT: Once the wrapped NFT is minted, the original NFT remains locked on the source chain until it is redeemed.Redeeming the Original NFTBurning the Wrapped NFT: To redeem the original NFT, the wrapped NFT on the target chain is burned, and a corresponding Wormhole message is created.Unlocking the Original NFT: The Wormhole guardians validate the burning event and relay the message back to the source chain. The original NFT is then unlocked and can be transferred or sold.Security and Trust ConsiderationsGuardian Network: The security of the bridge relies on the integrity of the Wormhole guardians. A decentralized and diverse set of guardians is essential to prevent collusion and ensure trust.Smart Contract Audits: Both the source and target chain smart contracts must be thoroughly audited to prevent vulnerabilities and ensure the safe handling of NFTs.Economic Incentives: Guardians and participants in the bridge should be incentivized to act honestly, with mechanisms in place to penalize malicious behavior.Also, Discover | A Guide to Implementing NFT Royalties on ERC-721 & ERC-1155Potential Challenges and SolutionsCross-Chain CompatibilityChallenge: Different blockchains have different standards and architectures, making it challenging to create a seamless bridge.Solution: Wormhole's extensible design allows it to support multiple chains, and the use of wrapped NFTs ensures compatibility with the target chain's standards.Security RisksChallenge: Cross-chain bridges are attractive targets for hackers due to the value of assets being transferred.Solution: Implementing robust security measures, such as multi-signature schemes, regular audits, and a decentralized guardian network, can mitigate these risks.ScalabilityChallenge: As the number of cross-chain transactions grows, the bridge must be able to handle increased load without compromising performance.Solution: Leveraging Solana's high throughput and low latency can help ensure that the bridge remains scalable and efficient.You may also like | How to Get the Transaction History of an NFTConclusionCross-chain NFT bridges represent a significant step forward in the evolution of the blockchain ecosystem. By enabling NFTs to move seamlessly between different networks, these bridges unlock new possibilities for interoperability, liquidity, and innovation. Solana's Wormhole protocol provides a powerful foundation for building such bridges, offering a decentralized, extensible, and efficient solution.While there are challenges to overcome, the theoretical architecture outlined in this blog provides a roadmap for building secure and scalable cross-chain NFT bridges. As the blockchain space continues to mature, we can expect to see more sophisticated and user-friendly bridges that bring the vision of a truly interconnected blockchain ecosystem to life. If you are planning to build and launch your NFT project, connect with our skilled blockchain developers to get started.
Category: Blockchain Development & Web3 Solutions
How to Build a Cross-Chain Bridge Using Solidity and Rust The capacity to transfer assets across networks effortlessly is more important than ever in the ever-changing world of blockchain development. Envision a bridge that unites the Ethereum and Solana worlds, letting tokens move freely while upholding security and openness. In this project, we use the robust programming languages Solidity and Rust to set out on the task of creating a cross-chain bridge. Through utilizing Rust's efficiency for Solana's on-chain logic and Solidity's capabilities for Ethereum smart contracts, our goal is to provide a solid framework that makes token exchanges simple. Whether you're a crypto enthusiast excited to explore new possibilities or a developer trying to improve interoperability, this guide will take you through all the necessary steps to realize this ambitious aim. Now let's dive in.PrerequisitesProgrammingLanguages:Solidity, Javascript/Typescript, and RustIDE:VS-Code and any preferred IDELibraries:ETH:Hardhat, Ethers, Axios, OpenzepplinSOL:Solana Web3.js, Solana Spl-tokenAlso, Explore | How To Build "Buy Me a Coffee" DeFi dApp Using SolidityHow to Build a Cross-Chain Bridge Using Solidity and RustIt's preferred that you setup 3 projects, separately for:I) Ethereum's ERC-20 Token Smart Contract:You'll need to setup node.js, solidity and hardhat in your IDE/ system. So, we'll begin with setting up hardhat code, for example "click-here". Here's the code for the ERC-20 Token using Openzepplin's library.code.................................................................................................. // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract Testtoken is ERC20, Ownable { event BurnAndMoveToSolana(address indexed user, string solanaAddress, uint256 amount); event MintFromSolana(address indexed user, uint256 amount); address public relayer; constructor() ERC20("EthereumToken", "ETHR") Ownable(msg.sender){ _mint(msg.sender, 1000000 * (10 ** decimals()));// change amount as per your understanding } modifier onlyRelayer() { require(msg.sender == relayer, "Not authorized"); _; } function setRelayer(address _relayer) external onlyOwner { relayer = _relayer; } function burnAndMoveToSolana(uint256 amount, string memory solanaAddress) external {// main transfering function _burn(msg.sender, amount); emit BurnAndMoveToSolana(msg.sender, solanaAddress, amount); } function mintFromSolana(address to, uint256 amount) external onlyRelayer { _mint(to, amount); emit MintFromSolana(to, amount); } event TokensBurned(address indexed from, address indexed solanaAddress, uint256 amount); }You may also like | Building a Decentralized Voting System with Solidity and Hardhat2) Solana's SPL Token Program:You'll need to setup node.js, Solana, and Rust in your IDE/ system. To begin with, we'll set-up a empty solana-sdk code. Here's the full code/implementation for the SPL Token using Solana-web3.js & Solana spl-token.code................................................................................................. const { Connection, Keypair, PublicKey, clusterApiUrl, LAMPORTS_PER_SOL } = require('@solana/web3.js'); const { createMint, getOrCreateAssociatedTokenAccount, mintTo, getAccount, burn } = require('@solana/spl-token'); async function mintAndBurnTokens(connection, fromWallet, tokenAccount, mint, amountToMint, amountToBurn, ethAddress) { await mintTo( connection, fromWallet, mint, tokenAccount.address, fromWallet.publicKey, amountToMint ); console.log(`Minted ${amountToMint / (10 ** 9)} tokens to your associated token account.`); const tokenAccountBalance = await getAccount(connection, tokenAccount.address); console.log(`Token account balance after minting: ${Number(tokenAccountBalance.amount) / (10 ** 9)} tokens`); if (Number(tokenAccountBalance.amount) < amountToBurn) { console.log(`Insufficient funds. Current balance: ${Number(tokenAccountBalance.amount) / (10 ** 9)} tokens.`); return; } await burn( connection, fromWallet, tokenAccount.address, mint, fromWallet.publicKey, amountToBurn ); console.log(`Burned ${amountToBurn / (10 ** 9)} tokens from ${fromWallet.publicKey} and moving to Ethereum wallet ${ethAddress}.`); console.log(`Relaying burn event to Ethereum relayer for Ethereum wallet: ${ethAddress}, amount: ${amountToBurn / (10 ** 9)}.`); } (async () => { const fromWallet = Keypair.fromSecretKey(new Uint8Array([your,secret,keypair])); const ethAddress = "0xyourAddress";//add your eth wallet address const mintAmount = 100000 * 10 ** 9;// amount of SPL tokens to mint const burnAmount = 1000 * 10 ** 9;// amount of SPL tokens to burn/transfer const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');// put your preferred cluster console.log('Creating SPL token...'); const mint = await createMint( connection, fromWallet, fromWallet.publicKey, null, 9 ); const fromTokenAccount = await getOrCreateAssociatedTokenAccount( connection, fromWallet, mint, fromWallet.publicKey ); console.log('Minting tokens...'); await mintAndBurnTokens(connection, fromWallet, fromTokenAccount, mint, mintAmount, burnAmount, ethAddress); console.log(`View token account on Solana Explorer: https://explorer.solana.com/address/${fromTokenAccount.address}?cluster=devnet`); })(); ////////////////////////////////////////////////////////////////////////Also, Read | How To Create a Daily Game Reward System in Solidity3) Relayer-Bridge Project:In order to facilitate safe and transparent token transfers between two blockchains, a relayer-bridge project serves as an essential bridge. Using smart contracts and event listeners, the relayer in the Ethereum and Solana context watches on particular occurrences on one blockchain, like an Ethereum token burn. When the relayer notices one of these events, it sends the required information—such as the recipient's address and the quantity of tokens—to the other chain so that the corresponding action—like minting the tokens on Solana—can take place there. In order to preserve network balance, this bi-directional communication makes sure that tokens burned on one chain are minted on the other. In order to smoothly connect the two ecosystems, the relayer's job is to validate and relay these transactions without sacrificing security or speed.Here's the Code for the Relayer-Bridge :code.................................................................................................. const WebSocket = require("ws"); const { ethers } = require("ethers"); const fs = require("fs"); require('dotenv').config(); const wsUrl = "wss://api.devnet.solana.com";//your desired network const connection = new WebSocket(wsUrl); const provider = new ethers.WebSocketProvider(process.env.ETH_WSS_URL); const wallet = new ethers.Wallet(process.env.ETH_PRIVATE_KEY, provider); const contractAddress = process.env.ETH_CONTRACT_ADDRESS; const abi = JSON.parse(fs.readFileSync("./path_to_your/eth_contract_abi.json")); const contract = new ethers.Contract(contractAddress, abi, wallet); connection.on("open", () => { console.log("Connected to Solana WebSocket"); const subscriptionMessage = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "logsSubscribe", params: [ { mentions: [""],// Your SPL token address }, { commitment: "finalized", }, ], }); connection.send(subscriptionMessage); }); connection.on("message", async (data) => { const response = JSON.parse(data); if (response.method === "logsNotification") { const logData = response.params.result; // Check if the log indicates a burn if (isBurnLog(logData)) { const amountBurned = extractBurnAmount(logData); console.log(`Burn detected: ${amountBurned} tokens`); await mintTokens(amountBurned); } } else { console.log("Received message:", response); } }); connection.on("close", () => { console.log("Connection closed"); }); connection.on("error", (error) => { console.error("WebSocket error:", error); }); // Function to Check the log data structure to confirm it's a burn event function isBurnLog(logData) { return logData && logData.err === null && logData.logs && logData.logs.some(log => log.includes("burn")); } // Function to extract the amount burned from the log data function extractBurnAmount(logData) { const amountLog = logData.logs.find(log => log.includes("burn")); if (amountLog) { const amount =/* logic to parse your burn amount format */; return parseFloat(amount);// Return the amount as a number } return 0; } // Function to mint tokens on Ethereum async function mintTokens(amount) { try { const tx = await contract.mint(wallet.address, ethers.utils.parseUnits(amount.toString(), 18)); console.log(`Mint transaction sent: ${tx.hash}`); await tx.wait(); console.log("Minting successful"); } catch (error) { console.error("Minting failed:", error); } } /////////////////////////////////////////////////////////////////////////This part of the relayer works for the transfer of SPL tokens to the ERC-20 tokens on Ethereum. Similarly, we can perform the transfer of ERC-20 tokens to SPL Tokens on the Solana blockchain, burn them, and its functionality will trigger the SPL Token's mint function to complete the cross-chain transaction.Also, Discover | How to Create a MultiSig Wallet in SolidityConclusionIn conclusion, creating a relayer-equipped cross-chain bridge enables users to transfer assets between Ethereum and Solana with ease, opening up a world of decentralised opportunities. Utilising Solidity and Rust's respective advantages, you can build a scalable, secure solution that connects two robust blockchain ecosystems. This project shapes the future of decentralised finance by paving the ground for true blockchain interoperability with the correct tools and knowledge. Connect with our skilled Solidity developers to bring your blockchain-related vision into reality.
Category: Blockchain Development & Web3 Solutions
Building a Decentralized Voting System with Solidity and Hardhat In this blog, we will guide you through the process of building a decentralized voting system using Solidity and Hardhat, which are heavily used in blockchain app development. You will learn how to create and deploy smart contracts that ensure secure, transparent, and tamper-proof voting. Ideal for developers looking to harness the power of blockchain technology in democratic processes, this tutorial covers everything from setting up your environment to writing and testing your contracts. With the help of this mechanism, voters will be able to safely cast their ballots on the Ethereum blockchain, guaranteeing that they cannot be manipulated.Also, Check | How To Build "Buy Me a Coffee" DeFi dApp Using SoliditySetting Up the Development Environment1. Install Node.js.2. Setup Hard Hat: Install Hardhat by running the following command in your terminal: npm install --save-dev hardhat3. Create a Hardhat Project: Initialize a new Hardhat project by running: npx hardhat You may also like | How To Create a Daily Game Reward System in SolidityWriting the Smart Contract1. Create the Contract: Inside the contracts directory, create a new file named Voting.sol. This Solidity file will hold our voting logic.2. Implement the Contract: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Voting { struct Candidate { uint id; string name; uint voteCount; } mapping(uint => Candidate) public candidates; uint public candidatesCount; mapping(address => bool) public voters; constructor() { addCandidate('Alice'); addCandidate('Bob'); } function addCandidate(string memory _name) private { candidatesCount ++; candidates[candidatesCount] = Candidate(candidatesCount, _name, 0); } function vote(uint _candidateId) public { require(!voters[msg.sender], 'You have already voted.'); require(_candidateId > 0 && _candidateId <= candidatesCount, 'Invalid candidate ID.'); voters[msg.sender] = true; candidates[_candidateId].voteCount ++; } }Also, Check | How to Create a MultiSig Wallet in SolidityDeploying the Contract1. Configure Deployment Scripts:- Inside the scripts directory, create a file named deploy.js.- Add the following code to deploy the contract: async function main() { const Voting = await ethers.getContractFactory('Voting'); const voting = await Voting.deploy(); await voting.deployed(); console.log('Voting deployed to:', voting.address); } main().catch((error) => { console.error(error); process.exitCode = 1; });Also, Read | Exploring Data Structures in Solidity for Advanced Smart ContractsTesting the Contract1. Write Tests:- In the test directory, create a new file for the tests.- Use Hardhat's testing framework to write tests for your contract.2. Run Tests: npx hardhat test Also, Explore | Identifying Smart Contract Orchestration Patterns in SolidityConclusionCongratulations! You now have a sophisticated decentralized voting system deployed on Ethereum using Solidity and Hardhat. This system lays the groundwork for numerous enhancements, such as advanced voting mechanisms, time-bound functionalities, and complex candidate structures. Imagine implementing features like weighted votes, multi-tiered elections, or secure voter verification – the possibilities are endless!Ready to take your decentralized voting system to the next level? Contact our expert Solidity developers at Oodles to transform your vision into a robust, feature-rich solution tailored to your specific needs. Let's innovate together and redefine the future of voting!
Category: Blockchain Development & Web3 Solutions
How to Build and Deploy a Subgraph with The Graph Protocol The Graph is a tool that helps apps quickly access blockchain data. It uses a technology called GraphQL to pull data efficiently. Developers use it during blockchain app development to build faster and more reliable blockchain apps. It organizes blockchain data into something called subgraphs, making it easier to handle. This tool supports several blockchains, enhancing its usefulness for developers.AdvantagesSpeed: It speeds up how quickly apps can access blockchain data.Efficiency: It makes data retrieval more efficient using GraphQL.Simplicity: Organizes data into subgraphs, which are easier for developers to manage.Flexibility: Supports multiple blockchains, giving developers more options.Reliability: Enhances the stability and reliability of decentralized applications.Also, Check | How to Setup and Run a Solana RPC NodeSteps to create and deploy subgraphInstall Graph CLI:Command:yarn global add @graphprotocol/graph-cli` or `npm install -g @graphprotocol/graph-cli`This installs the necessary tools to create and manage subgraphs.Initialize Your Subgraph:Command:`graph init --from-example <GITHUB_USER>/<SUBGRAPH_NAME>`This sets up a new subgraph directory with starter files based on a template.Define subgraph.yaml:Example yaml specVersion: 0.0.2 description: "Example subgraph" repository: "https://github.com/example/repo" dataSources: - kind: ethereum/contract name: ContractName source: address: "0x..." abi: ContractABI mapping: kind: ethereum/events apiVersion: 0.0.4 language: wasm/assemblyscript entities: - EntityName abis: - name: ContractABI file: ./abis/ContractABI.json eventHandlers: - event: Transfer(address,address,uint256) handler: handleTransfer file: ./src/mapping.ts This file configures your subgraph, defining which blockchain events it listens to and how they map to the data entities.You may also like | How To Build "Buy Me a Coffee" DeFi dApp Using SolidityCreate the GraphQL Schema:File: schema.graphqlExample content: ```graphql type Transfer @entity { id: ID! from: Bytes! to: Bytes! value: BigInt! } ```This defines the data structures that your subgraph will store and make queryable.Write AssemblyScript Mappings:File: `src/mapping.ts`Example function: ```typescript import { BigInt } from "@graphprotocol/graph-ts" import { Transfer } from "../generated/Contract/Contract" export function handleTransfer(event: Transfer): void { let transfer = new Transfer(event.params.from, event.params.to, event.params.value) transfer.save() } ```This script processes incoming blockchain events and translates them into entities defined in the GraphQL schema.Deploy Your Subgraph:Command:`graph deploy --product hosted-service <GITHUB_USER>/<SUBGRAPH_NAME>`This uploads your subgraph to The Graph's hosted service, making it available for queries.Also, Read | A Guide to Google Calendar API Integration into Your React ApplicationConclusionIn conclusion, The Graph Protocol is a powerful tool for developers looking to build and deploy efficient, reliable, and scalable blockchain applications. By using GraphQL to quickly access and manage blockchain data, and organizing it into subgraphs, developers can significantly enhance the performance and stability of their decentralized applications. With support for multiple blockchains, The Graph provides flexibility and a robust solution for a variety of use cases. Following the steps outlined—installing the Graph CLI, initializing your subgraph, defining the subgraph.yaml, creating a GraphQL schema, writing AssemblyScript mappings, and deploying your subgraph—will enable you to leverage the full potential of The Graph Protocol, streamlining your development process and improving your application's overall functionality. Connect with our skilled blockchain developers for a quick consultation regarding your blockchain or crypto project.
Category: Blockchain Development & Web3 Solutions
How to Develop Programmable Non-Fungible Tokens on Solana The introduction of Programmable Non-Fungible Tokens (PNFTs) on Solana brings a new dimension to the world of digital ownership, as it allows creators to insert custom rules into their NFTs. In this way, it becomes possible to ensure that activities like transfers and burns are always done with respect to the creator's true intentions in the Solana development space.Do Read | How to Create a Compressed NFT on SolanaKey Features of PNFTsEnhanced Security: For any operation, PNFTs demand that such an action must go through the Token Metadata program, ensuring that no set rules by the creator are bypassed as they might be using the SPL token program.Granular Delegation: Creators can determine precise operations allowed on assets, and assign delegates who have detailed security systems for managing these permissions.Rule Enforcement: Any operation upon which PNFT is enforced may be subjected to diverse custom rules thus allowing inventors to put various prerequisites regarding actions like transfer and update into effect.Practical ApplicationsRoyalty Enforcement: These tokens can enforce payments for royalties by using rules such as those that prevent transfers unless they comply with imposed royalty terms. This perspective on NFTs lets artists claim more control over how their digital assets are used or transferred.Step 1: Project SetupInitialize Project DirectoryCreate and navigate into a new project directory:bash mkdir pnft_project cd pnft_projectCreate TypeScript FileGenerate the main TypeScript file for your application:bash touch app.tsInitialize PackageUse Yarn or npm to initialize your project with default settings:yarn init -y # or npm init -yTypeScript ConfigurationSetup TypeScript with JSON module resolution and target ES2020:tsc --init --resolveJsonModule --target ES2020Paper Wallet SetupStore your development network Solana keys in a file called guideSecret.json:Ensure you generate this securely and store safely.Step 2: Install DependenciesInstall necessary libraries for interacting with Solana's blockchain:yarn add @solana/web3.js @metaplex-foundation/js or npm install @solana/web3.js @metaplex-foundation/jsStep 3: Configure Your AppImport Required ModulesAt the top of app.ts, include the necessary modules from the installed libraries: typescriptimport { Connection, Keypair, PublicKey } from "@solana/web3.js"; import { Metaplex, keypairIdentity, bundlrStorage, toMetaplexFile, toBigNumber } from "@metaplex-foundation/js"; import { TokenStandard } from '@metaplex-foundation/mpl-token-metadata'; import secret from './guideSecret.json';Setup ConnectionConfigure the connection to the Solana cluster using an endpoint:const QUICKNODE_RPC = 'https://example.solana-devnet.quiknode.pro/your_unique_id/'; const SOLANA_CONNECTION = new Connection(QUICKNODE_RPC);Initialize MetaplexSet up the Metaplex client with the connection and wallet information:const WALLET = Keypair.fromSecretKey(new Uint8Array(secret)); const METAPLEX = Metaplex.make(SOLANA_CONNECTION) .use(keypairIdentity(WALLET)) .use(bundlrStorage({ address: 'https://devnet.bundlr.network', providerUrl: QUICKNODE_RPC, timeout: 60000, }));Step 4: Define NFT ConfigurationConfigure the metadata and attributes of your NFT: typescriptconst NFT_CONFIG = { imgName: 'MyUniqueNFT', symbol: 'UNIQ', sellerFeeBasisPoints: 500, // 5% fee creators: [{ address: WALLET.publicKey, share: 100 }], metadata: 'https://arweave.net/example_URI' };Step 5: Create the Mint FunctionImplement the function to mint your pNFT: typescriptasync function mintProgrammableNft( metadataUri: string, name: string, sellerFee: number, symbol: string, creators: { address: PublicKey, share: number }[] ) { console.log("Minting pNFT..."); try { const transactionBuilder = await METAPLEX.nfts().builders().create({ uri: metadataUri, name: name, sellerFeeBasisPoints: sellerFee, symbol: symbol, creators: creators, isMutable: true, isCollection: false, tokenStandard: TokenStandard.ProgrammableNonFungible, ruleSet: null }); let { signature, confirmResponse } = await METAPLEX.rpc().sendAndConfirmTransaction(transactionBuilder); if (confirmResponse.value.err) { throw new Error('Failed to confirm transaction'); } const { mintAddress } = transactionBuilder.getContext(); console.log(` Success!🎉`); console.log(` Minted NFT: https://explorer.solana.com/address/${mintAddress.toString()}?cluster=devnet`); console.log(` Tx: https://explorer.solana.com/tx/${signature}?cluster=devnet`); } catch (err) { console.log(err); } }Step 6: Mint the NFTExecute the minting process by calling your function with the configured settings: typescriptmintProgrammableNft( NFT_CONFIG.metadata, NFT_CONFIG.imgName, NFT_CONFIG.sellerFeeBasisPoints, NFT_CONFIG.symbol, NFT_CONFIG.creators ); // Run the script ts-node app.ts You may also read | A Detailed Guide to NFT Minting on Solana using Metaplex APIConclusionProgrammable NFTs on Solana offer NFT developers an innovative, fast, and secure platform to create advanced and customizable decentralized applications, revolutionizing the digital asset landscape. Contact our NFT developers for expert services.References - (Click the link below)GithubStackOverflow
Category: Blockchain Development & Web3 Solutions
How to Transfer SOL and SPL Tokens Using Anchor The Anchor framework is a Rust-based framework for building Solana programs using Solana blockchain development. It simplifies the process of developing Solana smart contracts by providing a higher-level abstraction over Solana's low-level programming model. Anchor abstracts away much of the complexity involved in writing Solana programs, making it easier for developers to focus on application logic rather than low-level details.PrerequisitesRust InstallationGohere to install Rust.Solana InstallationTo set up Solana, visit the Solana website to download and install the software. Once installed, open your terminal and run the command `solana-keygen new` to generate a key pair. This will create a keypair at the default location.Yarn InstallationGohere to install Yarn.Anchor InstallationInstalling using Anchor version managerAnchor version manager is a tool for using multiple versions of the anchor-cli. It will require the same dependencies as building from source. It is recommended you uninstall the NPM package if you have it installed.Install avm using Cargo. Note this will replace your anchor binary if you had one installed.cargo install --git https://github.com/coral-xyz/anchor avm --locked --force On Linux systems, you may need to install additional dependencies if the cargo install fails. E.g. on Ubuntu:Install the latest version of the CLI using AVM, and then set it to be the version to use.avm install latest avm use latest Verify the installation. anchor --version Steps1.) Initialize a new project, simply run:anchor init <new-workspace-name> 2.) Let's begin by opening lib.rs in the programs folder, we can proceed with developing our program.3.) Below is the program codeuse anchor_lang::prelude::*; use anchor_spl::token::{self, Token, TokenAccount, Transfer as SplTransfer}; use solana_program::system_instruction; declare_id!("MyUniqueProgramId"); #[program] pub mod my_program { use super::*; // Function for transferring lamports pub fn transfer_lamports(ctx: Context<LamportTransfer>, amount: u64) -> Result<()> { let sender_account = &ctx.accounts.sender; let recipient_account = &ctx.accounts.recipient; // Create the transfer instruction let transfer_instruction = system_instruction::transfer(sender_account.key, recipient_account.key, amount); // Invoke the transfer instruction anchor_lang::solana_program::program::invoke_signed( &transfer_instruction, &[ sender_account.to_account_info(), recipient_account.clone(), ctx.accounts.system_program.to_account_info(), ], &[], )?; Ok(()) } // Function for transferring SPL tokens pub fn transfer_spl_tokens(ctx: Context<SplTokenTransfer>, amount: u64) -> Result<()> { let destination_account = &ctx.accounts.destination_token_account; let source_account = &ctx.accounts.source_token_account; let token_program = &ctx.accounts.spl_token_program; let authority = &ctx.accounts.authority; // Transfer tokens from source to destination let cpi_accounts = SplTransfer { from: source_account.to_account_info().clone(), to: destination_account.to_account_info().clone(), authority: authority.to_account_info().clone(), }; let cpi_program = token_program.to_account_info(); // Invoke SPL token transfer token::transfer(CpiContext::new(cpi_program, cpi_accounts), amount)?; Ok(()) } } // Accounts for transferring lamports #[derive(Accounts)] pub struct LamportTransfer<'info> { #[account(mut)] pub sender: Signer<'info>, #[account(mut)] pub recipient: AccountInfo<'info>, pub system_program: Program<'info, System>, } // Accounts for transferring SPL tokens #[derive(Accounts)] pub struct SplTokenTransfer<'info> { pub authority: Signer<'info>, #[account(mut)] pub source_token_account: Account<'info, TokenAccount>, #[account(mut)] pub destination_token_account: Account<'info, TokenAccount>, pub spl_token_program: Program<'info, Token>, } 4.)Let's break down the code and explain the program:Imports:anchor_lang::prelude::*: Imports the necessary items from the Anchor framework's prelude module, providing convenient access to commonly used types and traits.anchor_spl::token::{self, Token, TokenAccount, Transfer as SplTransfer}: Imports types related to SPL tokens from the Anchor SPL module, including theToken,TokenAccount, andTransfer structs.solana_program::system_instruction: Imports the system instruction module from Solana's program library, which includes functions for interacting with the system program.Program ID Declaration:declare_id!("MyUniqueProgramId"): Declares a unique program ID for the Solana program. This macro generates the declaration of a constant representing the program ID.Program Module:#[program] mod my_program { ... }: Defines a module namedmy_program that represents the Solana program. The#[program] attribute is a procedural macro provided by Anchor, which generates necessary code to define the Solana program.Function Definitions:Within themy_program module, two functions are defined:transfer_lamports: Handles the transfer of Solana lamports (the native token of the Solana blockchain).transfer_spl_tokens: Handles the transfer of SPL tokens.Function Parameters:Both functions take aContext parameter, which provides access to program accounts and environment data required for the execution of the function. TheContext is a generic type parameterized by a struct representing the accounts required for the function.Function Bodies:transfer_lamports: This function transfers a specified amount of lamports from one account to another using the system program's transfer instruction. It constructs the transfer instruction and invokes it using Anchor'sinvoke_signed function, passing required accounts and signers.transfer_spl_tokens: This function transfers a specified amount of SPL tokens from one account to another using the SPL token program's transfer instruction. It constructs the transfer instruction and invokes it using Anchor'stoken::transfer function, passing required accounts and amount.Account Structs:TransferLamports andSplTokenTransfer are account structs defined using the#[derive(Accounts)] attribute. These structs represent the accounts required by the respective transfer functions. Account structs define the accounts that the Solana program will interact with, including the types of those accounts (e.g.,Signer,Account,Program). Each field in the account struct corresponds to an account used by the program.Account Attributes:Account fields in the structs are decorated with attributes that specify their properties:#[account(mut)]: Indicates that the account is mutable, allowing the program to modify its data.#[account(signer)]: Specifies that the account must be a signer, providing authorization for transactions.To get started with Solana blockchain development or a project development on Solana, connect with our blockchain developers,
Category: Blockchain Development & Web3 Solutions
Creating a Tokenized Lootbox Game Smart Contract on Ethereum Within the quickly developing realm of blockchain gaming, tokenized loot boxes have become a fascinating fusion of virtual cash and gaming fun. With blockchain development services, you can create a unique gaming experience where players can purchase, earn, and open loot boxes containing a range of tokenized things by utilizing the Ethereum network and the ERC1155 multi-token standard. Using the "MyToken" contract as a foundational example, this blog post will walk you through the process of creating a tokenized loot-box game using the ERC1155 standard.Core Concept of the Tokenized Loot-box GameThe essence of our game revolves around loot boxes—special tokens that can contain an array of other tokenized items, ranging from common to rare. Players can acquire loot boxes through gameplay, purchases, or trading with other players. Opening a loot box reveals its contents, adding an element of surprise and anticipation to the gaming experience.The "MyToken" Contract: A BlueprintOur game's backbone is the "MyToken" smart contract, which implements the ERC1155 standard along with additional functionalities for managing loot boxes. Let's dissect the main components and functionalities that make this contract suitable for our game:ERC1155 Foundation: This allows for efficient handling of multiple token types, essential for distinguishing between different loot boxes and items within our game.LootBox Functions: These provide the game developers (contract owners) with exclusive rights to mint new items and loot boxes.Also, Check | Game On! Mastering Web3 Game DevelopmentPrerequisites:-Nodejs installedSolidity FamiliarityERC 1155 FamiliarityImplementing the Game LogicStep 1: Setting up the Hardhat EnvironmentCreate a new folder for the project.Use the following command:- npx hardhat And create a hardhat project.We can now create our solidity contract in the contract folder by deleting the lock.sol file provided by hardhat.run npm install @openzeppelin/contracts You may also like | A Guide on How to Develop a Dutch Auction Smart ContractStep 2: Smart Contract Development// SPDX-License-Identifier: MIT pragmasolidity^0.8.20; // Importing ERC1155, Ownable, and ERC1155Burnable contracts from OpenZeppelin. import"@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import"@openzeppelin/contracts/access/Ownable.sol"; import"@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; // MyToken is a contract that combines ERC1155 for multi-token functionality, // Ownable for ownership management, and ERC1155Burnable for token burnability. contract MyTokenis ERC1155, Ownable, ERC1155Burnable{ // Private variable to keep track of the next item ID. uint256private _nextItemId=1; // Private variable to keep track of the next loot box ID. uint256private _nextBoxId=1; // Struct representing a loot box, which contains arrays of item IDs and quantities. struct LootBox{ uint256[] itemIds; uint256[] itemQuantities; } // Mapping from loot box ID to LootBox struct, storing each loot box's details. mapping(uint256 => LootBox)private lootBoxes; // Event emitted when a loot box is opened. event LootBoxOpened(uint256indexed boxId,address opener); // Constructor sets the base URI for token metadata and transfers ownership. constructor(stringmemory uri,address initialOwner) ERC1155(uri) Ownable(initialOwner){ } // Function to create a new loot box. Only the owner can call this function. function createLootBox(uint256[]memory itemIds,uint256[]memory itemQuantities)public onlyOwner{ require(itemIds.length== itemQuantities.length,"Mismatch between item IDs and quantities"); uint256 newBoxId= _nextBoxId++; lootBoxes[newBoxId]= LootBox(itemIds, itemQuantities); } // Function to open a loot box, burning one instance of the box and minting one of the items inside. function openLootBox(uint256 boxId)public{ require(balanceOf(msg.sender, boxId)>0,"You do not own this loot-box"); _burn(msg.sender, boxId,1);// Burn one loot box from the caller's balance. LootBoxmemory box= lootBoxes[boxId]; // Generate a random index to determine which item to mint. uint256 randomIndex=uint256(keccak256(abi.encodePacked(block.timestamp,msg.sender)))% box.itemIds.length; uint256 itemId= box.itemIds[randomIndex]; uint256 quantity= box.itemQuantities[randomIndex]; _mint(msg.sender, itemId, quantity,"");// Mint the item to the caller. emit LootBoxOpened(boxId,msg.sender);// Emit an event for opening the loot box. } // Allows the owner to mint a new item directly to an address. function mintItem(address to,uint256 id,uint256 quantity,bytesmemory data)public onlyOwner{ _mint(to, id, quantity, data); } // Allows the owner to mint a new loot box and assigns it to an address. function mintLootBox(address to,uint256[]memory itemIds,uint256[]memory itemQuantities,uint256 quantity)public onlyOwner{ createLootBox(itemIds, itemQuantities);// Create the loot box first. _mint(to, _nextBoxId-1, quantity,"");// Then mint it to the specified address. } // Function for the owner to set a new URI for all token types. function setURI(stringmemory newuri)public onlyOwner{ _setURI(newuri); } // Function for the owner to mint tokens of a given ID and amount to a specified account. function mint(address account,uint256 id,uint256 amount,bytesmemory data)public onlyOwner{ _mint(account, id, amount, data); } // Function for the owner to mint multiple tokens of different IDs to a specified account. function mintBatch(address to,uint256[]memory ids,uint256[]memory amounts,bytesmemory data)public onlyOwner{ _mintBatch(to, ids, amounts, data); } } Create MyToken Contract:Create a new Solidity file namedMyToken.sol.Copy the provided Solidity code intoMyToken.sol.Understand Contract Components:ERC1155: This is a multi-token standard allowing the contract to manage multiple token types.Ownable: Provides basic authorization control functions, simplifying the implementation of "user permissions".ERC1155Burnable: Allows tokens to be burnt, removing them from the total supply.Implement Constructor: The constructor sets the base URI for token metadata and optionally transfers ownership to another address. Be prepared to provide the URI and the initial owner's address upon deployment.Implement Loot Box Logic:createLootBox: Allows the contract owner to create new loot boxes, each defined by arrays of item IDs and their quantities.openLootBox: Allows a loot box owner to open it, burning the loot box token and minting one of the items inside to the opener.Implement Minting Functions:mintItem: For the contract owner to mint a specific item directly to an address.mintLootBox: For the contract owner to mint a loot box (after creating it) directly to an address.mint: For minting tokens of a given ID to a specific account.mintBatch: Allows the owner to mint multiple types of tokens to a specified account in a single transaction.Utility Functions:setURI: To update the base URI for all token types.Private variables_nextItemId and_nextBoxId are used to keep track of the IDs for new items and loot boxes, ensuring uniqueness.Explore More | Smart Contract Development Using PythonStep 3: Minting Loot BoxesThe game developer mints a variety of loot boxes, each characterized by a unique ID and containing a predefined set of items. This step involves calling themintLootBox function to distribute the loot boxes to players.Step 4: Acquiring and Opening Loot BoxesPlayers acquire loot boxes through gameplay, purchases, or trades. To open a loot box, a player calls theopenLootBox function, which burns the loot box token and randomly selects an item from its contents to mint to the player's address.Also, Read | Creating a Staking Smart Contract on Solana using AnchorConclusionBuilding a tokenized loot-box game with ERC1155 offers a unique opportunity to blend blockchain technology with engaging gameplay elements. The "MyToken" contract serves as a solid foundation, showcasing how to manage loot boxes and items within a unified framework. By embracing this technology, developers can create rich, interactive gaming experiences that leverage the power of blockchain for transparency, ownership, and traceability of digital assets. As the blockchain gaming industry continues to grow, innovative applications like tokenized loot boxes will undoubtedly play a significant role in shaping its future. Interested in developing a similar project for a wider audience, get in touch with smart contract developers to get started.
Category: Blockchain Development & Web3 Solutions
How to Set Up Strapi and Deploy on Heroku Creating a Content Management System (CMS) using Strapi and deploying it on Heroku combines the flexibility of Strapi's headless CMS with the simplicity of Heroku's cloud platform. Strapi enables developers to design a customized CMS, defining content structures and APIs, while Heroku streamlines the deployment process, providing a scalable and managed environment for hosting the Strapi-based CMS. This synergy allows for efficient content management and delivery in a cloud-native and user-friendly ecosystem for your app development.What is Strapi?Strapi is an open-source, headless Content Management System (CMS) known for its flexibility and customization capabilities, allowing developers to design tailored admin panels, APIs, and database models. As a self-hosted solution built on modern technologies, Strapi is ideal for those seeking a customizable CMS for various projects, including web applications and mobile apps. Here are its key features:Headless CMSUnlike traditional CMS platforms like WordPress, Strapi is "headless". This means it doesn't dictate how content is presented but provides it as data over an API. This approach offers more flexibility in how the content is used, whether it's for websites, mobile apps, or other platforms.API-CentricStrapi is designed to build APIs quickly. It provides a powerful API to interact with your content.CustomizableStrapi is highly customizable for different use cases. It allows developers to tailor the admin panel, APIs, and database models according to their needs.Self-HostedStrapi is self-hosted, giving users complete control over their data and infrastructure.Technology StackIt's built using modern technologies such as Node.js, and React, and supports various databases like MongoDB, MySQL, SQLite, and PostgreSQL.Use CaseIt's ideal for developers looking to build project-specific CMS without the overhead of managing presentation layers, perfect for web applications, mobile apps, and more.You may also like | Native vs. Hybrid vs. Web | Choosing Mobile App DevelopmentWhat is Heroku?Heroku stands as a Platform-as-a-Service (PaaS) cloud platform, empowering developers to construct, execute, and manage applications seamlessly within the cloud environment. Vital elements of Heroku comprise:User-FriendlinessHeroku is renowned for its straightforwardness and user-friendly interface. It allows developers to deploy, manage, and scale applications with minimal configuration and hassle.Support for Multiple LanguagesHeroku supports several programming languages, including Ruby, Java, Node.js, Scala, Clojure, Python, PHP, and Go.Add-onsOffers a range of add-ons that provide various services like databases, monitoring, caching, and more, which can be easily integrated into applications.DeploymentSimplifies the deployment process with Git-based deployments and Docker container support.DynosApplications on Heroku run in lightweight, isolated environments called "dynos" that are abstracted from the underlying hardware.ScalabilityProvides easy scalability options, allowing applications to handle increased traffic by adjusting the number of active dynos.Managed InfrastructureHeroku manages hardware and servers, allowing developers to focus on their applications.Use CaseIdeal for startups and businesses that want to deploy applications quickly without dealing with infrastructure management.Also, Explore | Cross-platform Mobile App DevelopmentSteps to Set Up Strapi and Deploy on Heroku1. Local Setup with StrapiInstall Strapi:Use Node Package Manager (npm) to install Strapi.bashnpx create-strapi-app my-blog --quickstartThis command creates a new Strapi project in a folder namedmy-blog.Configure Strapi:After installation, Strapi's admin panel will open in your browser.Set up an admin account to manage your blog.Create Blog Content Types:Use the Content-Type Builder in Strapi's admin panel to define the structure of your blog content, such as 'Articles', 'Categories', 'Authors', etc.2. Preparing for Heroku DeploymentDatabase Configuration:Strapi's default SQLite database isn't suitable for Heroku. Configure Strapi to use a PostgreSQL database, which is more suitable for production and supported by Heroku.Environment Variables:Set up environment variables for database connections (likeDATABASE_URL) and other necessary settings. These variables will differ between your local environment and Heroku.Install Heroku CLI:Download and install the Heroku CLI tool, which allows you to manage your Heroku applications from the command line.3. Deploying to HerokuCreate a Heroku App:Log in to your Heroku dashboard and create a new app. Alternatively, use the Heroku CLI:bashheroku create my-strapi-blogConfigure Database on Heroku:Add a PostgreSQL database to your Heroku application. This can be done from the 'Resources' tab in your Heroku app dashboard.Set Environment Variables in Heroku:Transfer your local environment variables to Heroku. This can be done via the 'Settings' tab in your Heroku app dashboard or via the CLI:bashheroku config:set DATABASE_URL=your_database_urlDeploy Your Strapi App:Deploy your Strapi application to Heroku. If your code is hosted on GitHub, you can connect your GitHub repository for continuous deployment. Alternatively, you can deploy using Git:bashgit push heroku master4. Post-Deployment SetupInitialize Database:After deployment, you may need to run database migrations or seed the database.Access Strapi Admin Panel:Visithttps://my-strapi-blog.herokuapp.com/admin to access the Strapi admin panel on Heroku.5. Maintaining and UpdatingRegular Updates:Keep Strapi and its dependencies updated. Regularly pull changes from your repository and redeploy to Heroku.Monitoring:Use Heroku’s dashboard to monitor your app's performance and logs.Scaling:Scale your Heroku dynos as needed to handle increased traffic or background tasks.Also, Read | Hybrid App Development | An Introductory GuideConclusionIn the realm of modern web development, the combination of Strapi's headless Content Management System (CMS) and Heroku's cloud platform as a service (PaaS) presents a powerful synergy. Strapi's flexibility, customization options, and API-centric approach make it an ideal choice for developers seeking to create tailored CMS solutions. On the other hand, Heroku's simplicity, language support, and managed infrastructure alleviate the complexities of deployment and scaling, providing a seamless environment for hosting applications. If you are looking to develop a custom CMS or web or mobile app, connect with our app developers to get started.
A Guide to Gasless ERC20 Token Transfer Find out the step-by-step guide for gasless ERC20 token transfers in Erc-20 token development services to streamline transactions and foster accessibility in blockchain assets.Gasless ERC20 Token Transfers: Embracing Efficiency and AccessibilityIn the evolving world of blockchain technology, the implementation of gasless ERC20 token transfers stands out as a significant innovation. This approach utilizes meta-transactions to enhance the efficiency and accessibility of token transfers on the Ethereum network.What are ERC20 Tokens?ERC20 tokens are digital assets built on the Ethereum blockchain. They adhere to a specific set of standards, making them interoperable with various applications and services within the Ethereum ecosystem.Suggested Read | ERC-20 Token Standard | Development EssentialsThe Role of Meta TransactionsMeta transactions represent a pivotal shift in how blockchain transactions are processed. By allowing a third party to pay the transaction fees ('gas'), users can transfer ERC20 tokens without incurring these costs directly. This method significantly lowers the barrier to entry and makes transactions more user-friendly.Exploring the Solidity CodeIERC20Permit InterfaceTheIERC20Permit interface extends the standard ERC20 functionalities, introducing thepermit function. This function is crucial for authorizing third-party transactions without the token holder incurring gas fees.GaslessTokenTransfer ContractTheGaslessTokenTransfer contract leverages thepermit method to enable gasless transactions. Thesend function within this contract allows the transfer of tokens from a sender to a receiver, with the transaction fee being handled externally.ERC20Permit ContractTheERC20Permit contract is a concrete implementation of the ERC20 standard, incorporating the EIP-2612 standard. This includes thepermit function, allowing for more flexible and efficient token transfers.Example Code:// SPDX-License-Identifier: MIT pragmasolidity^0.8.18; interface IERC20Permit{ function totalSupply()externalviewreturns(uint256); function balanceOf(address account)externalviewreturns(uint256); function transfer(address recipient,uint256 amount)externalreturns(bool); function allowance(address owner,address spender)externalviewreturns(uint256); function approve(address spender,uint256 amount)externalreturns(bool); function transferFrom( address sender, address recipient, uint256 amount )externalreturns(bool); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s )external; event Transfer(addressindexed from,addressindexed to,uint256 value); event Approval(addressindexed owner,addressindexed spender,uint256 value); } contract GaslessTokenTransfer{ function send( address token, address sender, address receiver, uint256 amount, uint256 fee, uint256 deadline, // Permit signature uint8 v, bytes32 r, bytes32 s )external{ // Permit IERC20Permit(token).permit( sender, address(this), amount+ fee, deadline, v, r, s ); // Send amount to receiver IERC20Permit(token).transferFrom(sender, receiver, amount); // Take fee - send fee to msg.sender IERC20Permit(token).transferFrom(sender,msg.sender, fee); } } /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstractcontract ERC20{ /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(addressindexed from,addressindexed to,uint256 amount); event Approval(addressindexed owner,addressindexed spender,uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ stringpublic name; stringpublic symbol; uint8publicimmutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256public totalSupply; mapping(address =>uint256)public balanceOf; mapping(address =>mapping(address =>uint256))public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256internalimmutable INITIAL_CHAIN_ID; bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR; mapping(address =>uint256)public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(stringmemory _name,stringmemory _symbol,uint8 _decimals){ name= _name; symbol= _symbol; decimals= _decimals; INITIAL_CHAIN_ID=block.chainid; INITIAL_DOMAIN_SEPARATOR= computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender,uint256 amount)publicvirtualreturns(bool){ allowance[msg.sender][spender]= amount; emit Approval(msg.sender, spender, amount); returntrue; } function transfer(address to,uint256 amount)publicvirtualreturns(bool){ balanceOf[msg.sender]-= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked{ balanceOf[to]+= amount; } emit Transfer(msg.sender, to, amount); returntrue; } function transferFrom( address from, address to, uint256 amount )publicvirtualreturns(bool){ uint256 allowed= allowance[from][msg.sender];// Saves gas for limited approvals. if(allowed!=type(uint256).max) allowance[from][msg.sender]= allowed- amount; balanceOf[from]-= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked{ balanceOf[to]+= amount; } emit Transfer(from, to, amount); returntrue; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s )publicvirtual{ require(deadline>=block.timestamp,"PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked{ address recoveredAddress=ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require( recoveredAddress!=address(0)&& recoveredAddress== owner, "INVALID_SIGNER" ); allowance[recoveredAddress][spender]= value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR()publicviewvirtualreturns(bytes32){ return block.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator()internalviewvirtualreturns(bytes32){ return keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to,uint256 amount)internalvirtual{ totalSupply+= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked{ balanceOf[to]+= amount; } emit Transfer(address(0), to, amount); } function _burn(address from,uint256 amount)internalvirtual{ balanceOf[from]-= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked{ totalSupply-= amount; } emit Transfer(from,address(0), amount); } } contract ERC20Permitis ERC20{ constructor( stringmemory _name, stringmemory _symbol, uint8 _decimals ) ERC20(_name, _symbol, _decimals){} function mint(address to,uint256 amount)public{ _mint(to, amount); } }Advantages of Gasless Token TransfersCost Efficiency: Users can transfer tokens without the burden of gas fees.User Accessibility: Lowers the entry barrier for new users unfamiliar with the complexities of gas fees.Enhanced Transaction Flow: Streamlines the process, making it more appealing for regular use.Check It Out | ERC-20 Token Standard | Things You Must KnowConclusionThe integration of gasless ERC20 token transfers marks a significant stride in blockchain technology, offering a more accessible and efficient means of handling digital assets. As blockchain matures, innovations like these play a critical role in shaping its future, making it more inclusive and user-friendly.Interested in ERC20 token development? Connect with our Ethereum developers today to get started.
Category: Blockchain Development & Web3 Solutions
How to Create a MultiSig Wallet in Solidity Creating a Multi-Sig Wallet in SolidityA multi-signature (multi-sig) wallet is like a safe that needs multiple keys to open. It's a smart contract that holds cryptocurrency and requires more than one person's approval to make transactions. Today, we'll delve into creating a multi-sig wallet using Hardhat, a popular Ethereum development environment. Creating a multi-sig wallet requires significant expertise in smart contract development.PrerequisitesA basic understanding of Solidity and Ethereum.Node.js is installed on your machine.Hardhat is installed globally using the command:npm install -g hardhat.Setting Up HardhatCreating a New Project: Runnpx hardhat in your terminal and follow the prompts to create a new project.Installing Dependencies: Inside your project directory, install the necessary npm packages with:bashnpm install @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethersWriting the Multi-Sig Wallet Smart ContractCreate a new file calledMultiSigWallet.sol in thecontracts folder.Paste the below code// SPDX-License-Identifier: MIT pragmasolidity^0.8.20; contract MultiSigWallet{ event Deposit(addressindexed sender,uint amount,uint balance); event SubmitTransaction( addressindexed owner, uintindexed txIndex, addressindexed to, uint value, bytes data ); event ConfirmTransaction(addressindexed owner,uintindexed txIndex); event RevokeConfirmation(addressindexed owner,uintindexed txIndex); event ExecuteTransaction(addressindexed owner,uintindexed txIndex); address[]public owners; mapping(address =>bool)public isOwner; uintpublic numConfirmationsRequired; struct Transaction{ address to; uint value; bytes data; bool executed; uint numConfirmations; } // mapping from tx index => owner => bool mapping(uint =>mapping(address =>bool))public isConfirmed; Transaction[]public transactions; modifier onlyOwner(){ require(isOwner[msg.sender],"not owner"); _; } modifier txExists(uint _txIndex){ require(_txIndex< transactions.length,"tx does not exist"); _; } modifier notExecuted(uint _txIndex){ require(!transactions[_txIndex].executed,"tx already executed"); _; } modifier notConfirmed(uint _txIndex){ require(!isConfirmed[_txIndex][msg.sender],"tx already confirmed"); _; } constructor(address[]memory _owners,uint _numConfirmationsRequired){ require(_owners.length>0,"owners required"); require( _numConfirmationsRequired>0&& _numConfirmationsRequired<= _owners.length, "invalid number of required confirmations" ); for(uint i=0; i< _owners.length; i++){ address owner= _owners[i]; require(owner!=address(0),"invalid owner"); require(!isOwner[owner],"owner not unique"); isOwner[owner]=true; owners.push(owner); } numConfirmationsRequired= _numConfirmationsRequired; } receive()externalpayable{ emit Deposit(msg.sender,msg.value,address(this).balance); } function submitTransaction( address _to, uint _value, bytesmemory _data )public onlyOwner{ uint txIndex= transactions.length; transactions.push( Transaction({ to: _to, value: _value, data: _data, executed:false, numConfirmations:0 }) ); emit SubmitTransaction(msg.sender, txIndex, _to, _value, _data); } function confirmTransaction( uint _txIndex )public onlyOwner txExists(_txIndex) notExecuted(_txIndex) notConfirmed(_txIndex){ Transactionstorage transaction= transactions[_txIndex]; transaction.numConfirmations+=1; isConfirmed[_txIndex][msg.sender]=true; emit ConfirmTransaction(msg.sender, _txIndex); } function executeTransaction( uint _txIndex )public onlyOwner txExists(_txIndex) notExecuted(_txIndex){ Transactionstorage transaction= transactions[_txIndex]; require( transaction.numConfirmations>= numConfirmationsRequired, "cannot execute tx" ); transaction.executed=true; (bool success,)= transaction.to.call{value: transaction.value}( transaction.data ); require(success,"tx failed"); emit ExecuteTransaction(msg.sender, _txIndex); } function revokeConfirmation( uint _txIndex )public onlyOwner txExists(_txIndex) notExecuted(_txIndex){ Transactionstorage transaction= transactions[_txIndex]; require(isConfirmed[_txIndex][msg.sender],"tx not confirmed"); transaction.numConfirmations-=1; isConfirmed[_txIndex][msg.sender]=false; emit RevokeConfirmation(msg.sender, _txIndex); } function getOwners()publicviewreturns(address[]memory){ return owners; } function getTransactionCount()publicviewreturns(uint){ return transactions.length; } function getTransaction( uint _txIndex ) public view returns( address to, uint value, bytesmemory data, bool executed, uint numConfirmations ) { Transactionstorage transaction= transactions[_txIndex]; return( transaction.to, transaction.value, transaction.data, transaction.executed, transaction.numConfirmations ); } } Compiling Your Smart Contract:In your terminal, runnpx hardhat compile.Contract DetailsContract SetupDefined usingpragma solidity ^0.8.20; to specify the Solidity compiler version.contract SecureMultiWallet { ... } initiates the contract definition.Event DefinitionsEvents such asFundsDeposited,TransactionSubmitted,TransactionConfirmed,ConfirmationRevoked, andTransactionExecuted are defined to emit logs for significant actions within the contract.State VariablesauthorizedUsers is an array to track wallet signatories.isAuthorized is a mapping to quickly verify if an address is authorized.requiredApprovals specifies the number of approvals needed to execute a transaction.pendingTransactions is an array to store all proposed transactions.hasConfirmed is a nested mapping to keep track of approvals per transaction.Struct DefinitionPendingTransaction struct is defined to hold information about each proposed transaction.ModifiersonlyAuthorized ensures the function is called by an authorized user.transactionExists checks if the transaction ID exists.notYetExecuted checks if the transaction has not been executed yet.notYetConfirmed checks if the transaction has not already been approved by the caller.ConstructorThe constructor initializes the contract with a list of authorized users and the required number of approvals.Fallback FunctionThereceive function allows the contract to accept ether and emits aFundsDeposited event.Transaction Management FunctionsaddTransaction: Allows an authorized user to propose a new transaction.approveTransaction: Allows an authorized user to approve a proposed transaction.runTransaction: Allows an authorized user to execute a transaction once the required number of approvals have been met.retractApproval: Allows an authorized user to retract their approval from a proposed transaction.View FunctionslistUsers: Allows anyone to query the list of authorized users.countTransactions: Allows anyone to query the total number of proposed transactions.fetchTransaction: Allows anyone to query details of a specific transaction by its ID.Github: https://github.com/AshishG2/MultiSigSolidityIf you are looking for smart contract development or crypto wallet development, connect with our skilled smart contract developers to get started.