aelf 백서

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

ds ​ GetProposal : Fetch proposal by ID GetMemberCount : Fetch member count GetMemberExist : Check if a member exists by address 4. Message Definitions ​ Member : DAO member (address) Proposal : Proposal (title, description, votes, status, vote threshold) CreateProposalInput : Fields for creating a proposal (title, description, vote threshold) VoteInput : Fields for voting on a proposal (proposal ID, vote) MemberList : List of DAO members ProposalList : List of proposals Defining Contract State ​ Open the src/BuildersDAOState.cs file. Replace its contents with this code snippet. src/BuildersDAOState.cs using System . Collections . Generic ; using System . Diagnostics . CodeAnalysis ; using AElf . Sdk . CSharp . State ; using AElf . Types ; namespace AElf . Contracts . BuildersDAO { // The state class is access the blockchain state public class BuildersDAOState : ContractState { public BoolState Initialized { get ; set ; } public MappedState < Address , bool > Members { get ; set ; } public MappedState < string , Proposal > Proposals { get ; set ; } public Int32State MemberCount { get ; set ; } public Int32State NextProposalId { get ; set ; } } } Understanding the Code ​ 3. State Variables ​ Members : Mapping each member to a boolean indicates if they joined the DAO Proposals : Mapping each proposal to an ID for identification and retrieval MemberCountId and NextProposalId : Track total number of members and proposals Next Step ​ Implement the logic of our voting smart contract. Implement Voting Smart Contract Logic ​ Checking Smart Contract Logics ​ Open src/BuildersDAO.cs Replace the existing content with this code snippet. src/BuildersDAO.cs using System . Collections . Generic ; using System . Security . Principal ; using AElf . Sdk . CSharp ; using AElf . Sdk . CSharp . State ; using AElf . Types ; using Google . Protobuf . WellKnownTypes ; namespace AElf . Contracts . BuildersDAO { public class BuildersDAO : BuildersDAOContainer . BuildersDAOBase { const string author = "REPLACE PLACEHOLDER HERE" ; // Implement Initialize Smart Contract Logic public override Empty Initialize ( Empty input ) { } // Implement Join DAO Logic public override Empty JoinDAO ( Address input ) { } // Implement Create Proposal Logic public override Proposal CreateProposal ( CreateProposalInput input ) { } // Implement Vote on Proposal Logic public override Proposal VoteOnProposal ( VoteInput input ) { } // Implement Get All Proposals Logic public override ProposalList GetAllProposals ( Empty input ) { } // Implement Get Proposal Logic public override Proposal GetProposal ( StringValue input ) { } // Implement Get Member Count Logic public override Int32Value GetMemberCount ( Empty input ) { } // Implement Get Member Exist Logic public override BoolValue GetMemberExist ( Address input ) { } } } danger Aelf dAppChain does not allow duplicate identical smart contracts. Hence, we will be using the author variable as the unique identifier for our voting smart contract in order to deploy the smart contract successfully. Implementing Initialize Function ​ Go to the comment Implement Vote on Proposal Logic . Check if the smart contract is already initialized; return if true. Define a hardcoded proposal with necessary parameters. Update the Proposals state variable with the hardcoded proposal and increment the proposalId. src/BuildersDAO.cs // Implement Initialize Smart Contract Logic public override Empty Initialize ( Empty input ) { Assert ( ! State . Initialized . Value , "already initialized" ) ; var initialProposal = new Proposal { Id = "0" , Title = "Proposal #1" , Description = "This is the first proposal of the DAO" , Status = "IN PROGRESS" , VoteThreshold = 1 , } ; State . Proposals [ initialProposal . Id ] = initialProposal ; State . NextProposalId . Value = 1 ; State . MemberCount . Value = 0 ; State . Initialized . Value = true ; return new Empty ( ) ; } Implementing Join DAO Function ​ Go to the comment Implement Join DAO Logic Check if the member already exists in the DAO using the Members state variable. If not found, update Members to include the user's address. Increment membersCount to reflect the new member added. You'll implement this function. Once done, you can proceed to the next page to compare your code with the reference implementation. src/BuildersDAO.cs // Implement Join DAO Logic public override Empty JoinDAO ( Address input ) { // Based on the address, determine whether the address has joined the DAO. If it has, throw an exception // If the address has not joined the DAO, then join and update the state's value to true // Read the value of MemberCount in the state, increment it by 1, and update it in the state // Using 'return null' to ensure the contract compiles successfully. Please update it to the correct return value when implementing return null ; } Implementing Create Proposal Function ​ Go to the comment Implement Create Proposal Logic Check if the user is a DAO member (required to create proposals). Create a new proposal object using fields from CreateProposalInput . Update Proposals with the new proposal, increment NextProposalId, and return the created proposal object. Now, use the provided code snippet to fill in the CreateProposal function. src/BuildersDAO.cs // Implement Create Proposal Logic public override Proposal CreateProposal ( CreateProposalInput input ) { Assert ( State . Members [ input . Creator ] , "Only DAO members can create proposals" ) ; var proposalId = State . NextProposalId . Value . ToString ( ) ; var newProposal = new Proposal { Id = proposalId , Title = input . Title , Description = input . Description , Status = "IN PROGRESS" , VoteThreshold = input . VoteThreshold , YesVotes = { } , // Initialize as empty NoVotes = { } , // Initialize as empty } ; State . Proposals [ proposalId ] = newProposal ; State . NextProposalId . Value += 1 ; return newProposal ; // Ensure return } Implementing Vote On Proposal Function ​ Go to the comment Implement Vote on Logic Perform these checks: Verify if the member is a DAO member (required to vote). Confirm if the proposal exists; otherwise, display an error message. Check if the member has already voted on the proposal; members can vote only once. If all checks pass, store the member’s vote and update the proposal state. Update the proposal status based on vote thresholds: If yesVotes reach the threshold, update status to "PASSED". If noVotes reach the threshold, update status to "DENIED". Now, use the provided code snippet to complete the VoteOnProposal function. src/BuildersDAO.cs // Implement Vote on Proposal Logic public override Proposal VoteOnProposal ( VoteInput input ) { Assert ( State . Members [ input . Voter ] , "Only DAO members can vote" ) ; var proposal = State . Proposals [ input . ProposalId ] ; // ?? new proposal Assert ( proposal != null , "Proposal not found" ) ; Assert ( ! proposal . YesVotes . Contains ( input . Voter ) && ! proposal . NoVotes . Contains ( input . Voter ) , "Member already voted" ) ; // Add the vote to the appropriate list if ( input . Vote ) { proposal . YesVotes . Add ( input . Voter ) ; } else { proposal . NoVotes . Add ( input . Voter ) ; } // Update the proposal in state State . Proposals [ input . ProposalId ] = proposal ; // Check if the proposal has reached its vote threshold if ( proposal . YesVotes . Count >= proposal . VoteThreshold ) { proposal . Status = "PASSED" ; } else if ( proposal . NoVotes . Count >= proposal . VoteThreshold ) { proposal . Status = "DENIED" ; } return proposal ; } Implementing Get All Proposals Function ​ Go to the comment Implement Get All Proposals Logic Create a new ProposalList object from the message definition in BuildersDAO.proto . Fetch and iterate through Proposals . Update ProposalList with proposal objects and return the list of proposals. If yesVotes reach the threshold, update status to "PASSED". If noVotes reach the threshold, update status to "DENIED". You'll implement this function. Once done, you can proceed to the next page to compare your code with the reference implementation. src/BuildersDAO.cs // Implement Get All Proposals Logic public override ProposalList GetAllProposals ( Empty input ) { // Create a new list called ProposalList // Start iterating through Proposals from index 0 until the value of NextProposalId, read the corresponding proposal, add it to ProposalList, and finally return ProposalList // Using 'return null' to ensure the contract compiles successfully. Please update it to the correct return value when implementing return null ; } Implementing Get Proposal / Get Member Count / Get Member Exist Functions ​ 1. Get Proposal ​ Navigate to Implement Get Proposal Logic . Retrieve a proposal by proposalId . Use proposalId as the key to query State.Proposals . Return the corresponding proposal value. 2. Get Member Count ​ Navigate to Implement Get Member Count Logic . Retrieve the total member count. Return the value of MemberCount from State . 3. Get Member Exist ​ Navigate to Implement Get Member Exist Logic . Check if a member exists by address . Use address as the key to query State.Members . Return the corresponding existence value. Implement these methods to access different states effectively in your smart contract. src/BuildersDAO.cs // Implement Get Proposal Logic public override Proposal GetProposal ( StringValue input ) { var proposal = State . Proposals [ input . Value ] ; return proposal ; } // Implement Get Member Count Logic public override Int32Value GetMemberCount ( Empty input ) { var memberCount = new Int32Value { Value = State . MemberCount . Value } ; return memberCount ; } // Implement Get Member Exist Logic public override BoolValue GetMemberExist ( Address input ) { var exist = new BoolValue { Value = State . Members [ input ] } ; return exist ; } With that, we have implemented all the functionalities of our Voting dApp smart contract. In the next step, we will compile our smart contract and deploy our written smart contract to the aelf dAppChain. Complete Implementation ​ Implementing Join DAO Function ​ Check Membership : See if the address has already joined the DAO by checking State.Members. Use the Assert method for this verification. Add New Member : If the address isn't a member yet, add it to State.Members and set its value to true. Update Member Count : Increase State.MemberCount by 1 and save the new value. src/BuildersDAO.cs public override Empty JoinDAO ( Address input ) { // Based on the address, determine whether the address has joined the DAO. If it has, throw an exception Assert ( ! State . Members [ input ] , "Member is already in the DAO" ) ; // If the address has not joined the DAO, then join and update the state's value to true State . Members [ input ] = true ; // Read the value of MemberCount in the state, increment it by 1, and update it in the state var currentCount = State . MemberCount . Value ; State . MemberCount . Value = currentCount + 1 ; return new Empty ( ) ; } Implementing Get All Proposals Function ​ Create a list object called ProposalList . Loop from 0 to the value of State.NextProposalId . In each loop iteration, get the values from State.Proposals and add them to ProposalList . Return ProposalList . src/BuildersDAO.cs public override ProposalList GetAllProposals ( Empty input ) { // Create a new list called ProposalList var proposals = new ProposalList ( ) ; // Start iterating through Proposals from index 0 until the value of NextProposalId, read the corresponding proposal, add it to ProposalList, and finally return ProposalList for ( var i = 0 ; i < State . NextProposalId . Value ; i ++ ) { var proposalCount = i . ToString ( ) ; var proposal = State . Proposals [ proposalCount ] ; proposals . Proposals . Add ( proposal ) ; } return proposals ; } Once you've implemented these two methods and run the unit tests again, you should see that all test cases pass. Building Smart Contract ​ Build the new code with the following commands inside src folder: Terminal dotnet build You should see BuildersDAO.dll.patched in the directory src/bin/Debug/net.6.0 Step 3 - Deploy Smart Contract ​ Create A Wallet ​ To send transactions on the aelf blockchain, you must have a wallet. Run this command to create aelf wallet. Terminal aelf-command create You will be prompted to save your account, please do save your account as shown below: Terminal ? Save account info into a file? ( Y/n ) Y Make sure to choose Y to save your account information. tip ℹ️ Note: If you do not save your account information (by selecting n or N), do not export the wallet password. Only proceed to the next step if you have saved your account information. Next, enter and confirm your password. Then export your wallet password as shown below: Linux and macOs Windows Terminal export WALLET_PASSWORD = "YOUR_WALLET_PASSWORD" Command Prompt $env :WALLET_PASSWORD = "YOUR_WALLET_PASSWORD" Acquire Testnet Tokens (Faucet) for Development ​ To deploy smart contracts or execute on-chain transactions on aelf, you'll require testnet ELF tokens. Get ELF Tokens Go to https://faucet-ui.aelf.dev Enter your address and click Get Tokens . Deploy Smart Contract: The smart contract needs to be deployed on the chain before users can interact with it. Run the following command to deploy a contract. Remember to export the path of BuildersDAO.dll.patched to CONTRACT_PATH. Linux and macOs Windows Terminal export CONTRACT_PATH = $( find ~+ . -path "*patched*" | head -n 1 ) Terminal aelf-deploy -a $WALLET_ADDRESS -p $WALLET_PASSWORD -c $CONTRACT_PATH -e https://tdvw-test-node.aelf.io/ Command Prompt $CONTRACT_PATH = Get-ChildItem -Recurse -Filter "*patched*" | Select-Object -First 1 -ExpandProperty FullName $env :CONTRACT_PATH = $CONTRACT_PATH Command Prompt aelf-deploy -a $env :WALLET_ADDRESS -p $env :WALLET_PASSWORD -c $env :CONTRACT_PATH -e https://tdvw-test-node.aelf.io/ Please wait for approximately 1 to 2 minutes. If the deployment is successful, it will provide you with the contract address. Copy the smart contract address from the address field Export your smart contract address: Linux and macOs Windows Terminal export CONTRACT_ADDRESS = "YOUR_SMART_CONTRACT_ADDRESS e.g. 2LUmicHyH4RXrMjG4beDwuDsiWJESyLkgkwPdGTR8kahRzq5XS" Command Prompt $env :CONTRACT_ADDRESS = "YOUR_SMART_CONTRACT_ADDRESS e.g. 2LUmicHyH4RXrMjG4beDwuDsiWJESyLkgkwPdGTR8kahRzq5XS" tip ℹ️ Note: You are to copy the smart contract address as we will be referencing it in the next steps! info 🎉 You have successfully deployed your dApp smart contract on the aelf testnet! In the next steps, we will be building the frontend components that allow us to interact with our deployed smart contract! Step 4 - Interact with Your Deployed Smart Contract ​ Project Setup ​ Let's start by cloning the frontend project repository from GitHub. Run the following command in the capstone_aelf directory: Terminal git clone https://github.com/AElfProject/aelf-samples.git Next, navigate to the frontend project directory with this command: Terminal cd aelf-samples/vote/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 libraries ​ Run this command in the terminal: Terminal npm install We are now ready to build the frontend components of our Voting dApp. Configure Portkey Provider & Write Connect Wallet Function ​ We'll set up our Portkey provider to let users connect their Portkey wallets to our app and interact with our voting smart contract. Go to the src/useDAOSmartContract.ts file. In this file, we'll create a component that initializes the Portkey wallet provider and fetches our deployed voting smart contract. This will enable our frontend components to interact with the smart contract for actions like joining the DAO, creating proposals, and more. Locate the comment Step A - Setup Portkey Wallet Provider and replace the existing useEffect hook with the following code snippet: useDAOSmartContract.ts //Step A - Setup Portkey Wallet Provider useEffect ( ( ) => { ( async ( ) => { if ( ! provider ) return null ; try { // 1. get the aelf dAppChain using provider.getChain const chain = await provider ?. getChain ( "tDVW" ) ; if ( ! chain ) throw new Error ( "No chain" ) ; //Address of DAO Smart Contract //Replace with Address of Deployed Smart Contract const address = "2GkJoDicXLqo7cR9YhjCEnCXQt8KUFUTPfCkeJEaAxGFYQo2tb" ; // 2. get the DAO contract const daoContract = chain ?. getContract ( address ) ; setSmartContract ( daoContract ) ; } catch ( error ) { console . log ( error , "====error" ) ; } } ) ( ) ; } , [ provider ] ) ; tip ℹ️ Note: You are to replace the address placeholder with your deployed voting contract address from "Deploy Voting dApp Smart Contract"! example: //Replace with Address of Deployed Smart Contract const address = "your_deployed_voting_contract_address"; Next, go to the src/HomeDAO.tsx file. The HomeDAO.tsx file is the landing page of our Voting dApp. It allows users to interact with the deployed smart contract, join the DAO, view proposals, and vote on them. Before users can interact with the smart contract, we need to write the Connect Wallet function. Find the comment Step B - Connect Portkey Wallet . Replace the existing connect function with this code snippet: src/HomeDAO.ts const connect = async ( ) => { //Step B - Connect Portkey Wallet const accounts = await provider ?. request ( { method : MethodsBase . REQUEST_ACCOUNTS , } ) ; const account = accounts ?. tDVW ?. [ 0 ] ; setCurrentWalletAddress ( account ) ; setIsConnected ( true ) ; alert ( "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. Write Initialize Smart Contract & Join DAO Functions ​ Let's write the Initialize and Join DAO functions. Find the comment Step C - Write Initialize Smart Contract and Join DAO Logic . Replace the existing initializeAndJoinDAO function with this code snippet: src/HomeDAO.ts const initializeAndJoinDAO = async ( ) => { //Step C - Write Initialize Smart Contract and Join DAO Logic try { const accounts = await provider ?. request ( { method : MethodsBase . ACCOUNTS , } ) ; if ( ! accounts ) throw new Error ( "No accounts" ) ; const account = accounts ?. tDVW ?. [ 0 ] ; if ( ! account ) throw new Error ( "No account" ) ; if ( ! initialized ) { await DAOContract ?. callSendMethod ( "Initialize" , account , { } ) ; setInitialized ( true ) ; alert ( "DAO Contract Successfully Initialized" ) ; } await DAOContract ?. callSendMethod ( "JoinDAO" , account , account ) ; setJoinedDAO ( true ) ; alert ( "Successfully Joined DAO" ) ; } catch ( error ) { console . error ( error , "====error" ) ; } } ; Here's what the function does: ​ Fetches your wallet account using the Portkey wallet provider. Initializes the DAO smart contract if it hasn't been done already, updating the state and showing a success al
프로젝트로 돌아가기