bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 41쪽 중 11쪽
kahRzq5XS"
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
Lets try to call methods on your newly-deployed smart contract using
aelf-command
.
Firstly, we will set a message using the
Update
method. Run the following command,
and enter the message argument as
test
. This will set
test
into the Message contract state.
Remember to export CONTRACT_ADDRESS equals to your deployed contract address.
Terminal
aelf-command send
$CONTRACT_ADDRESS
-a
$WALLET_ADDRESS
-p
$WALLET_PASSWORD
-e
https://tdvw-test-node.aelf.io Update
After that, we can use
Read
method to retrieve the value previously set for the Message contract state.
Running the following command should yield
test
.
Terminal
aelf-command call
$CONTRACT_ADDRESS
-a
$WALLET_ADDRESS
-p
$WALLET_PASSWORD
-e
https://tdvw-test-node.aelf.io Read
🎯 Conclusion
🎊 Congratulations!
You've successfully completed the Hello World Contract tutorial!
Awesome job sticking with it until the end. 🌟
📚 What You've Learned
In this tutorial, you've discovered:
🛠️ How to set up your development environment for aelf.
💻 The basics of writing a smart contract in C# for the aelf blockchain.
🚀 How to deploy and interact with your Hello World Contract on the aelf network.
🔍 Final Output
By now, you should have:
📜 A deployed Hello World Contract on the aelf blockchain.
🎉 Successfully invoked the contract to set and read a message.
To verify, you should have seen the
test
message returned when you called the Read method after setting it using the Update method. If you see this message, you've nailed it! 🏆
➡️ What's Next?
Now that you've got the basics down, why not dive into more advanced topics or other tutorials like the Lottery Game Contract or Voting Contract?
Keep experimenting and building to level up your aelf blockchain development skills. 🚀
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
🎯 Conclusion
## /quick-start/developers/lottery-game-smart-contract
On this page
Description
: This contract is moderately complex. It demonstrates the use of
state variables, user interactions, and random number generation to create a
basic lottery game.
Purpose
: To introduce you to more advanced concepts such as state
management, event handling, and randomization in smart contracts.
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
lottery-game
cd
lottery-game
dotnet new aelf
-n
LotteryGame
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.
Enter this command in your
Terminal
.
Terminal
cd
src
Defining Methods and Messages
Rename the file name from
Protobuf/contract/hello_world_contract.proto
to
lottery_game_contract.proto
:
Terminal
mv
Protobuf/contract/hello_world_contract.proto Protobuf/contract/lottery_game_contract.proto
open the project with your IDE.
The implementation of file
src/Protobuf/contract/lottery_game_contract.proto
is as follows:
lottery_game_contract.proto
syntax
=
"proto3"
;
import
"aelf/core.proto"
;
import
"aelf/options.proto"
;
import
"google/protobuf/empty.proto"
;
import
"google/protobuf/wrappers.proto"
;
import
"Protobuf/reference/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
)
=
"Protobuf/reference/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:
src/LotteryGameState.cs
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
<
Address
>
Owner
{
get
;
set
;
}
}
}
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
// 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
<
string
,
string
>
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
.
Add프로젝트로 돌아가기