aelf 백서

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

setTimeout ( ( ) => { for ( let i = 0 ; i < 8 ; i ++ ) { particleTypes . forEach ( ( type ) => { const offsetX = ( Math . random ( ) - 0.5 ) * 20 ; const offsetY = ( Math . random ( ) - 0.5 ) * 20 ; createParticle ( x + offsetX , y + offsetY , container , type ) ; } ) ; } } , wave * 100 ) ; } } , [ createParticle ] , ) ; Step 5: Displays the result Find the comment // Step M - Displays the result message based on the game outcome. . Replace the existing handleGameResult fucntion with this code snippet: page.tsx // Step M - Displays the result message based on the game outcome. const handleGameResult = ( isWin : boolean , amount : string ) => { const number = new Bignumber ( amount ) . div ( 10 ** 8 ) ; let message = ` Lose ${ number } ELF. Better luck next time! ` ; if ( isWin ) { message = ` Congratulations! You Win ${ number . multipliedBy ( 2 ) } ELF! ` ; } setMessage ( message ) ; } ; Step 6: Rolls the dice Find the comment // Step N - Rolls the dice . Replace the existing rollDice fucntion with this code snippet: page.tsx // Step N - Rolls the dice const rollDice = async ( multiplier = 1 ) => { setMessage ( '' ) ; if ( isRolling ) return ; setIsRolling ( true ) ; setMessage ( '' ) ; const sequenceNumber = Math . floor ( Math . random ( ) * 3 ) + 1 ; setSequence ( sequenceNumber ) ; new Audio ( 'https://www.soundjay.com/misc/sounds/dice-roll-01.mp3' ) . play ( ) . catch ( ( ) => { } ) ; const startTimePlay = Date . now ( ) ; try { setIsPlaying ( true ) ; await play ( multiplier ) ; } catch ( error : any ) { console . log ( 'play failed: ' , error ) ; setIsPlaying ( false ) ; setIsRolling ( false ) ; alert ( error . message ) ; return ; } const startTime = Date . now ( ) ; const getPlayInfoInterval = setInterval ( async ( ) => { const result = await getPlayerInfo ( ) ; if ( result . pending === false ) { // if (result.score !== prePlayScore) { clearInterval ( getPlayInfoInterval ) ; setPlayerInfo ( { ... playerInfo , ... result , } ) ; playerInfoGlobal = result ; if ( result . win ) { handleGameResult ( true , result . amount ) ; } else { handleGameResult ( false , result . amount ) ; } const finalDice1 = parseInt ( playerInfoGlobal . dice1 , 10 ) ; const finalDice2 = parseInt ( playerInfoGlobal . dice2 , 10 ) ; setDice1 ( finalDice1 ) ; setDice2 ( finalDice2 ) ; setIsRolling ( false ) ; setIsShaking ( true ) ; setIsPlaying ( false ) ; // Get dice positions for particle effects const dice1El = document . querySelector ( '.dice-container:first-child' ) ; const dice2El = document . querySelector ( '.dice-container:last-child' ) ; if ( dice1El ) { const rect1 = dice1El . getBoundingClientRect ( ) ; spawnParticles ( rect1 . left + rect1 . width / 2 , rect1 . top + rect1 . height / 2 , ) ; } if ( dice2El ) { const rect2 = dice2El . getBoundingClientRect ( ) ; spawnParticles ( rect2 . left + rect2 . width / 2 , rect2 . top + rect2 . height / 2 , ) ; } // Remove screen shake after animation setTimeout ( ( ) => setIsShaking ( false ) , 500 ) ; console . log ( 'Time used: ' , Date . now ( ) - startTime ) ; console . log ( 'Time used with play: ' , Date . now ( ) - startTimePlay ) ; } } , 500 ) ; } ; Now that we've written all the necessary frontend functions and hooks, we're ready to run the Dice Game in the next step. Run Application ​ In this step, we will run the Dice Game dApp. To begin, run the following command on your terminal. Terminal npm run dev info Note : Ensure that you are running this command under the todo/2-dapp folder. You should observe the following as shown below. Upon clicking on the localhost URL , you should be directed to the Dice Game landing page as shown below. Create Portkey Wallet ​ info Portkey is the first AA wallet from aelf's ecosystem, migrating users, developers and projects from Web2 to Web3 with DID solution. Users can swiftly log into Portkey via their Web2 social info with no private keys or mnemonics required. Underpinned by social recovery and decentralized guardian design, Portkey safeguards users' assets from centralized control and theft. Portkey has a unique payment delegation mechanism which enables interested parties to function as delegatees to pay for user activities on users' behalf. This means that users can create accounts for free and fees for other usages may also be covered in Portkey. Portkey also provides crypto on/off-ramp services, allowing users to exchange fiat with crypto freely. It supports the storage and management of various digital assets such as tokens, NFTs, etc. The compatibility with multi-chains and seamless connection to all kinds of DApps makes Portkey a great way to enter the world of Web3. With DID solution as its core, Portkey provides both Portkey Wallet and Portkey SDKs. For more information, you may visit the official documentation for Portkey at https://doc.portkey.finance/ . Download the Chrome extension for Portkey from https://chromewebstore.google.com/detail/portkey-wallet/iglbgmakmggfkoidiagnhknlndljlolb . info The Portkey extension supports Chrome browser only (for now). Please ensure that you are using Chrome browser. You may download Chrome from https://www.google.com/intl/en_sg/chrome/ . Once you have downloaded the extension, you should see the following on your browser as shown below. Click on Get Start and you should see the following interface as shown below. Sign up Switch to aelf Testnet network by selecting it: danger Please make sure you are using aelf Testnet in order to be able to receive your testnet tokens from the Faucet. Proceed to sign up with a Google Account or your preferred login method and complete the necessary accounts creation prompts and you should observe the following interface once you have signed up. With that, you have successfully created your very first Portkey wallet within seconds. How easy was that? info It is highly recommended to pin the Portkey wallet extension for easier access and navigation to your Portkey wallet! Next, click on ‘Open Portkey’ and you should now observe the following as shown below. Claim Test Token Click on Copy icon in right side in the top as shown in the below image and copy your wallet address. Open Faucet site : https://faucet-ui.aelf.dev Enter your wallet address and submit the form after verify the captcha. ELF token will be claimed in your account successfully. . We’ve completed all the steps to set up the wallet and claim the testnet ELF tokens. Now, let’s move on to the wallet connection process in the next step. Connect Portkey Wallet Open your Running project and click on "Login" to connect your Portkey wallet. You will get login popup so Click on "Connect Wallet" option as shown below. Next, you will see a list of wallet options. Select the "Portkey" wallet from the list. You will get the connection request on your Portkey wallet Next. Click on Approve button on request modal. After successfully connection with Portkey wallet, you will get the wallet info in Dice game page. With the wallet connection setup complete, we’re now ready to start playing the game! Play Dice Game Let's start to play the dice game with step by step. As you can see we have 3 token price option to play with ELF token. i.e: 0.1 ELF, 0.2 ELF and 0.4 ELF so let's play with 0.1 ELF tokens. Click on "0.1 ELF" button to play the game with Token. Game will start after click on "0.1 ELF" button and PROCESSING... message will be appear as below. The first time you perform a transaction with ELF tokens, you will receive a Token Allowance request in your wallet. Click on "Pre-athorize " button to authenticate and approve the Allowance transaction as shown below. After the successfully transaction of Allowance, your will get Play transaction request as shown below. Click on Sign the transaction. After the successfully transaction of Play game, you will get the game result in Dice game. success 🎉 Congratulations Learners! You have successfully built the Dice game 🎲🎲 🎯 Conclusion ​ 🎉 Congratulations on completing the Dice Game dApp tutorial! 🎉 You've learned how to set up your development environment, develop and deploy a smart contract, and build a functional Dice Game on the aelf blockchain. 🌟 📚 What You've Learned Throughout this tutorial, you’ve accomplished: 🛠️ Setting Up Your Development Environment: You prepared your workspace with all necessary tools to start building the Dice Game. 🎲 Developing the Dice Game Smart Contract: You created the core logic for the Dice Game, including dice rolling, reward handling, and managing bets. 🚀 Deploying the Smart Contract: You deployed your smart contract to the aelf blockchain, enabling its functionalities to be used in a live environment. 🔧 Building and Running the dApp: You connected your frontend to the blockchain and implemented key features like: Wallet integration via Portkey. Configuring token and dice contract methods. Adding gameplay logic and user-friendly features. 🔍 Final Output You now have: A deployed smart contract managing game logic and rewards. A fully operational Dice Game dApp where users can securely place bets, roll dice, and claim rewards. ➡️ Next Steps Now that you’ve mastered the basics, consider: Enhancing gameplay with new features like customizable bets or multiplayer options. Strengthening security with advanced testing and best practices. Exploring cross-chain functionality to make your game interoperable with other blockchains. You’ve taken a significant step into blockchain development. Keep building and innovating with aelf! 🚀 Happy coding! 😊 Step 1 - Setting up your development environment Step 2 - Develop Smart Contract Start Your Smart Contract Project Adding Your Smart Contract Code Building Smart Contract Step 3 - Deploy Smart Contract Step 4 - Interact with Your Deployed Smart Contract Approving Smart Contract Spending Initializing Dice Game Contract Depositing funds into the Dice Game Contract Playing the Dice Game Understanding randomization using Oracle Contract in aelf Step 5 - Interact Your Deployed Smart Contract With Frontend Project Setup Setting Up the Dice Game Smart Contract Setting Up Wallet Integration Setting Up Token Contract Methods Setting Up Dice Contract Methods Setting Up Game Features and Effects Run Application 🎯 Conclusion ## /quick-start/advance-tutorials/nft-indexer On this page NFT AeIndexer Description : This application demonstrates how to maintain account balances and transfer records by indexing aelf's NFT issued data. Purpose : Shows you how to create, develop, and deploy your own AeIndexer on AeFinder. Difficulty Level : Easy Step 1 - Setting up your development environment ​ Install dotnet 8.0 SDK Install IDE: Install your favorite IDE, such as: VS Code , Visual Studio , Rider , etc. Step 2 - Create AeIndexer in AeFinder ​ Log in to the AeFinder website. TestNet: https://test.aefinder.io/login Enter the AeIndexer Name and other information to create a NFT AeIndexer. Step 3 - Develop NFT AeIndexer ​ Download the Development Template ​ Go to the AeIndexer details page for your created project. Download the development template by entering your project name. Unzip the downloaded file to extract the project files. After unzipping the files, you are ready to start development Project Structure ​ The initial version of the NFT AeIndexer project has the following structure: To ensure the project is set up correctly, follow these steps: Setting Up the Files for NFT AeIndexer ​ Rename Files in the Contracts/ Folder: Navigate to the Contracts/ folder inside your project. Rename MyContract.c.cs to TokenContract.c.cs . Rename MyContract.g.cs to TokenContract.g.cs . Download Token Contract Files: Go to the AeFinder GitHub NFT Indexer sample . Locate and download the token contract files. Copy and Replace Code: Open the downloaded token contract files. Copy the content from TokenContract.c.cs and TokenContract.g.cs . Paste the copied code into the corresponding files in the src/NFTAeIndexer/Contracts directory of your project: Paste into TokenContract.c.cs . Paste into TokenContract.g.cs . Rename and add new files in the Entities/ Folder: Navigate to the Entities/ folder inside your project. Rename MyEntity.cs to Account.cs . Add new TransferRecord.cs file inside Entities folder. Rename and add new files in the Processors/ Folder: Navigate to the Processors/ folder inside your project. Rename MyLogEventProcessor.cs to NFTTransferredProcessor.cs . Updated Project Structure ​ After the setup, your NFT AeIndexer project will have the following structure: tip ℹ️ Note: If other contract files are needed, they need to be generated by compiling the corresponding contract project! 1. Entity Models ​ Navigate to the Entities/ folder inside your project. Account.cs file is as follows: Account.cs using AeFinder . Sdk . Entities ; using Nest ; namespace NFTAeIndexer . Entities ; public class Account : AeFinderEntity , IAeFinderEntity { [ Keyword ] public string Address { get ; set ; } [ Keyword ] public string Symbol { get ; set ; } public long Amount { get ; set ; } public string TokenName { get ; set ; } public string NftImageUri { get ; set ; } public string NftAttributes { get ; set ; } } TransferRecord.cs file is as follows: TransferRecord.cs using AeFinder . Sdk . Entities ; using Nest ; namespace NFTAeIndexer . Entities ; public class TransferRecord : AeFinderEntity , IAeFinderEntity { [ Keyword ] public string Symbol { get ; set ; } [ Keyword ] public string ToAddress { get ; set ; } public long Amount { get ; set ; } [ Text ] public string Memo { get ; set ; } } 2. NFT Transfer Processor ​ The NFTTransferredProcessor handles NFT transfer events and updates account balances: Navigate to the Processors/ folder inside your project. NFTTransferredProcessor.cs file is as follows: NFTTransferredProcessor.cs using Microsoft . Extensions . Logging ; using AElf . Contracts . MultiToken ; using AeFinder . Sdk . Logging ; using AeFinder . Sdk . Processor ; using AeFinder . Sdk ; using NFTAeIndexer . Entities ; using Volo . Abp . DependencyInjection ; namespace NFTAeIndexer . Processors ; public class NFTTransferredProcessor : LogEventProcessorBase < Issued > , ITransientDependency { private readonly IBlockChainService _blockChainService ; public NFTTransferredProcessor ( IBlockChainService blockChainService ) { _blockChainService = blockChainService ; } public override string GetContractAddress ( string chainId ) { return chainId switch { "AELF" => "JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE" , "tDVW" => "ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx" , _ => string . Empty } ; } public override async Task ProcessAsync ( Issued logEvent , LogEventContext context ) { if ( ! IsNftTransfer ( logEvent ) ) { return ; } var tokenInfoParam = new GetTokenInfoInput { Symbol = logEvent . Symbol } ; var nftTransfer = new TransferRecord { Id = $" { context . ChainId } - { context . Transaction . TransactionId } - { context . LogEvent . Index } " , ToAddress = logEvent . To . ToBase58 ( ) , Symbol = logEvent . Symbol , Amount = logEvent . Amount , Memo = logEvent . Memo } ; await SaveEntityAsync ( nftTransfer ) ; await ChangeNFTBalanceAsync ( context . ChainId , logEvent . To . ToBase58 ( ) , logEvent . Symbol , logEvent . Amount ) ; } private async Task ChangeNFTBalanceAsync ( string chainId , string address , string symbol , long amount ) { var accountId = $" { chainId } - { address } - { symbol } " ; var account = await GetEntityAsync < Account > ( accountId ) ; var tokenInfoParam = new GetTokenInfoInput { Symbol = symbol } ; var contractAddress = GetContractAddress ( chainId ) ; var tokenInfo = await _blockChainService . ViewContractAsync < TokenInfo > ( chainId , contractAddress , "GetTokenInfo" , tokenInfoParam ) ; Logger . LogDebug ( "TokenInfo response: {@TokenInfo}" , tokenInfo ) ; if ( account == null ) { account = new Account { Id = accountId , Symbol = symbol , Amount = amount , Address = address , TokenName = tokenInfo . TokenName , NftImageUri = tokenInfo . ExternalInfo ?. Value [ "__nft_image_uri" ] ?? tokenInfo . ExternalInfo ?. Value [ "__nft_image_url" ] , NftAttributes = tokenInfo . ExternalInfo ?. Value [ "__nft_attributes" ] } ; } else { account . Amount += amount ; } Logger . LogDebug ( "NFT Balance changed: {0} {1} {2} {3}" , account . Address , account . Symbol , account . Amount , account . TokenName ) ; await SaveEntityAsync ( account ) ; } private bool IsNftTransfer ( Issued logEvent ) { return ! string . IsNullOrEmpty ( logEvent . Symbol ) && logEvent . Symbol . Contains ( "-" ) && logEvent . Amount > 0 && logEvent . To != null && ! string . IsNullOrEmpty ( logEvent . To . ToBase58 ( ) ) ; } } Add files AccountDto.cs , TransferRecordDto.cs , GetAccountInput.cs , GetTransferRecordInput.cs to the directory src/NFTAeIndexer/GraphQL . AccountDto.cs using AeFinder . Sdk . Dtos ; namespace NFTAeIndexer . GraphQL ; public class AccountDto : AeFinderEntityDto { public string Address { get ; set ; } public string Symbol { get ; set ; } public long Amount { get ; set ; } public string TokenName { get ; set ; } public string NftImageUri { get ; set ; } public string NftAttributes { get ; set ; } } TransferRecordDto.cs using AeFinder . Sdk . Dtos ; namespace NFTAeIndexer . GraphQL ; public class TransferRecordDto : AeFinderEntityDto { public string Symbol { get ; set ; } public string FromAddress { get ; set ; } public string ToAddress { get ; set ; } public long Amount { get ; set ; } } GetAccountInput.cs namespace NFTAeIndexer . GraphQL ; public class GetAccountInput { public string ChainId { get ; set ; } public string Address { get ; set ; } public string Symbol { get ; set ; } } GetTransferRecordInput.cs namespace NFTAeIndexer . GraphQL ; public class GetTransferRecordInput { public string ChainId { get ; set ; } public string Address { get ; set ; } public string Symbol { get ; set ; } } Modify src/NFTAeIndexer/GraphQL/Query.cs to add query logic. Query.cs using AeFinder . Sdk ; using GraphQL ; using NFTAeIndexer . Entities ; using Volo . Abp . ObjectMapping ; namespace NFTAeIndexer . GraphQL ; public class Query { public static async Task < List < AccountDto > > Account ( [ FromServices ] IReadOnlyRepository < Account > repository , [ FromServices ] IObjectMapper objectMapper , GetAccountInput input ) { var queryable = await repository . GetQueryableAsync ( ) ; queryable = queryable . Where ( a => a . Metadata . ChainId == input . ChainId ) ; if ( ! input . Address . IsNullOrWhiteSpace ( ) ) { queryable = queryable . Where ( a => a . Address == input . Address ) ; } if ( ! input . Symbol . IsNullOrWhiteSpace ( ) ) { queryable = queryable . Where ( a => a . Symbol == input . Symbol && a . Symbol . Contains ( "-" ) ) ; } var accounts = queryable . OrderBy ( o => o . Metadata . Block . BlockHeight ) . ToList ( ) ; return objectMapper . Map < List < Account > , List < AccountDto > > ( accounts ) ; } public static async Task < List < TransferRecordDto > > TransferRecord ( [ FromServices ] IReadOnlyRepo
프로젝트로 돌아가기