diff --git a/docs/quick-start/lottery-game/index.md b/docs/quick-start/lottery-game/index.md
new file mode 100644
index 0000000..8beda1c
--- /dev/null
+++ b/docs/quick-start/lottery-game/index.md
@@ -0,0 +1,1462 @@
+---
+sidebar_position: 4
+title: Lottery Game Contract
+---
+
+# Lottery Game Contract
+
+This guide provides step-by-step instructions to set up your local development environment to get started developing and deploying aelf smart contracts.
+
+## 1. Setup Environment
+
+### Prerequisites
+
+- Basic knowledge of terminal commands
+- **IDE** - Install [VS Code](https://code.visualstudio.com/)
+
+### Install Required Packages
+
+- [Install dotnet](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
+- Install aelf contract templates
+
+```bash
+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
+
+```bash
+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.
+
+### Install Node.js and Yarn
+
+- [Install Node.js](https://nodejs.org/en)
+- Install aelf-command
+
+```bash
+sudo 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.
+
+## 2. Develop Smart Contract
+
+### Start Your Smart Contract Project
+
+Open your `Terminal`.
+
+Enter the following command to generate a new project:
+
+```bash
+mkdir hello-world
+cd hello-world
+dotnet new aelf -n HelloWorld
+```
+
+### Install ACS12.proto
+
+```bash
+mkdir Protobuf
+cd Protobuf
+mkdir references
+cd ..
+export ACS_DIR=Protobuf/references
+curl -O --output-dir $ACS_DIR https://raw.githubusercontent.com/AElfProject/AElf/dev/protobuf/acs12.proto
+```
+
+### Adding Your Smart Contract Code
+
+Now that we have a template lottery game project, we can customise the template to incorporate our own contract logic.
+Lets start by implementing methods to provide basic functionality for updating and reading a message stored persistently in the contract state.
+
+```bash
+cd src
+```
+
+#### Defining Methods and Messages
+
+The implementation of file `src/Protobuf/contract/lottery_game_contract.proto` is as follows:
+
+```csharp
+syntax = "proto3";
+
+import "aelf/core.proto";
+import "aelf/options.proto";
+import "google/protobuf/empty.proto";
+import "google/protobuf/wrappers.proto";
+import "acs12.proto";
+// The namespace of this class
+option csharp_namespace = "AElf.Contracts.LotteryGame";
+
+service LotteryGame {
+ // The name of the state class the smart contract is going to use to access blockchain state
+ option (aelf.csharp_state) = "AElf.Contracts.LotteryGame.LotteryGameState";
+ option (aelf.base) = "acs12.proto";
+
+ rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) {
+ }
+
+ rpc Play (google.protobuf.Int64Value) returns (google.protobuf.Empty) {
+ }
+
+ rpc Withdraw (google.protobuf.Int64Value) returns (google.protobuf.Empty) {
+ }
+
+ rpc Deposit (google.protobuf.Int64Value) returns (google.protobuf.Empty) {
+ }
+
+ rpc TransferOwnership (aelf.Address) returns (google.protobuf.Empty) {
+ }
+
+ rpc GetPlayAmountLimit (google.protobuf.Empty) returns (PlayAmountLimitMessage) {
+ option (aelf.is_view) = true;
+ }
+
+ rpc GetContractBalance (google.protobuf.Empty) returns (google.protobuf.Int64Value) {
+ option (aelf.is_view) = true;
+ }
+
+ rpc GetOwner (google.protobuf.Empty) returns (google.protobuf.StringValue) {
+ option (aelf.is_view) = true;
+ }
+}
+
+// An event that will be emitted from contract method call when Play is called.
+message PlayOutcomeEvent {
+ option (aelf.is_event) = true;
+ int64 amount = 1;
+ int64 won = 2;
+}
+
+// An event that will be emitted from contract method call when Withdraw is called.
+message WithdrawEvent {
+ option (aelf.is_event) = true;
+ int64 amount = 1;
+ aelf.Address from = 2;
+ aelf.Address to = 3;
+}
+
+// An event that will be emitted from contract method call when Deposit is called.
+message DepositEvent {
+ option (aelf.is_event) = true;
+ int64 amount = 1;
+ aelf.Address from = 2;
+ aelf.Address to = 3;
+}
+
+// The message containing the play amount limits
+message PlayAmountLimitMessage {
+ int64 minimumAmount = 1;
+ int64 maximumAmount = 2;
+}
+
+
+```
+
+#### Define Contract States
+
+The implementation of file `src/LotteryGameState.cs` is as follows:
+
+```csharp
+using AElf.Sdk.CSharp.State;
+using AElf.Types;
+
+namespace AElf.Contracts.LotteryGame
+{
+ // The state class is access the blockchain state
+ public partial class LotteryGameState : ContractState
+ {
+ // A state to check if contract is initialized
+ public BoolState Initialized { get; set; }
+ // A state to store the owner address
+ public SingletonState
Owner { get; set; }
+ }
+}
+```
+
+#### Contract Reference State
+
+Create a new folder **reference** under `src/Protobuf/`
+
+```bash
+cd /src/Protobuf
+mkdir reference
+```
+
+Create a new file **token_contract.proto** under `src/Protobuf/reference/`
+The implementation of file `token_contract.protot`
+
+```csharp
+
+/**
+ * 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
+ // and only the issuer (creator) of the token can issue tokens.
+ // Issuing tokens effectively increases the circulating supply.
+ rpc Issue (IssueInput) returns (google.protobuf.Empty) {
+ }
+
+ // Transferring tokens simply is the action of transferring a given amount of tokens from one address to another.
+ // The origin or source address is the signer of the transaction.
+ // The balance of the sender must be higher than the amount that is transferred.
+ rpc Transfer (TransferInput) returns (google.protobuf.Empty) {
+ }
+
+ // The TransferFrom action will transfer a specified amount of tokens from one address to another.
+ // For this operation to succeed the from address needs to have approved (see allowances) enough tokens
+ // to Sender of this transaction. If successful the amount will be removed from the allowance.
+ rpc TransferFrom (TransferFromInput) returns (google.protobuf.Empty) {
+ }
+
+ // The approve action increases the allowance from the Sender to the Spender address,
+ // enabling the Spender to call TransferFrom.
+ rpc Approve (ApproveInput) returns (google.protobuf.Empty) {
+ }
+
+ rpc BatchApprove (BatchApproveInput) returns (google.protobuf.Empty) {
+ }
+
+ // This is the reverse operation for Approve, it will decrease the allowance.
+ rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) {
+ }
+
+ // This method can be used to lock tokens.
+ rpc Lock (LockInput) returns (google.protobuf.Empty) {
+ }
+
+ // This is the reverse operation of locking, it un-locks some previously locked tokens.
+ rpc Unlock (UnlockInput) returns (google.protobuf.Empty) {
+ }
+
+ // This action will burn the specified amount of tokens, removing them from the token’s Supply.
+ rpc Burn (BurnInput) returns (google.protobuf.Empty) {
+ }
+
+ // Set the primary token of side chain.
+ rpc SetPrimaryTokenSymbol (SetPrimaryTokenSymbolInput) returns (google.protobuf.Empty) {
+ }
+
+ // This interface is used for cross-chain transfer.
+ rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) {
+ }
+
+ // This method is used to receive cross-chain transfers.
+ rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) {
+ }
+
+ // The side chain creates tokens.
+ rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) {
+ }
+
+ // When the side chain is started, the side chain is initialized with the parent chain information.
+ rpc InitializeFromParentChain (InitializeFromParentChainInput) returns (google.protobuf.Empty) {
+ }
+
+ // Handle the transaction fees charged by ChargeTransactionFees.
+ rpc ClaimTransactionFees (TotalTransactionFeesMap) returns (google.protobuf.Empty) {
+ }
+
+ // Used to collect transaction fees.
+ rpc ChargeTransactionFees (ChargeTransactionFeesInput) returns (ChargeTransactionFeesOutput) {
+ }
+
+ rpc ChargeUserContractTransactionFees(ChargeTransactionFeesInput) returns(ChargeTransactionFeesOutput){
+
+ }
+
+ // Check the token threshold.
+ rpc CheckThreshold (CheckThresholdInput) returns (google.protobuf.Empty) {
+ }
+
+ // Initialize coefficients of every type of tokens supporting charging fee.
+ rpc InitialCoefficients (google.protobuf.Empty) returns (google.protobuf.Empty){
+ }
+
+ // Processing resource token received.
+ rpc DonateResourceToken (TotalResourceTokensMaps) returns (google.protobuf.Empty) {
+ }
+
+ // A transaction resource fee is charged to implement the ACS8 standards.
+ rpc ChargeResourceToken (ChargeResourceTokenInput) returns (google.protobuf.Empty) {
+ }
+
+ // Verify that the resource token are sufficient.
+ rpc CheckResourceToken (google.protobuf.Empty) returns (google.protobuf.Empty) {
+ }
+
+ // Set the list of tokens to pay transaction fees.
+ rpc SetSymbolsToPayTxSizeFee (SymbolListToPayTxSizeFee) returns (google.protobuf.Empty){
+ }
+
+ // Update the coefficient of the transaction fee calculation formula.
+ rpc UpdateCoefficientsForSender (UpdateCoefficientsInput) returns (google.protobuf.Empty) {
+ }
+
+ // Update the coefficient of the transaction fee calculation formula.
+ rpc UpdateCoefficientsForContract (UpdateCoefficientsInput) returns (google.protobuf.Empty) {
+ }
+
+ // This method is used to initialize the governance organization for some functions,
+ // including: the coefficient of the user transaction fee calculation formula,
+ // the coefficient of the contract developer resource fee calculation formula, and the side chain rental fee.
+ rpc InitializeAuthorizedController (google.protobuf.Empty) returns (google.protobuf.Empty){
+ }
+
+ rpc AddAddressToCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) {
+ }
+ rpc RemoveAddressFromCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) {
+ }
+
+ rpc SetTransactionFeeDelegations (SetTransactionFeeDelegationsInput) returns (SetTransactionFeeDelegationsOutput){
+ }
+
+ rpc RemoveTransactionFeeDelegator (RemoveTransactionFeeDelegatorInput) returns (google.protobuf.Empty){
+ }
+
+ rpc RemoveTransactionFeeDelegatee (RemoveTransactionFeeDelegateeInput) returns (google.protobuf.Empty){
+ }
+
+ rpc SetSymbolAlias (SetSymbolAliasInput) returns (google.protobuf.Empty){
+ }
+
+ // Get all delegatees' address of delegator from input
+ rpc GetTransactionFeeDelegatees (GetTransactionFeeDelegateesInput) returns (GetTransactionFeeDelegateesOutput) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query token information.
+ rpc GetTokenInfo (GetTokenInfoInput) returns (TokenInfo) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query native token information.
+ rpc GetNativeTokenInfo (google.protobuf.Empty) returns (TokenInfo) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query resource token information.
+ rpc GetResourceTokenInfo (google.protobuf.Empty) returns (TokenInfoList) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the balance at the specified address.
+ rpc GetBalance (GetBalanceInput) returns (GetBalanceOutput) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the account's allowance for other addresses
+ rpc GetAllowance (GetAllowanceInput) returns (GetAllowanceOutput) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the account's available allowance for other addresses
+ rpc GetAvailableAllowance (GetAllowanceInput) returns (GetAllowanceOutput) {
+ option (aelf.is_view) = true;
+ }
+
+ // Check whether the token is in the whitelist of an address,
+ // which can be called TransferFrom to transfer the token under the condition of not being credited.
+ rpc IsInWhiteList (IsInWhiteListInput) returns (google.protobuf.BoolValue) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the information for a lock.
+ rpc GetLockedAmount (GetLockedAmountInput) returns (GetLockedAmountOutput) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the address of receiving token in cross-chain transfer.
+ rpc GetCrossChainTransferTokenContractAddress (GetCrossChainTransferTokenContractAddressInput) returns (aelf.Address) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the name of the primary Token.
+ rpc GetPrimaryTokenSymbol (google.protobuf.Empty) returns (google.protobuf.StringValue) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the coefficient of the transaction fee calculation formula.
+ rpc GetCalculateFeeCoefficientsForContract (google.protobuf.Int32Value) returns (CalculateFeeCoefficients) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query the coefficient of the transaction fee calculation formula.
+ rpc GetCalculateFeeCoefficientsForSender (google.protobuf.Empty) returns (CalculateFeeCoefficients) {
+ option (aelf.is_view) = true;
+ }
+
+ // Query tokens that can pay transaction fees.
+ rpc GetSymbolsToPayTxSizeFee (google.protobuf.Empty) returns (SymbolListToPayTxSizeFee){
+ option (aelf.is_view) = true;
+ }
+
+ // Query the hash of the last input of ClaimTransactionFees.
+ rpc GetLatestTotalTransactionFeesMapHash (google.protobuf.Empty) returns (aelf.Hash){
+ option (aelf.is_view) = true;
+ }
+
+ // Query the hash of the last input of DonateResourceToken.
+ rpc GetLatestTotalResourceTokensMapsHash (google.protobuf.Empty) returns (aelf.Hash){
+ option (aelf.is_view) = true;
+ }
+ rpc IsTokenAvailableForMethodFee (google.protobuf.StringValue) returns (google.protobuf.BoolValue) {
+ option (aelf.is_view) = true;
+ }
+ rpc GetReservedExternalInfoKeyList (google.protobuf.Empty) returns (StringList) {
+ option (aelf.is_view) = true;
+ }
+
+ rpc GetTransactionFeeDelegationsOfADelegatee(GetTransactionFeeDelegationsOfADelegateeInput) returns(TransactionFeeDelegations){
+ option (aelf.is_view) = true;
+ }
+
+ rpc GetTokenAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) {
+ option (aelf.is_view) = true;
+ }
+
+ rpc GetSymbolByAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) {
+ option (aelf.is_view) = true;
+ }
+}
+
+message TokenInfo {
+ // The symbol of the token.f
+ string symbol = 1;
+ // The full name of the token.
+ string token_name = 2;
+ // The current supply of the token.
+ int64 supply = 3;
+ // The total supply of the token.
+ int64 total_supply = 4;
+ // The precision of the token.
+ int32 decimals = 5;
+ // The address that has permission to issue the token.
+ aelf.Address issuer = 6;
+ // A flag indicating if this token is burnable.
+ bool is_burnable = 7;
+ // The chain id of the token.
+ int32 issue_chain_id = 8;
+ // The amount of issued tokens.
+ int64 issued = 9;
+ // The external information of the token.
+ ExternalInfo external_info = 10;
+ // The address that owns the token.
+ aelf.Address owner = 11;
+}
+
+message ExternalInfo {
+ map value = 1;
+}
+
+message CreateInput {
+ // The symbol of the token.
+ string symbol = 1;
+ // The full name of the token.
+ string token_name = 2;
+ // The total supply of the token.
+ int64 total_supply = 3;
+ // The precision of the token
+ int32 decimals = 4;
+ // The address that has permission to issue the token.
+ aelf.Address issuer = 5;
+ // A flag indicating if this token is burnable.
+ bool is_burnable = 6;
+ // A whitelist address list used to lock tokens.
+ repeated aelf.Address lock_white_list = 7;
+ // The chain id of the token.
+ int32 issue_chain_id = 8;
+ // The external information of the token.
+ ExternalInfo external_info = 9;
+ // The address that owns the token.
+ aelf.Address owner = 10;
+}
+
+message SetPrimaryTokenSymbolInput {
+ // The symbol of the token.
+ string symbol = 1;
+}
+
+message IssueInput {
+ // The token symbol to issue.
+ string symbol = 1;
+ // The token amount to issue.
+ int64 amount = 2;
+ // The memo.
+ string memo = 3;
+ // The target address to issue.
+ aelf.Address to = 4;
+}
+
+message TransferInput {
+ // The receiver of the token.
+ aelf.Address to = 1;
+ // The token symbol to transfer.
+ string symbol = 2;
+ // The amount to to transfer.
+ int64 amount = 3;
+ // The memo.
+ string memo = 4;
+}
+
+message LockInput {
+ // The one want to lock his token.
+ aelf.Address address = 1;
+ // Id of the lock.
+ aelf.Hash lock_id = 2;
+ // The symbol of the token to lock.
+ string symbol = 3;
+ // a memo.
+ string usage = 4;
+ // The amount of tokens to lock.
+ int64 amount = 5;
+}
+
+message UnlockInput {
+ // The one want to un-lock his token.
+ aelf.Address address = 1;
+ // Id of the lock.
+ aelf.Hash lock_id = 2;
+ // The symbol of the token to un-lock.
+ string symbol = 3;
+ // a memo.
+ string usage = 4;
+ // The amount of tokens to un-lock.
+ int64 amount = 5;
+}
+
+message TransferFromInput {
+ // The source address of the token.
+ aelf.Address from = 1;
+ // The destination address of the token.
+ aelf.Address to = 2;
+ // The symbol of the token to transfer.
+ string symbol = 3;
+ // The amount to transfer.
+ int64 amount = 4;
+ // The memo.
+ string memo = 5;
+}
+
+message ApproveInput {
+ // The address that allowance will be increased.
+ aelf.Address spender = 1;
+ // The symbol of token to approve.
+ string symbol = 2;
+ // The amount of token to approve.
+ int64 amount = 3;
+}
+message BatchApproveInput {
+ repeated ApproveInput value = 1;
+}
+
+message UnApproveInput {
+ // The address that allowance will be decreased.
+ aelf.Address spender = 1;
+ // The symbol of token to un-approve.
+ string symbol = 2;
+ // The amount of token to un-approve.
+ int64 amount = 3;
+}
+
+message BurnInput {
+ // The symbol of token to burn.
+ string symbol = 1;
+ // The amount of token to burn.
+ int64 amount = 2;
+}
+
+message ChargeResourceTokenInput {
+ // Collection of charge resource token, Symbol->Amount.
+ map cost_dic = 1;
+ // The sender of the transaction.
+ aelf.Address caller = 2;
+}
+
+message TransactionFeeBill {
+ // The transaction fee dictionary, Symbol->fee.
+ map fees_map = 1;
+}
+
+message TransactionFreeFeeAllowanceBill {
+ // The transaction free fee allowance dictionary, Symbol->fee.
+ map free_fee_allowances_map = 1;
+}
+
+message CheckThresholdInput {
+ // The sender of the transaction.
+ aelf.Address sender = 1;
+ // The threshold to set, Symbol->Threshold.
+ map symbol_to_threshold = 2;
+ // Whether to check the allowance.
+ bool is_check_allowance = 3;
+}
+
+message GetTokenInfoInput {
+ // The symbol of token.
+ string symbol = 1;
+}
+
+message GetBalanceInput {
+ // The symbol of token.
+ string symbol = 1;
+ // The target address of the query.
+ aelf.Address owner = 2;
+}
+
+message GetBalanceOutput {
+ // The symbol of token.
+ string symbol = 1;
+ // The target address of the query.
+ aelf.Address owner = 2;
+ // The balance of the owner.
+ int64 balance = 3;
+}
+
+message GetAllowanceInput {
+ // The symbol of token.
+ string symbol = 1;
+ // The address of the token owner.
+ aelf.Address owner = 2;
+ // The address of the spender.
+ aelf.Address spender = 3;
+}
+
+message GetAllowanceOutput {
+ // The symbol of token.
+ string symbol = 1;
+ // The address of the token owner.
+ aelf.Address owner = 2;
+ // The address of the spender.
+ aelf.Address spender = 3;
+ // The amount of allowance.
+ int64 allowance = 4;
+}
+
+message CrossChainTransferInput {
+ // The receiver of transfer.
+ aelf.Address to = 1;
+ // The symbol of token.
+ string symbol = 2;
+ // The amount of token to transfer.
+ int64 amount = 3;
+ // The memo.
+ string memo = 4;
+ // The destination chain id.
+ int32 to_chain_id = 5;
+ // The chain id of the token.
+ int32 issue_chain_id = 6;
+}
+
+message CrossChainReceiveTokenInput {
+ // The source chain id.
+ int32 from_chain_id = 1;
+ // The height of the transfer transaction.
+ int64 parent_chain_height = 2;
+ // The raw bytes of the transfer transaction.
+ bytes transfer_transaction_bytes = 3;
+ // The merkle path created from the transfer transaction.
+ aelf.MerklePath merkle_path = 4;
+}
+
+message IsInWhiteListInput {
+ // The symbol of token.
+ string symbol = 1;
+ // The address to check.
+ aelf.Address address = 2;
+}
+
+message SymbolToPayTxSizeFee{
+ // The symbol of token.
+ string token_symbol = 1;
+ // The charge weight of primary token.
+ int32 base_token_weight = 2;
+ // The new added token charge weight. For example, the charge weight of primary Token is set to 1.
+ // The newly added token charge weight is set to 10. If the transaction requires 1 unit of primary token,
+ // the user can also pay for 10 newly added tokens.
+ int32 added_token_weight = 3;
+}
+
+message SymbolListToPayTxSizeFee{
+ // Transaction fee token information.
+ repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 1;
+}
+
+message ChargeTransactionFeesInput {
+ // The method name of transaction.
+ string method_name = 1;
+ // The contract address of transaction.
+ aelf.Address contract_address = 2;
+ // The amount of transaction size fee.
+ int64 transaction_size_fee = 3;
+ // Transaction fee token information.
+ repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 4;
+}
+
+message ChargeTransactionFeesOutput {
+ // Whether the charge was successful.
+ bool success = 1;
+ // The charging information.
+ string charging_information = 2;
+}
+
+message CallbackInfo {
+ aelf.Address contract_address = 1;
+ string method_name = 2;
+}
+
+message ExtraTokenListModified {
+ option (aelf.is_event) = true;
+ // Transaction fee token information.
+ SymbolListToPayTxSizeFee symbol_list_to_pay_tx_size_fee = 1;
+}
+
+message GetLockedAmountInput {
+ // The address of the lock.
+ aelf.Address address = 1;
+ // The token symbol.
+ string symbol = 2;
+ // The id of the lock.
+ aelf.Hash lock_id = 3;
+}
+
+message GetLockedAmountOutput {
+ // The address of the lock.
+ aelf.Address address = 1;
+ // The token symbol.
+ string symbol = 2;
+ // The id of the lock.
+ aelf.Hash lock_id = 3;
+ // The locked amount.
+ int64 amount = 4;
+}
+
+message TokenInfoList {
+ // List of token information.
+ repeated TokenInfo value = 1;
+}
+
+message GetCrossChainTransferTokenContractAddressInput {
+ // The chain id.
+ int32 chainId = 1;
+}
+
+message CrossChainCreateTokenInput {
+ // The chain id of the chain on which the token was created.
+ int32 from_chain_id = 1;
+ // The height of the transaction that created the token.
+ int64 parent_chain_height = 2;
+ // The transaction that created the token.
+ bytes transaction_bytes = 3;
+ // The merkle path created from the transaction that created the transaction.
+ aelf.MerklePath merkle_path = 4;
+}
+
+message InitializeFromParentChainInput {
+ // The amount of resource.
+ map resource_amount = 1;
+ // The token contract addresses.
+ map registered_other_token_contract_addresses = 2;
+ // The creator the side chain.
+ aelf.Address creator = 3;
+}
+
+message UpdateCoefficientsInput {
+ // The specify pieces gonna update.
+ repeated int32 piece_numbers = 1;
+ // Coefficients of one single type.
+ CalculateFeeCoefficients coefficients = 2;
+}
+
+enum FeeTypeEnum {
+ READ = 0;
+ STORAGE = 1;
+ WRITE = 2;
+ TRAFFIC = 3;
+ TX = 4;
+}
+
+message CalculateFeePieceCoefficients {
+ // Coefficients of one single piece.
+ // The first char is its type: liner / power.
+ // The second char is its piece upper bound.
+ repeated int32 value = 1;
+}
+
+message CalculateFeeCoefficients {
+ // The resource fee type, like READ, WRITE, etc.
+ int32 fee_token_type = 1;
+ // Coefficients of one single piece.
+ repeated CalculateFeePieceCoefficients piece_coefficients_list = 2;
+}
+
+message AllCalculateFeeCoefficients {
+ // The coefficients of fee Calculation.
+ repeated CalculateFeeCoefficients value = 1;
+}
+
+message TotalTransactionFeesMap
+{
+ // Token dictionary that charge transaction fee, Symbol->Amount.
+ map value = 1;
+ // The hash of the block processing the transaction.
+ aelf.Hash block_hash = 2;
+ // The height of the block processing the transaction.
+ int64 block_height = 3;
+}
+
+message TotalResourceTokensMaps {
+ // Resource tokens to charge.
+ repeated ContractTotalResourceTokens value = 1;
+ // The hash of the block processing the transaction.
+ aelf.Hash block_hash = 2;
+ // The height of the block processing the transaction.
+ int64 block_height = 3;
+}
+
+message ContractTotalResourceTokens {
+ // The contract address.
+ aelf.Address contract_address = 1;
+ // Resource tokens to charge.
+ TotalResourceTokensMap tokens_map = 2;
+}
+
+message TotalResourceTokensMap
+{
+ // Resource token dictionary, Symbol->Amount.
+ map value = 1;
+}
+
+message StringList {
+ repeated string value = 1;
+}
+
+message TransactionFeeDelegations{
+ // delegation, symbols and its' amount
+ map delegations = 1;
+ // height when added
+ int64 block_height = 2;
+ //Whether to pay transaction fee continuously
+ bool isUnlimitedDelegate = 3;
+}
+
+message TransactionFeeDelegatees{
+ map delegatees = 1;
+}
+
+message SetTransactionFeeDelegationsInput {
+ // the delegator address
+ aelf.Address delegator_address = 1;
+ // delegation, symbols and its' amount
+ map delegations = 2;
+}
+
+message SetTransactionFeeDelegationsOutput {
+ bool success = 1;
+}
+
+message RemoveTransactionFeeDelegatorInput{
+ // the delegator address
+ aelf.Address delegator_address = 1;
+}
+
+message RemoveTransactionFeeDelegateeInput {
+ // the delegatee address
+ aelf.Address delegatee_address = 1;
+}
+
+message GetTransactionFeeDelegationsOfADelegateeInput {
+ aelf.Address delegatee_address = 1;
+ aelf.Address delegator_address = 2;
+}
+
+message GetTransactionFeeDelegateesInput {
+ aelf.Address delegator_address = 1;
+}
+
+message GetTransactionFeeDelegateesOutput {
+ repeated aelf.Address delegatee_addresses = 1;
+}
+
+message SetSymbolAliasInput {
+ string symbol = 1;
+ string alias = 2;
+}
+
+// Events
+
+message Transferred {
+ option (aelf.is_event) = true;
+ // The source address of the transferred token.
+ aelf.Address from = 1 [(aelf.is_indexed) = true];
+ // The destination address of the transferred token.
+ aelf.Address to = 2 [(aelf.is_indexed) = true];
+ // The symbol of the transferred token.
+ string symbol = 3 [(aelf.is_indexed) = true];
+ // The amount of the transferred token.
+ int64 amount = 4;
+ // The memo.
+ string memo = 5;
+}
+
+message Approved {
+ option (aelf.is_event) = true;
+ // The address of the token owner.
+ aelf.Address owner = 1 [(aelf.is_indexed) = true];
+ // The address that allowance be increased.
+ aelf.Address spender = 2 [(aelf.is_indexed) = true];
+ // The symbol of approved token.
+ string symbol = 3 [(aelf.is_indexed) = true];
+ // The amount of approved token.
+ int64 amount = 4;
+}
+
+message UnApproved {
+ option (aelf.is_event) = true;
+ // The address of the token owner.
+ aelf.Address owner = 1 [(aelf.is_indexed) = true];
+ // The address that allowance be decreased.
+ aelf.Address spender = 2 [(aelf.is_indexed) = true];
+ // The symbol of un-approved token.
+ string symbol = 3 [(aelf.is_indexed) = true];
+ // The amount of un-approved token.
+ int64 amount = 4;
+}
+
+message Burned
+{
+ option (aelf.is_event) = true;
+ // The address who wants to burn token.
+ aelf.Address burner = 1 [(aelf.is_indexed) = true];
+ // The symbol of burned token.
+ string symbol = 2 [(aelf.is_indexed) = true];
+ // The amount of burned token.
+ int64 amount = 3;
+}
+
+message ChainPrimaryTokenSymbolSet {
+ option (aelf.is_event) = true;
+ // The symbol of token.
+ string token_symbol = 1;
+}
+
+message CalculateFeeAlgorithmUpdated {
+ option (aelf.is_event) = true;
+ // All calculate fee coefficients after modification.
+ AllCalculateFeeCoefficients all_type_fee_coefficients = 1;
+}
+
+message RentalCharged {
+ option (aelf.is_event) = true;
+ // The symbol of rental fee charged.
+ string symbol = 1;
+ // The amount of rental fee charged.
+ int64 amount = 2;
+ // The payer of rental fee.
+ aelf.Address payer = 3;
+ // The receiver of rental fee.
+ aelf.Address receiver = 4;
+}
+
+message RentalAccountBalanceInsufficient {
+ option (aelf.is_event) = true;
+ // The symbol of insufficient rental account balance.
+ string symbol = 1;
+ // The balance of the account.
+ int64 amount = 2;
+}
+
+message TokenCreated {
+ option (aelf.is_event) = true;
+ // The symbol of the token.
+ string symbol = 1;
+ // The full name of the token.
+ string token_name = 2;
+ // The total supply of the token.
+ int64 total_supply = 3;
+ // The precision of the token.
+ int32 decimals = 4;
+ // The address that has permission to issue the token.
+ aelf.Address issuer = 5;
+ // A flag indicating if this token is burnable.
+ bool is_burnable = 6;
+ // The chain id of the token.
+ int32 issue_chain_id = 7;
+ // The external information of the token.
+ ExternalInfo external_info = 8;
+ // The address that owns the token.
+ aelf.Address owner = 9;
+}
+
+message Issued {
+ option (aelf.is_event) = true;
+ // The symbol of issued token.
+ string symbol = 1;
+ // The amount of issued token.
+ int64 amount = 2;
+ // The memo.
+ string memo = 3;
+ // The issued target address.
+ aelf.Address to = 4;
+}
+
+message CrossChainTransferred {
+ option (aelf.is_event) = true;
+ // The source address of the transferred token.
+ aelf.Address from = 1;
+ // The destination address of the transferred token.
+ aelf.Address to = 2;
+ // The symbol of the transferred token.
+ string symbol = 3;
+ // The amount of the transferred token.
+ int64 amount = 4;
+ // The memo.
+ string memo = 5;
+ // The destination chain id.
+ int32 to_chain_id = 6;
+ // The chain id of the token.
+ int32 issue_chain_id = 7;
+}
+
+message CrossChainReceived {
+ option (aelf.is_event) = true;
+ // The source address of the transferred token.
+ aelf.Address from = 1;
+ // The destination address of the transferred token.
+ aelf.Address to = 2;
+ // The symbol of the received token.
+ string symbol = 3;
+ // The amount of the received token.
+ int64 amount = 4;
+ // The memo.
+ string memo = 5;
+ // The destination chain id.
+ int32 from_chain_id = 6;
+ // The chain id of the token.
+ int32 issue_chain_id = 7;
+ // The parent chain height of the transfer transaction.
+ int64 parent_chain_height = 8;
+ // The id of transfer transaction.
+ aelf.Hash transfer_transaction_id =9;
+}
+
+message TransactionFeeDelegationAdded {
+ option (aelf.is_event) = true;
+ aelf.Address delegator = 1 [(aelf.is_indexed) = true];
+ aelf.Address delegatee = 2 [(aelf.is_indexed) = true];
+ aelf.Address caller = 3 [(aelf.is_indexed) = true];
+}
+
+message TransactionFeeDelegationCancelled {
+ option (aelf.is_event) = true;
+ aelf.Address delegator = 1 [(aelf.is_indexed) = true];
+ aelf.Address delegatee = 2 [(aelf.is_indexed) = true];
+ aelf.Address caller = 3 [(aelf.is_indexed) = true];
+}
+
+message SymbolAliasAdded {
+ option (aelf.is_event) = true;
+ string symbol = 1 [(aelf.is_indexed) = true];
+ string alias = 2 [(aelf.is_indexed) = true];
+}
+
+message SymbolAliasDeleted {
+ option (aelf.is_event) = true;
+ string symbol = 1 [(aelf.is_indexed) = true];
+ string alias = 2 [(aelf.is_indexed) = true];
+}
+```
+
+#### Contract Reference State
+
+Navigate to `src/` create a **new file** `ContractReferences.cs`
+The implementation of file `src/ContractRefefrence.cs` is as follows:
+
+```csharp
+using AElf.Contracts.MultiToken;
+
+namespace AElf.Contracts.LotteryGame
+{
+ public partial class LotteryGameState
+ {
+ internal TokenContractContainer.TokenContractReferenceState TokenContract { get; set; }
+ }
+}
+```
+
+#### Implement Lottery Game Smart Contract
+
+Navigate to `src/LotteryGame.cs`
+
+```csharp
+using AElf.Contracts.MultiToken;
+using AElf.Sdk.CSharp;
+using AElf.Types;
+using Google.Protobuf.WellKnownTypes;
+
+namespace AElf.Contracts.LotteryGame
+{
+ // Contract class must inherit the base class generated from the proto file
+ public class LotteryGame : LotteryGameContainer.LotteryGameBase
+ {
+ private const string TokenContractAddress = "ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx"; // tDVW token contract address
+ private const string TokenSymbol = "ELF";
+ private const long MinimumPlayAmount = 1_000_000; // 0.01 ELF
+ private const long MaximumPlayAmount = 1_000_000_000; // 10 ELF
+
+ // Initializes the contract
+ public override Empty Initialize(Empty input)
+ {
+ // Check if the contract is already initialized
+ Assert(State.Initialized.Value == false, "Already initialized.");
+ // Set the contract state
+ State.Initialized.Value = true;
+ // Set the owner address
+ State.Owner.Value = Context.Sender;
+
+ // Initialize the token contract
+ State.TokenContract.Value = Address.FromBase58(TokenContractAddress);
+
+ return new Empty();
+ }
+
+ // Plays the lottery game with a specified amount of tokens.
+ // The method checks if the play amount is within the limit.
+ // If the player wins, tokens are transferred from the contract to the sender and a PlayOutcomeEvent is fired with the won amount.
+ // If the player loses, tokens are transferred from the sender to the contract and a PlayOutcomeEvent is fired with the lost amount.
+ public override Empty Play(Int64Value input)
+ {
+ var playAmount = input.Value;
+
+ // Check if input amount is within the limit
+ Assert(playAmount is >= MinimumPlayAmount and <= MaximumPlayAmount, "Invalid play amount.");
+
+ // Check if the sender has enough tokens
+ var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
+ {
+ Owner = Context.Sender,
+ Symbol = TokenSymbol
+ }).Balance;
+ Assert(balance >= playAmount, "Insufficient balance.");
+
+ // Check if the contract has enough tokens
+ var contractBalance = State.TokenContract.GetBalance.Call(new GetBalanceInput
+ {
+ Owner = Context.Self,
+ Symbol = TokenSymbol
+ }).Balance;
+ Assert(contractBalance >= playAmount, "Insufficient contract balance.");
+
+ if(IsWinner())
+ {
+ // Transfer the token from the contract to the sender
+ State.TokenContract.Transfer.Send(new TransferInput
+ {
+ To = Context.Sender,
+ Symbol = TokenSymbol,
+ Amount = playAmount
+ });
+
+ // Emit an event to notify listeners about the outcome
+ Context.Fire(new PlayOutcomeEvent
+ {
+ Amount = input.Value,
+ Won = playAmount
+ });
+ }
+ else
+ {
+ // Transfer the token from the sender to the contract
+ State.TokenContract.TransferFrom.Send(new TransferFromInput
+ {
+ From = Context.Sender,
+ To = Context.Self,
+ Symbol = TokenSymbol,
+ Amount = playAmount
+ });
+
+ // Emit an event to notify listeners about the outcome
+ Context.Fire(new PlayOutcomeEvent
+ {
+ Amount = input.Value,
+ Won = -playAmount
+ });
+ }
+
+ return new Empty();
+ }
+
+ // Withdraws a specified amount of tokens from the contract.
+ // This method can only be called by the owner of the contract.
+ // After the tokens are transferred, a WithdrawEvent is fired to notify any listeners about the withdrawal.
+ public override Empty Withdraw(Int64Value input)
+ {
+ AssertIsOwner();
+
+ // Transfer the token from the contract to the sender
+ State.TokenContract.Transfer.Send(new TransferInput
+ {
+ To = Context.Sender,
+ Symbol = TokenSymbol,
+ Amount = input.Value
+ });
+
+ // Emit an event to notify listeners about the withdrawal
+ Context.Fire(new WithdrawEvent
+ {
+ Amount = input.Value,
+ From = Context.Self,
+ To = State.Owner.Value
+ });
+
+ return new Empty();
+ }
+
+ // Deposits a specified amount of tokens into the contract.
+ // This method can only be called by the owner of the contract.
+ // After the tokens are transferred, a DepositEvent is fired to notify any listeners about the deposit.
+ public override Empty Deposit(Int64Value input)
+ {
+ AssertIsOwner();
+
+ // Transfer the token from the sender to the contract
+ State.TokenContract.TransferFrom.Send(new TransferFromInput
+ {
+ From = Context.Sender,
+ To = Context.Self,
+ Symbol = TokenSymbol,
+ Amount = input.Value
+ });
+
+ // Emit an event to notify listeners about the deposit
+ Context.Fire(new DepositEvent
+ {
+ Amount = input.Value,
+ From = Context.Sender,
+ To = Context.Self
+ });
+
+ return new Empty();
+ }
+
+ // Transfers the ownership of the contract to a new owner.
+ // This method can only be called by the current owner of the contract.
+ public override Empty TransferOwnership(Address input)
+ {
+ AssertIsOwner();
+
+ // Set the new owner address
+ State.Owner.Value = input;
+
+ return new Empty();
+ }
+
+ // A method that read the contract's play amount limit
+ public override PlayAmountLimitMessage GetPlayAmountLimit(Empty input)
+ {
+ // Wrap the value in the return type
+ return new PlayAmountLimitMessage
+ {
+ MinimumAmount = MinimumPlayAmount,
+ MaximumAmount = MaximumPlayAmount
+ };
+ }
+
+ // A method that read the contract's current balance
+ public override Int64Value GetContractBalance(Empty input)
+ {
+ // Get the balance of the contract
+ var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
+ {
+ Owner = Context.Self,
+ Symbol = TokenSymbol
+ }).Balance;
+
+ // Wrap the value in the return type
+ return new Int64Value
+ {
+ Value = balance
+ };
+ }
+
+ // A method that read the contract's owner
+ public override StringValue GetOwner(Empty input)
+ {
+ return State.Owner.Value == null ? new StringValue() : new StringValue {Value = State.Owner.Value.ToBase58()};
+ }
+
+ // Determines if the player is a winner.
+ // This method generates a random number based on the current block height and checks if it's equal to 0.
+ // If the random number is 0, the player is considered a winner.
+ private bool IsWinner()
+ {
+ var randomNumber = Context.CurrentHeight % 2;
+ return randomNumber == 0;
+ }
+
+ // This method is used to ensure that only the owner of the contract can perform certain actions.
+ // If the context sender is not the owner, an exception is thrown with the message "Unauthorized to perform the action."
+ private void AssertIsOwner()
+ {
+ Assert(Context.Sender == State.Owner.Value, "Unauthorized to perform the action.");
+ }
+ }
+
+}
+```
+
+#### Building Smart Contract
+
+Build the new code with the following commands inside `src/` folder:
+
+```bash
+dotnet build
+```
+
+You should see **LotteryGame.dll.patched** in the directory `lottery_game/src/bin/Debug/net.6.0`
+
+## 3. Create A Wallet
+
+To send transactions on the aelf blockchain, you must have a wallet.
+
+Run this command to create aelf wallet.
+
+```bash title="Terminal"
+aelf-command create
+```
+
+![result](/img/create_wallet_output.png)
+
+### 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**
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+
+
+Run the following command to get testnet ELF tokens from faucet. Remember to either export your wallet address and wallet password or replace $WALLET_ADDRESS and $WALLET_ADDRESS with your wallet address and wallet password respectively.
+
+```bash title="Terminal"
+export WALLET_ADDRESS="YOUR_WALLET_ADDRESS"
+curl -X POST "https://faucet.aelf.dev/api/claim?walletAddress=$WALLET_ADDRESS" -H "accept: application/json" -d ""
+```
+
+To check your wallet's current ELF balance:
+
+```bash title="Terminal"
+export WALLET_PASSWORD="YOUR_WALLET_PASSWORD"
+aelf-command call ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io GetBalance
+```
+
+You will be prompted for the following:
+
+```sh
+Enter the required param : **ELF**
+Enter the required param : **$WALLET_ADDRESS**
+```
+
+You should see the Result displaying your wallet's ELF balance.
+
+
+
+
+Go to this url [https://faucet-ui.aelf.dev](https://faucet-ui.aelf.dev). Enter your address and click `Get Tokens`.
+
+![result](/img/get-token-ui.png)
+
+
+
+
+## 4. Deploying your first smart contract on testnet
+
+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 LotteryGame.dll.patched to CONTRACT_PATH. Remember to export CONTRACT_FILE equals to LotteryGame.
+
+```bash
+export CONTRACT_PATH="SRC_DIRECTORY_PATH" + /bin/Debug/net6.0
+export CONTRACT_FILE=LotteryGame
+aelf-deploy -a $WALLET_ADDRESS -p $WALLET_PASSWORD -c $CONTRACT_PATH/$CONTRACT_FILE.dll.patched -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.
+
+![result](/img/deploy-result.png)
+
+## 5. Connecting to your smart contract on testnet (lottery)
+
+### 5.1 Approving Smart Contract Spending
+
+```bash
+aelf-command send ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Approve
+```
+
+`ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx` is the contract address of **Multitoken Contract** on aelf Testnet Sidechain (tDVW).
+When prompted, enter the following parameters to approve the spending of 90 ELF tokens:
+
+```terminal
+Enter the params one by one, type `Enter` to skip optional param:
+? Enter the required param : $CONTRACT_ADDRESS
+? Enter the required param : ELF
+? Enter the required param : 9000000000
+```
+
+### 5.2 Initializing Lottery Game Contract
+
+```bash
+aelf-command send $CONTRACT_ADDRESS -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Initialize
+```
+
+### 5.3 Depositing funds into the Lottery Game Contract
+
+```bash
+aelf-command send $CONTRACT_ADDRESS -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Deposit
+```
+
+### 5.4 Playing the Lottery Game
+
+```bash
+aelf-command send $CONTRACT_ADDRESS -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Play
+```
+
+Let's check the **balance**
+
+```bash
+aelf-command call ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io GetBalance
+```
+
+You will be prompted for the following:
+
+```terminal
+Enter the required param : ELF
+Enter the required param : $WALLET_ADDRESS
+```