aelf 백서

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

f 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. Connect Portkey Wallet Click on "Connect Wallet" to connect your Portkey wallet. You will be get below connect wallet request on your Portkey wallet. Click on Approve button. The button will change to "Your Wallet Address" when the connection is successful. Add New Expense Click on "Add Expense" button to add a new expense. You will see the pop-up modal with form to add a new expense. Please fill all the necessary fields like Description , Amount and Select Category . Click on Add New Expense button as shown below. You will receive a transaction request on your portkey wallet to Sign the transaction. Click on Sign the transaction. After the transaction is successfully processed, Your first expense will be added ✅. As we have Added an Expense successfully, You will be able to see the expense card and the total Amount of the Outcome . Edit the Expense Click on the "Edit" button to edit a selected expense. You will see the pop-up modal with form to edit the expense. Edit the necessary fields according to your need. Click on Update Expense Button. Now, You will recieve a transaction request on your portkey wallet to Sign the transaction. Click on Sign the transaction. After the transaction is successfully processed, your expense details will be updated✅. As we have Edited an Expense successfully. Let's test remove expense functionality. Remove the Expense Click on "Remove" button to remove the selected expense. Now, You will recieve a transaction request on your portkey wallet to Sign the transaction. Click on Sign the transaction. After the transaction is successfully processed, your expense will be removed from the list. success 🎉 Congratulations Learners! You have successfully built your Expense Tracker dApp. 🎯 Conclusion ​ 🎉 Congratulations on completing the Expense Tracker dApp tutorial! 🎉 You've accomplished critical milestones, from setting up your development environment to creating, deploying, and interacting with your Expense Tracker smart contract on the aelf blockchain. 🌟 📚 What You've Learned Throughout this tutorial, you've gained hands-on experience with: 🛠️ Setting Up Your Development Environment: You successfully installed and configured all necessary tools to start developing smart contracts on the aelf blockchain. 💻 Developing Your Smart Contract: You crafted the essential logic for the Expense Tracker, building a contract to manage tasks like adding, updating, and deleting expenses while keeping a record of all transactions. 🚀 Deploying the Smart Contract: You deployed your Expense Tracker contract on the aelf blockchain, ensuring it was live and operational for real-world use. 🔧 Interacting with Your Deployed Smart Contract through the dApp: You created a front-end interface, connected it to the blockchain using Portkey, and implemented crucial functions like wallet connectivity, creating new expense, and interacting with expense data. 🔍 Final Output By now, you should have: 📜 A deployed smart contract that governs the logic of managing expenses and storing financial data on the blockchain. 💻 A fully functional Expense Tracker dApp where users can securely connect their wallets, create new expenses, update or delete them, and view their financial data — all in a decentralized manner. ➡️ What's Next? Now that you've built the foundation of your Expense Tracker dApp, consider extending it with advanced features: 📊 Add Analytical Features: Implement visualizations and analytics to help users gain better insights into their spending patterns and budgeting habits. 🔒 Enhance Security: Strengthen your dApp by applying smart contract security best practices to ensure that users' financial data remains private and secure. 🌍 Exploring Cross-Chain Capabilities: Explore aelf’s cross-chain capabilities to integrate your dApp with other blockchain networks and allow users to manage their finances across multiple chains. You've taken a significant step toward mastering blockchain development with your Expense Tracker dApp. Now, you’re ready to continue innovating and expanding your decentralized applications on the aelf platform. 🚀 Happy coding and expanding your Expense Tracker dApp! 😊 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 through dApp Project Setup Configure Portkey Provider & Write Connect Wallet Function Configure Connect Wallet Function Configure Create Expense Form Check Contract Initialization Initialize Contract Add New Expense Update Expense Delete Expense Handle Submit Form Fetch All Expenses Run Application 🎯 Conclusion ## /quick-start/developers/single-pool-staking-dapp On this page Description : The Single Pool Staking dApp is a decentralized application built on the aelf blockchain that allows users to stake their tokens in a single staking pool. Users can earn rewards based on the amount and duration of their staked assets, with staking and reward distribution processes fully automated and secured by blockchain technology. The dApp offers a transparent and simple interface for users to monitor their staked assets and track reward accumulation over time. Purpose : The Single Pool Staking dApp aims to demonstrate the seamless integration of staking mechanisms with blockchain, providing users with a secure, transparent, and efficient way to grow their holdings. It serves as an educational tool for learning about staking contracts and their role in decentralized finance (DeFi), while showcasing the potential of blockchain technology for creating decentralized financial services that offer fairness and trustless reward distribution. Difficulty Level : Moderate Step 1 - Setting up your development environment ​ Local Codespaces Basic knowledge of terminal commands IDE - Install VS Code Note for Apple Silicon users: Ensure that Rosetta is installed, if it is not, use the following command: Terminal softwareupdate --install-rosetta Install Required Packages Install dotnet 8.0.x SDK Install aelf contract templates Linux and macOs Windows Terminal dotnet new --install AElf.ContractTemplates Command Prompt dotnet new install AElf.ContractTemplates AELF.ContractTemplates contains various predefined templates for the ease of developing smart contracts on the aelf blockchain. Install aelf deploy tool Terminal dotnet tool install --global aelf.deploy aelf.deploy is a utility tool for deploying smart contracts on the aelf blockchain. Please remember to export PATH after installing aelf.deploy. info ℹ️ Note: If you have installed aelf.deploy and your terminal says that there is no such command available, please uninstall and install aelf.deploy. Install Node.js and Yarn Install Node.js Install Yarn Install aelf-command Linux and macOs Windows Terminal sudo npm i -g aelf-command Command Prompt npm i -g aelf-command aelf-command is a CLI tool for interacting with the aelf blockchain, enabling tasks like creating wallets and managing transactions. Provide required permissions while installing aelf-command globally. Visit aelf-devcontainer-template . Click the Use this template button. Choose Create a new repository . Enter a suitable repository name. Click Create repository . Within the GitHub interface of your new repository, click on Code . Select Codespaces . Click on the + sign to create a new Codespace. After some time, your workspace will load with the contents of the repository. You can now continue your development using GitHub Codespaces. Step 2 - Develop Smart Contract ​ Start Your Smart Contract Project ​ Open your Terminal . Enter the following command to generate a new project: Terminal mkdir single-pool-staking-dapp cd single-pool-staking-dapp dotnet new aelf -n SinglePoolStaking Adding Your Smart Contract Code ​ Now that we have a template Single Pool Staking project, we can customise the template to incorporate our own contract logic. Let's start by implementing methods to handle the basic functionality like deposit tokens to the staking pool, withdraw tokens from the staking pool, withdrawing tokens before the lock(stake) period ends (forceWithdraw), get the reward amount for an address from the pool, fetch all the deposits linked to a user and retrieve the total staked amount in the contract. Single Pool Staking dApp includes the below functionalities like: Deposit: Allows users to stake tokens, update the total staked amount and the deposit gets linked to the user. Withdraw: Allows users to withdraw tokens and rewards after the lock period ends. ForceWithdraw: Allows users to withdraw tokens before the lock period ends without rewards. GetReward: Retrieves the reward amount earned from a specific deposit. GetDeposits: Lists all deposits linked to a user. GetTotalStakedAmount: Retrieves the total staked amount Enter this command in your Terminal . Terminal cd src Defining Methods and Messages ​ Rename the proto file name hello_world_contract.proto inside folder Protobuf/contract/ to single_pool_staking.proto : Terminal mv Protobuf/contract/hello_world_contract.proto Protobuf/contract/single_pool_staking.proto The .proto file defines the structure and serialization of data, ensuring consistent communication and data exchange between the contract and external systems. Open the project with your IDE. The implementation of single_pool_staking.proto file inside folder src/Protobuf/contract/ is as follows: single_pool_staking.proto syntax = "proto3" ; import "aelf/core.proto" ; import "aelf/options.proto" ; import "google/protobuf/empty.proto" ; import "Protobuf/reference/acs12.proto" ; import "google/protobuf/wrappers.proto" ; option csharp_namespace = "AElf.Contracts.StakingContract" ; service StakingContract { option ( aelf . csharp_state ) = "AElf.Contracts.StakingContract.StakingContractState" ; option ( aelf . base ) = "Protobuf/reference/acs12.proto" ; rpc Initialize ( InitializeInput ) returns ( google . protobuf . Empty ) ; rpc Deposit ( DepositInput ) returns ( google . protobuf . StringValue ) ; rpc Withdraw ( WithdrawInput ) returns ( google . protobuf . Empty ) ; rpc ForceWithdraw ( google . protobuf . StringValue ) returns ( google . protobuf . Empty ) ; rpc GetReward ( google . protobuf . StringValue ) returns ( google . protobuf . Int64Value ) { option ( aelf . is_view ) = true ; } rpc GetDeposits ( google . protobuf . StringValue ) returns ( DepositList ) { option ( aelf . is_view ) = true ; } // New functions rpc IfInitialized ( google . protobuf . Empty ) returns ( google . protobuf . BoolValue ) { option ( aelf . is_view ) = true ; } rpc GetTotalStakedAmount ( google . protobuf . Empty ) returns ( google . protobuf . Int64Value ) { option ( aelf . is_view ) = true ; } } message DepositInput { string token_symbol = 1 ; int64 amount = 2 ; int64 lock_time = 3 ; } message InitializeInput { aelf . Address token_contract_address = 1 ; } message WithdrawInput { string deposit_id = 1 ; } message TransferInput { aelf . Address to = 1 ; string symbol = 2 ; int64 amount = 3 ; string memo = 4 ; // Add this field } message StringList { repeated string values = 1 ; } message Deposit { string deposit_id = 1 ; string address = 2 ; string token_symbol = 3 ; // The specific FT token symbol int64 amount = 4 ; int64 lock_time = 5 ; int64 deposit_time = 6 ; } message DepositList { repeated Deposit deposits = 1 ; } rpc methods define the callable functions within the contract, allowing external systems to interact with the contract's logic. message represent the structured data exchanged between the contract and external systems. Define Contract States ​ The implementation of the Single Pool Staking smart contract state inside file src/SinglePoolStakingState.cs is as follows: src/SinglePoolStakingState.cs using AElf . Sdk . CSharp . State ; using AElf . Types ; using AElf . Contracts . MultiToken ; namespace AElf . Contracts . StakingContract { public class StakingContractState : ContractState { public BoolState Initialized { get ; set ; } public SingletonState < Address > Owner { get ; set ; } public MappedState < string , Deposit > Deposits { get ; set ; } // Mapping from deposit ID to Deposit public MappedState < Address , StringList > UserDeposits { get ; set ; } // User to deposit IDs public Int32State DepositCounter { get ; set ; } public Int64State TotalStakedAmount { get ; set ; } // New state to track total staked amount internal TokenContractContainer . TokenContractReferenceState TokenContract { get ; set ; } } } The State.cs file in the aelf blockchain smart contract holds the variables that store the contract's data, making sure this data is saved and accessible whenever the contract needs it. Implement Single Pool Staking Smart Contract ​ The implementation of the Single Pool Staking smart contract inside file src/SinglePoolStaking.cs is as follows: src/SinglePoolStaking.cs using Google . Protobuf . WellKnownTypes ; using AElf . Types ; using System . Collections . Generic ; using AElf . Contracts . MultiToken ; namespace AElf . Contracts . StakingContract { public class StakingContract : StakingContractContainer . StakingContractBase { private const int RewardRate = 10 ; // 10% reward public override Empty Initialize ( InitializeInput input ) { if ( State . Initialized . Value ) return new Empty ( ) ; State . Initialized . Value = true ; State . Owner . Value = Context . Sender ; State . DepositCounter . Value = 0 ; State . TotalStakedAmount . Value = 0 ; // Initialize total staked amount State . TokenContract . Value = input . TokenContractAddress ; return new Empty ( ) ; } public override StringValue Deposit ( DepositInput input ) { var depositId = ( State . DepositCounter . Value + 1 ) . ToString ( ) ; State . DepositCounter . Value ++ ; var deposit = new Deposit { DepositId = depositId , Address = Context . Sender . ToString ( ) , TokenSymbol = input . TokenSymbol , Amount = input . Amount , LockTime = input . LockTime , DepositTime = Context . CurrentBlockTime . Seconds } ; State . Deposits [ depositId ] = deposit ; var userDeposits = State . UserDeposits [ Context . Sender ] ?? new StringList ( ) ; userDeposits . Values . Add ( depositId ) ; State . UserDeposits [ Context . Sender ] = userDeposits ; State . TotalStakedAmount . Value += input . Amount ; // Update total staked amount return new StringValue { Value = depositId } ; } public override Empty Withdraw ( WithdrawInput input ) { var deposit = State . Deposits [ input . DepositId ] ; Assert ( deposit != null , "Deposit not found." ) ; Assert ( deposit . Address == Context . Sender . ToString ( ) , "Unauthorized." ) ; Assert ( Context . CurrentBlockTime . Seconds >= deposit . DepositTime + deposit . LockTime , "Lock period not over." ) ; var reward = CalculateReward ( deposit . Amount ) ; TransferFromContract ( deposit . TokenSymbol , Context . Sender , deposit . Amount + reward ) ; State . TotalStakedAmount . Value -= deposit . Amount ; // Update total staked amount RemoveDeposit ( deposit . DepositId ) ; return new Empty ( ) ; } public override Empty ForceWithdraw ( StringValue input ) { var deposit = State . Deposits [ input . Value ] ; Assert ( deposit != null , "Deposit not found." ) ; Assert ( deposit . Address == Context . Sender . ToString ( ) , "Unauthorized." ) ; TransferFromContract ( deposit . TokenSymbol , Context . Sender , deposit . Amount ) ; State . TotalStakedAmount . Value -= deposit . Amount ; // Update total staked amount RemoveDeposit ( deposit . DepositId ) ; return new Empty ( ) ; } public override Int64Value GetReward ( StringValue input ) { var deposit = State . Deposits [ input . Value ] ; Assert ( deposit != null , "Deposit not found." ) ; return new Int64Value { Value = CalculateReward ( deposit . Amount ) } ; } public override DepositList GetDeposits ( StringValue input ) { var deposits = State . UserDeposits [ Address . FromBase58 ( input . Value ) ] ; var depositList = new DepositList ( ) ; foreach ( var depositId in deposits . Values ) { var deposit = State . Deposits [ depositId ] ; if ( deposit != null ) { depositList . Deposits . Add ( deposit ) ; } } return depositList ; } // New function to check if initialized public override BoolValue IfInitialized ( Empty input ) { return new BoolValue { Value = State . Initialized . Value } ; } // New function to get the total staked amount public override Int64Value GetTotalStakedAmount ( Empty input ) { return new Int64Value { Value = State . TotalStakedAmount . Value } ; } private long CalculateReward ( long amount ) { return ( amount * RewardRate ) / 100 ; } private void TransferFromContract ( string symbol , Address to , long amount ) { var virtualAddressHash = GetVirtualAddressHash ( Context . Self , symbol ) ; State . TokenContract . TransferFrom . Send ( new TransferFromInput { Symbol = symbol , Amount = amount , From = Context . Self , Memo = "Transfer from Staking Contract" , To = to } ) ; } private static Hash GetVirtualAddressHash ( Address contractAddress , string symbol ) { return HashHelper . ConcatAndCompute ( HashHelper . ComputeFrom ( contractAddress ) , HashHelper . ComputeFrom ( symbol ) ) ; } private Address GetVirtualAddress ( Hash virtualAddressHash ) { return Context . ConvertVirtualAddressToContractAddress ( virtualAddressHash ) ; } private void RemoveDeposit ( string depositId ) { var deposit = State . Deposits [ depositId ] ; State . Deposits . Remove ( depositId ) ; var userDeposits = State . UserDeposits [ Address . FromBase58 ( deposit . Address ) ] ; userDeposits . Values . Remove ( depositId ) ; State . UserDeposits [ Address . FromBase58 ( deposit . Address ) ] = userDeposits ; } } } Contract Reference State ​ Create a new file token_contract.proto under src/Protobuf/reference/ . Replace this code of implementation file of token_contract.proto : token_contract.proto /** * MultiToken contract. */ syntax = "proto3" ; package token ; import "aelf/core.proto" ; import "aelf/options.proto" ; import "google/protobuf/empty.proto" ; import "google/protobuf/wrappers.proto" ; option csharp_namespace = "AElf.Contracts.MultiToken" ; service TokenContract { // Create a new token. rpc Create ( CreateInput ) returns ( google . protobuf . Empty ) { } // Issuing some amount of tokens to an address is the action of increasing that addresses balance // for the given token. The total amount of issued tokens must not exceed the total supply of the token
프로젝트로 돌아가기