aelf 백서

bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 41쪽 중 35쪽

sitory < TransferRecord > repository , [ FromServices ] IObjectMapper objectMapper , GetTransferRecordInput input ) { var queryable = await repository . GetQueryableAsync ( ) ; queryable = queryable . Where ( a => a . Metadata . ChainId == input . ChainId ) ; if ( ! input . Address . IsNullOrWhiteSpace ( ) ) { queryable = queryable . Where ( a => a . ToAddress == input . Address ) ; } // Filter by Symbol, ensuring it matches the NFT pattern if ( ! input . Symbol . IsNullOrWhiteSpace ( ) ) { queryable = queryable . Where ( a => a . Symbol == input . Symbol && a . Symbol . Contains ( "-" ) ) ; } // Ensure NFT-specific conditions queryable = queryable . Where ( a => a . Symbol . Contains ( "-" ) && a . Amount > 0 ) ; var transferRecords = queryable . OrderBy ( o => o . Metadata . Block . BlockHeight ) . ToList ( ) ; return objectMapper . Map < List < TransferRecord > , List < TransferRecordDto > > ( transferRecords ) ; } } Register log event processor Modify src/NFTAeIndexer/NFTAeIndexerModule.cs to register NFTTransferredProcessor. NFTAeIndexerModule.cs using AeFinder . Sdk . Processor ; using NFTAeIndexer . GraphQL ; using NFTAeIndexer . Processors ; using GraphQL . Types ; using Microsoft . Extensions . DependencyInjection ; using Volo . Abp . AutoMapper ; using Volo . Abp . Modularity ; namespace NFTAeIndexer ; public class NFTAeIndexerModule : AbpModule { public override void ConfigureServices ( ServiceConfigurationContext context ) { Configure < AbpAutoMapperOptions > ( options => { options . AddMaps < NFTAeIndexerModule > ( ) ; } ) ; context . Services . AddSingleton < ISchema , AeIndexerSchema > ( ) ; // Add your LogEventProcessor implementation. context . Services . AddSingleton < ILogEventProcessor , NFTTransferredProcessor > ( ) ; } } Add entity mapping Modify src/NFTAeIndexer/NFTAeIndexerAutoMapperProfile.cs and add entity mapping code. NFTAeIndexerAutoMapperProfile.cs using NFTAeIndexer . Entities ; using NFTAeIndexer . GraphQL ; using AutoMapper ; namespace NFTAeIndexer ; public class NFTAeIndexerAutoMapperProfile : Profile { public NFTAeIndexerAutoMapperProfile ( ) { CreateMap < Account , AccountDto > ( ) ; CreateMap < TransferRecord , TransferRecordDto > ( ) ; } } Building code ​ Use the following command in the code directory to compile the code. dotnet build -c Release Step 4 - Deploy AeIndexer ​ Open the AeIndexer details page and click Deploy. Fill out the subscription manifest and upload the DLL file. Subscription manifest: { "subscriptionItems" : [ { "chainId" : "tDVW" , "startBlockNumber" : 151018963 , "onlyConfirmed" : false , "transactions" : [ ] , "logEvents" : [ { "contractAddress" : "ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx" , "eventNames" : [ "Issued" , "Issue" ] } ] } ] } DLL file location: src/NFTAeIndexer/bin/Release/net8.0/NFTAeIndexer.dll Click the deploy button to submit deployment information. When the normal processing block information appears on the Logs page at the bottom of the details page, it means that the deployment has been successful and data indexing has started. Step 5 - Query indexed data ​ Through the Playground page below the details page, you can use GraphQL syntax to query the indexed data information. Enter the query statement on the left, and the query results will be displayed on the right. query{ account(input: { chainId: "tDVW", address: "2AaBGTi2cJLWtEXR7w96hzun4qVz2KnsGZ1XfqErhKTgDj9Q8x"}) { symbol, amount, address, tokenName, nftImageUri, nftAttributes, metadata { chainId, block { blockHeight } } } } tip ℹ️ Note: For the complete demo code, please visit AeFinder github to download. https://github.com/AeFinderProject/aefinder/tree/master/samples/TokenAeIndexer Step 6 - Getting NFT Seed ​ In order to create an NFT collection on the aelf blockchain, the deployer wallet must have an NFT SEED . Visit NFT Faucet to get your NFT SEED. After the request is successfully processed, the requestor wallet will receive the SEED . Please note this SEED symbol value, as it will be needed later while creating the NFT collection and NFT generation. This will become our Token Symbol . Step 7 - Interact with Smart Contract through frontend dApp ​ Project Setup ​ Let's start by cloning the frontend project repository from GitHub. Run the following command your Terminal: Terminal git clone https://github.com/AElfProject/aelf-samples.git Next, navigate to the frontend project directory with this command: Terminal cd aelf-samples/advance/nft/2-dapp Once you're in the 2-dapp directory, open the project with your preferred IDE (e.g., VSCode). You should see the project structure as shown below. Install necessary packages and libraries ​ Run teh following command in the terminal: Linux and macOs Windows Terminal sudo npm install Command Prompt npm install We are now ready to build the frontend components of our NFT dApp. Configure Portkey Provider & Write Connect Wallet Function ​ We'll set up our Portkey provider to allow users to connect their Portkey wallets to our app and interact with the aelf smart contracts. We'll be interacting with the already deployed multi-token contract for this tutorial. Step 1. Locate the File: Go to the src/hooks/useNFTSmartContract.ts file. Step 2. Fetch the Smart Contract: Find the comment //Step A - Function to fetch a smart contract based on the chain symbol and the contract address. Replace the existing fetchContract function with this updated code: useNFTSmartContract.ts //Step A - Function to fetch a smart contract based on the chain symbol and the contract address const fetchContract = async ( symbol : "AELF" | "tDVW" , contractAddress : string ) => { try { // If no provider is available, return null if ( ! provider ) return null ; // Fetch the chain information using the provider const chain = await provider . getChain ( symbol ) ; if ( ! chain ) throw new Error ( "Chain not found" ) ; // Get the smart contract instance from the chain const contract = chain . getContract ( contractAddress ) ; // Return the smart contract instance return contract ; } catch ( error ) { console . error ( "Error in fetchContract" , { symbol , contractAddress , error } ) ; } } ; Explanation: fetchContract Function : This function fetches a smart contract based on the given chain symbol (e.g., "AELF" or "tDVW") and the contract address. Check Provider : If no provider is available, the function returns null. Fetch Chain : The function fetches chain information using the provider. Get Contract : It retrieves the smart contract instance from the chain. Error Handling : If an error occurs, it logs the error to the console. Step 3. Initialize and Fetch the Smart Contracts: Find the comment // Step B - Effect hook to initialize and fetch the smart contracts when the provider changes. Replace the existing useEffect hook with this updated code: useNFTSmartContract.ts // Step B - Effect hook to initialize and fetch the smart contracts when the provider changes useEffect ( ( ) => { ( async ( ) => { // Fetch the MainChain Testnet Contract const mainChainContract = await fetchContract ( "AELF" , "JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE" ) ; setMainChainSmartContract ( mainChainContract as IContract ) ; // Fetch the dAppChain Testnet Contract const sideChainContract = await fetchContract ( "tDVW" , "ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx" ) ; setSideChainSmartContract ( sideChainContract as IContract ) ; } ) ( ) ; } , [ provider ] ) ; // Dependency array ensures this runs when the provider changes Explanation: useEffect Hook : This hook initializes and fetches the smart contracts when the provider changes. Check Provider : If no provider is available, the function returns null. Fetch Contracts : It fetches and sets the smart contracts for the main chain, side chain, and cross chain. MainChain Contract : Fetches the MainChain Testnet Contract and sets it in the state. dAppChain Contract : Fetches the dAppChain Testnet Contract and sets it in the state. By following these steps, we'll configure the Portkey provider to connect users' wallets to your app and interact with the multi-token smart contract including NFT related functionalities. This setup will enable our frontend components to perform actions like create NFTs , validate NFTs , and transfer NFTs . Configure Connect Wallet Function ​ Step 1: Locate the File Go to the src/components/layout/header/index.tsx file. Step 2: Write the Connect Wallet Function The header/index.tsx file is the header of our NFT dApp. It allows users to connect their Portkey wallet with the NFT dApp. Before users can interact with the smart contract, we need to write the Connect Wallet function. Find the comment // Step C - Connect Portkey Wallet . Replace the existing connect function with this code snippet: header/index.tsx const connect = async ( walletProvider ? : IPortkeyProvider ) => { // Step C - Connect Portkey Wallet const accounts = await ( walletProvider ? walletProvider : provider ) ?. request ( { method : MethodsBase . REQUEST_ACCOUNTS , } ) ; const account = accounts ?. AELF && accounts ?. AELF [ 0 ] ; if ( account ) { setCurrentWalletAddress ( account . replace ( / ^ ELF_ / , "" ) . replace ( / _AELF $ / , "" ) ) ; setIsConnected ( true ) ; } ! walletProvider && toast . success ( "Successfully connected" ) ; } ; Explanation: connect Function : This function connects the user's Portkey wallet with the dApp. Fetch Accounts : It fetches the wallet accounts using the provider. Log Accounts : Logs the accounts to the console for debugging. Set Wallet Address : Sets the current wallet address state variable with the fetched account. Update Connection Status : Updates the state to indicate that the wallet is connected. User Notification : Displays an alert to notify the user that their wallet is successfully connected. In this code, we fetch the Portkey wallet account using the provider and update the wallet address state variable. An alert notifies the user that their wallet is successfully connected. With the Connect Wallet function defined, we're ready to write the remaining functions in the next steps. Configure Create NFT Form Code ​ Step 1: Locate the File Go to the src/pages/create-nft/index.tsx file. This file is the Create NFTs page where users can enter details like the tokenName , symbol , totalSupply and decimals . Step 2: Prepare Form to Create NFTs Find the comment // Step D - Configure NFT Form . Replace the form variable with this code snippet: create-nft/index.tsx // Step D - Configure NFT Form const form = useForm < z . infer < typeof formSchema >> ( { resolver : zodResolver ( formSchema ) , defaultValues : { tokenName : "" , symbol : "" , totalSupply : "" , decimals : "" , } , } ) ; Here's what the function does: ​ Initializes a new form variable with default values needed to create an NFT. Fields include: tokenName , symbol , totalSupply , and decimals . Now the form is ready for users to fill in the necessary details for their NFT function interaction. Create NFT Collection ​ Let's write the functions to Create New NFT Collection on the aelf mainchain and the dAppChain. Step 1: Write the function to Create New NFT Collection on the MainChain The create-nft/index.tsx file includes the code to create NFTs. It allows users to create new NFTs. Find the comment // step 1 - Create New NFT Collection on the mainchain . Replace the existing createNftCollectionOnMainChain function with this code snippet: create-nft/index.tsx // step - 1 Create New NFT Collection on the mainchain const createNftCollectionOnMainChain = async ( values : z . infer < typeof formSchema > ) : Promise < INftInput | "error" > => { let createLoadingId ; try { createLoadingId = toast . loading ( "Creating NFT Collection.." ) ; // Create an object with the necessary information for the new NFT collection. const createNftInput : INftInput = { tokenName : values . tokenName , // Name of the nft Collection symbol : values . symbol , // Symbol of the token (You have to get it from your PortKey wallet on NFT seed from NFT section) totalSupply : values . totalSupply , // Total supply of the token decimals : values . decimals , // Decimals of the token issuer : currentWalletAddress , // Address of the token issuer isBurnable : true , // Indicates if the token can be burned issueChainId : sidechain_from_chain_id , // ID of the issuing chain owner : currentWalletAddress , // Owner's wallet address } ; // Call the smart contract method to create the new NFT collection on the main chain. const result = await mainChainSmartContract ?. callSendMethod ( "Create" , currentWalletAddress , createNftInput ) ; // Log the result of the creation for debugging purposes. console . log ( "========= result of createNewNft =========" , result ) ; toast . update ( createLoadingId , { render : "NFT Collection Created Successfully On MainChain" , type : "success" , isLoading : false , } ) ; removeNotification ( createLoadingId ) ; // Return the input data for further use. return createNftInput ; } catch ( error ) { handleError ( createLoadingId as number , error ) ; return "error" ; } } ; tip ℹ️ Note: You need to get symbol from the Portkey wallet. Follow Steps to get NFT symbol from Portkey Wallet: Open Portkey Wallet. Go to the NFTs tab. You will find the SEED that you already got from the above seed generation step. Click on the SEED to see details. You will find the Token Symbol inside the Token Creation via This Seed section. Copy and use that value of the token symbol. What This Function Does: ​ Creates an Object with NFT Details : It prepares the data needed to create a new NFT collection. Calls Smart Contract Method : It interacts with the blockchain smart contract to create the new NFT collection using the prepared data. Logs Information : It logs the result for debugging purposes. Return Values of Object : It returns necessary values as a object. Next, we'll write the Validate Collection Info Exist function. Step 2: Write the validates collection info exist function Scroll up to find the comment // step 2 - Validate Collection information exist . Replace the existing validateNftCollectionInfo function with this code snippet: create-nft/index.tsx // step 2 - Validate Collection information exist // This function validates if the token collection information already exists on the main blockchain. const validateNftCollectionInfo = async ( values : INftInput ) : Promise < ValidationResult | "error" > => { let validateLoadingId ; try { // Start loading before initiating the transaction validateLoadingId = toast . loading ( < CustomToast title = " Transaction is getting validated on aelf blockchain. Please wait! " message = " Validation means transaction runs through a consensus algorithm to be selected or rejected. Once the status changes, the process will complete. It usually takes some time in distributed systems. " /> ) ; // Create an object with the necessary information for token validation. const validateInput : INftInput = { symbol : values . symbol , // Symbol of the token tokenName : values . tokenName , // Name of the token totalSupply : values . totalSupply , // Total supply of the token decimals : values . decimals , // Decimals of the token issuer : currentWalletAddress , // Address of the token issuer isBurnable : true , // Indicates if the token can be burned issueChainId : sidechain_from_chain_id , // ID of the issuing chain owner : currentWalletAddress , // Owner's wallet address } ; // Get mainnet contract const aelfTokenContract = await getTokenContract ( aelf , wallet ) ; // Prepare to sign the transaction using the contract method const signedTx = aelfTokenContract . ValidateTokenInfoExists . getSignedTx ( validateInput ) ; // Send the transaction using the signed transaction const { TransactionId : VALIDATE_TXID } = await aelf . chain . sendTransaction ( signedTx ) ; // Get the validation result let VALIDATE_TXRESULT = await aelf . chain . getTxResult ( VALIDATE_TXID ) ; // Wait until the latest index height is greater than or equal to the transaction block number let heightDone = false ; while ( ! heightDone ) { await delay ( 5000 ) ; // Wait 5 seconds before checking again // Get the latest index height const sideIndexMainHeight = await GetParentChainHeight ( ) ; if ( Number ( sideIndexMainHeight ) >= VALIDATE_TXRESULT . Transaction . RefBlockNumber ) { VALIDATE_TXRESULT = await aelf . chain . getTxResult ( VALIDATE_TXID ) ; heightDone = true ; } } console . log ( "VALIDATE_TXRESULT" , VALIDATE_TXRESULT ) ; // Update the loading message toast . update ( validateLoadingId , { render : "Validating Token Successfully Executed" , type : "success" , isLoading : false , } ) ; // Remove the loading message removeNotification ( validateLoadingId ) ; // Return necessary details return { transactionId : VALIDATE_TXID , signedTx : signedTx , BlockNumber : VALIDATE_TXRESULT . BlockNumber , } ; } catch ( error ) { handleError ( validateLoadingId as number , error ) ; return "error" ; } } ; What This Function Does: ​ Creates an Object with Validate Collection Details : It prepares the data needed to validate the token information. Calls Smart Contract Method : It interacts with the multi-token smart contract method to check if the token information already exists using the prepared data. Return Values : It returns necessary values as an object. Next, we'll write the Get the parent chain height function. Step 3: Write the get the parent chain height function Scroll up to find the comment // Step 3: Get the parent chain height . Replace the existing GetParentChainHeight function with the following code snippet: create-nft/index.tsx // Step 3: Get the parent chain height // This function fetches the current height of the parent blockchain. const GetParentChainHeight = async ( ) => { try { const tdvwCrossChainContract = await getCrossChainContract ( tdvw , wallet ) ; // Call the smart contract method to get the parent chain height. const result = await tdvwCrossChainContract . GetParentChainHeight . call ( ) // Return the parent chain height if it exists, otherwise return an empty string. return result ? ( result . value as string ) : "" ; } catch ( error ) { // If there's an error, log it and return an error status. console . error ( error , "=====error in GetParentChainHeight" ) ; return "error" ; } } ; What This Function Does: ​ Calls Smart Contract Method : It interacts with the side chain smart contract method to fetch the current height of the parent blockchain. Returns Parent Chain's Height : It returns the parent chain's height if it exists. Next, we'll write the Fetch the Merkle path function. Step 4: Write the fetch the merkle path function Scroll up to find the comment // step 4 - Fetch the merkle path by transaction Id . Replace the existing getMerklePathByTxId function with this code snippet: create-nft/index.tsx // step 4 - Fetch the merkle path by transaction Id const getMerklePathByTxId = async ( aelf : AElf , txId : string ) => { try { const { MerklePathNodes } = await aelf . chain . getMerklePathByTxId ( txId ) ; const formattedMerklePathNodes = MerklePathNodes . map ( ( { Hash , IsLeftChildNode , } : { Hash :
프로젝트로 돌아가기