logo

Lottery Game Contract

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#

  • Basic knowledge of terminal commands
  • IDE - Install VS Code
  • Install Required Packages

  • Install dotnet 6.0 SDK
  • Install aelf contract templates
  • 1
    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
  • 1
    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
  • Install Yarn
  • Install aelf-command Linux and macOs

    1
    sudo npm i -g aelf-command

    Window

    1
    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.

    Step 2 - Develop Smart Contract#

    Start Your Smart Contract Project#

  • Open your Terminal.
  • Enter the following command to generate a new project:
  • 1
    mkdir lottery-game
    2
    cd lottery-game
    3
    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.
  • 1
    cd src

    Defining Methods and Messages#

  • Rename the file name from Protobuf/contract/hello_world_contract.proto to lottery_game_contract.proto:
  • 1
    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:

    1
    syntax = "proto3";
    2
    3
    import "aelf/core.proto";
    4
    import "aelf/options.proto";
    5
    import "google/protobuf/empty.proto";
    6
    import "google/protobuf/wrappers.proto";
    7
    import "Protobuf/reference/acs12.proto";
    8
    // The namespace of this class
    9
    option csharp_namespace = "AElf.Contracts.LotteryGame";
    10
    11
    service LotteryGame {
    12
    // The name of the state class the smart contract is going to use to access blockchain state
    13
    option (aelf.csharp_state) = "AElf.Contracts.LotteryGame.LotteryGameState";
    14
    option (aelf.base) = "Protobuf/reference/acs12.proto";
    15
    16
    rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) {
    17
    }
    18
    19
    rpc Play (google.protobuf.Int64Value) returns (google.protobuf.Empty) {
    20
    }
    21
    22
    rpc Withdraw (google.protobuf.Int64Value) returns (google.protobuf.Empty) {
    23
    }
    24
    25
    rpc Deposit (google.protobuf.Int64Value) returns (google.protobuf.Empty) {
    26
    }
    27
    28
    rpc TransferOwnership (aelf.Address) returns (google.protobuf.Empty) {
    29
    }
    30
    31
    rpc GetPlayAmountLimit (google.protobuf.Empty) returns (PlayAmountLimitMessage) {
    32
    option (aelf.is_view) = true;
    33
    }
    34
    35
    rpc GetContractBalance (google.protobuf.Empty) returns (google.protobuf.Int64Value) {
    36
    option (aelf.is_view) = true;
    37
    }
    38
    39
    rpc GetOwner (google.protobuf.Empty) returns (google.protobuf.StringValue) {
    40
    option (aelf.is_view) = true;
    41
    }
    42
    }
    43
    44
    // An event that will be emitted from contract method call when Play is called.
    45
    message PlayOutcomeEvent {
    46
    option (aelf.is_event) = true;
    47
    int64 amount = 1;
    48
    int64 won = 2;
    49
    }
    50
    51
    // An event that will be emitted from contract method call when Withdraw is called.
    52
    message WithdrawEvent {
    53
    option (aelf.is_event) = true;
    54
    int64 amount = 1;
    55
    aelf.Address from = 2;
    56
    aelf.Address to = 3;
    57
    }
    58
    59
    // An event that will be emitted from contract method call when Deposit is called.
    60
    message DepositEvent {
    61
    option (aelf.is_event) = true;
    62
    int64 amount = 1;
    63
    aelf.Address from = 2;
    64
    aelf.Address to = 3;
    65
    }
    66
    67
    // The message containing the play amount limits
    68
    message PlayAmountLimitMessage {
    69
    int64 minimumAmount = 1;
    70
    int64 maximumAmount = 2;
    71
    }

    Define Contract States#

    The implementation of file src/LotteryGameState.cs is as follows:

    1
    using AElf.Sdk.CSharp.State;
    2
    using AElf.Types;
    3
    4
    namespace AElf.Contracts.LotteryGame
    5
    {
    6
    // The state class is access the blockchain state
    7
    public partial class LotteryGameState : ContractState
    8
    {
    9
    // A state to check if contract is initialized
    10
    public BoolState Initialized { get; set; }
    11
    // A state to store the owner address
    12
    public SingletonState<Address> Owner { get; set; }
    13
    }
    14
    }

    Contract Reference State#

  • Create a new file token_contract.proto under src/Protobuf/reference/.
  • Replace this code of implementation file of token_contract.proto:
  • 1
    /**
    2
    * MultiToken contract.
    3
    */
    4
    syntax = "proto3";
    5
    6
    package token;
    7
    8
    import "aelf/core.proto";
    9
    import "aelf/options.proto";
    10
    import "google/protobuf/empty.proto";
    11
    import "google/protobuf/wrappers.proto";
    12
    13
    option csharp_namespace = "AElf.Contracts.MultiToken";
    14
    15
    service TokenContract {
    16
    // Create a new token.
    17
    rpc Create (CreateInput) returns (google.protobuf.Empty) {
    18
    }
    19
    20
    // Issuing some amount of tokens to an address is the action of increasing that addresses balance
    21
    // for the given token. The total amount of issued tokens must not exceed the total supply of the token
    22
    // and only the issuer (creator) of the token can issue tokens.
    23
    // Issuing tokens effectively increases the circulating supply.
    24
    rpc Issue (IssueInput) returns (google.protobuf.Empty) {
    25
    }
    26
    27
    // Transferring tokens simply is the action of transferring a given amount of tokens from one address to another.
    28
    // The origin or source address is the signer of the transaction.
    29
    // The balance of the sender must be higher than the amount that is transferred.
    30
    rpc Transfer (TransferInput) returns (google.protobuf.Empty) {
    31
    }
    32
    33
    // The TransferFrom action will transfer a specified amount of tokens from one address to another.
    34
    // For this operation to succeed the from address needs to have approved (see allowances) enough tokens
    35
    // to Sender of this transaction. If successful the amount will be removed from the allowance.
    36
    rpc TransferFrom (TransferFromInput) returns (google.protobuf.Empty) {
    37
    }
    38
    39
    // The approve action increases the allowance from the Sender to the Spender address,
    40
    // enabling the Spender to call TransferFrom.
    41
    rpc Approve (ApproveInput) returns (google.protobuf.Empty) {
    42
    }
    43
    44
    rpc BatchApprove (BatchApproveInput) returns (google.protobuf.Empty) {
    45
    }
    46
    47
    // This is the reverse operation for Approve, it will decrease the allowance.
    48
    rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) {
    49
    }
    50
    51
    // This method can be used to lock tokens.
    52
    rpc Lock (LockInput) returns (google.protobuf.Empty) {
    53
    }
    54
    55
    // This is the reverse operation of locking, it un-locks some previously locked tokens.
    56
    rpc Unlock (UnlockInput) returns (google.protobuf.Empty) {
    57
    }
    58
    59
    // This action will burn the specified amount of tokens, removing them from the token’s Supply.
    60
    rpc Burn (BurnInput) returns (google.protobuf.Empty) {
    61
    }
    62
    63
    // Set the primary token of side chain.
    64
    rpc SetPrimaryTokenSymbol (SetPrimaryTokenSymbolInput) returns (google.protobuf.Empty) {
    65
    }
    66
    67
    // This interface is used for cross-chain transfer.
    68
    rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) {
    69
    }
    70
    71
    // This method is used to receive cross-chain transfers.
    72
    rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) {
    73
    }
    74
    75
    // The side chain creates tokens.
    76
    rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) {
    77
    }
    78
    79
    // When the side chain is started, the side chain is initialized with the parent chain information.
    80
    rpc InitializeFromParentChain (InitializeFromParentChainInput) returns (google.protobuf.Empty) {
    81
    }
    82
    83
    // Handle the transaction fees charged by ChargeTransactionFees.
    84
    rpc ClaimTransactionFees (TotalTransactionFeesMap) returns (google.protobuf.Empty) {
    85
    }
    86
    87
    // Used to collect transaction fees.
    88
    rpc ChargeTransactionFees (ChargeTransactionFeesInput) returns (ChargeTransactionFeesOutput) {
    89
    }
    90
    91
    rpc ChargeUserContractTransactionFees(ChargeTransactionFeesInput) returns(ChargeTransactionFeesOutput){
    92
    93
    }
    94
    95
    // Check the token threshold.
    96
    rpc CheckThreshold (CheckThresholdInput) returns (google.protobuf.Empty) {
    97
    }
    98
    99
    // Initialize coefficients of every type of tokens supporting charging fee.
    100
    rpc InitialCoefficients (google.protobuf.Empty) returns (google.protobuf.Empty){
    101
    }
    102
    103
    // Processing resource token received.
    104
    rpc DonateResourceToken (TotalResourceTokensMaps) returns (google.protobuf.Empty) {
    105
    }
    106
    107
    // A transaction resource fee is charged to implement the ACS8 standards.
    108
    rpc ChargeResourceToken (ChargeResourceTokenInput) returns (google.protobuf.Empty) {
    109
    }
    110
    111
    // Verify that the resource token are sufficient.
    112
    rpc CheckResourceToken (google.protobuf.Empty) returns (google.protobuf.Empty) {
    113
    }
    114
    115
    // Set the list of tokens to pay transaction fees.
    116
    rpc SetSymbolsToPayTxSizeFee (SymbolListToPayTxSizeFee) returns (google.protobuf.Empty){
    117
    }
    118
    119
    // Update the coefficient of the transaction fee calculation formula.
    120
    rpc UpdateCoefficientsForSender (UpdateCoefficientsInput) returns (google.protobuf.Empty) {
    121
    }
    122
    123
    // Update the coefficient of the transaction fee calculation formula.
    124
    rpc UpdateCoefficientsForContract (UpdateCoefficientsInput) returns (google.protobuf.Empty) {
    125
    }
    126
    127
    // This method is used to initialize the governance organization for some functions,
    128
    // including: the coefficient of the user transaction fee calculation formula,
    129
    // the coefficient of the contract developer resource fee calculation formula, and the side chain rental fee.
    130
    rpc InitializeAuthorizedController (google.protobuf.Empty) returns (google.protobuf.Empty){
    131
    }
    132
    133
    rpc AddAddressToCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) {
    134
    }
    135
    rpc RemoveAddressFromCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) {
    136
    }
    137
    138
    rpc SetTransactionFeeDelegations (SetTransactionFeeDelegationsInput) returns (SetTransactionFeeDelegationsOutput){
    139
    }
    140
    141
    rpc RemoveTransactionFeeDelegator (RemoveTransactionFeeDelegatorInput) returns (google.protobuf.Empty){
    142
    }
    143
    144
    rpc RemoveTransactionFeeDelegatee (RemoveTransactionFeeDelegateeInput) returns (google.protobuf.Empty){
    145
    }
    146
    147
    rpc SetSymbolAlias (SetSymbolAliasInput) returns (google.protobuf.Empty){
    148
    }
    149
    150
    // Get all delegatees' address of delegator from input
    151
    rpc GetTransactionFeeDelegatees (GetTransactionFeeDelegateesInput) returns (GetTransactionFeeDelegateesOutput) {
    152
    option (aelf.is_view) = true;
    153
    }
    154
    155
    // Query token information.
    156
    rpc GetTokenInfo (GetTokenInfoInput) returns (TokenInfo) {
    157
    option (aelf.is_view) = true;
    158
    }
    159
    160
    // Query native token information.
    161
    rpc GetNativeTokenInfo (google.protobuf.Empty) returns (TokenInfo) {
    162
    option (aelf.is_view) = true;
    163
    }
    164
    165
    // Query resource token information.
    166
    rpc GetResourceTokenInfo (google.protobuf.Empty) returns (TokenInfoList) {
    167
    option (aelf.is_view) = true;
    168
    }
    169
    170
    // Query the balance at the specified address.
    171
    rpc GetBalance (GetBalanceInput) returns (GetBalanceOutput) {
    172
    option (aelf.is_view) = true;
    173
    }
    174
    175
    // Query the account's allowance for other addresses
    176
    rpc GetAllowance (GetAllowanceInput) returns (GetAllowanceOutput) {
    177
    option (aelf.is_view) = true;
    178
    }
    179
    180
    // Query the account's available allowance for other addresses
    181
    rpc GetAvailableAllowance (GetAllowanceInput) returns (GetAllowanceOutput) {
    182
    option (aelf.is_view) = true;
    183
    }
    184
    185
    // Check whether the token is in the whitelist of an address,
    186
    // which can be called TransferFrom to transfer the token under the condition of not being credited.
    187
    rpc IsInWhiteList (IsInWhiteListInput) returns (google.protobuf.BoolValue) {
    188
    option (aelf.is_view) = true;
    189
    }
    190
    191
    // Query the information for a lock.
    192
    rpc GetLockedAmount (GetLockedAmountInput) returns (GetLockedAmountOutput) {
    193
    option (aelf.is_view) = true;
    194
    }
    195
    196
    // Query the address of receiving token in cross-chain transfer.
    197
    rpc GetCrossChainTransferTokenContractAddress (GetCrossChainTransferTokenContractAddressInput) returns (aelf.Address) {
    198
    option (aelf.is_view) = true;
    199
    }
    200
    201
    // Query the name of the primary Token.
    202
    rpc GetPrimaryTokenSymbol (google.protobuf.Empty) returns (google.protobuf.StringValue) {
    203
    option (aelf.is_view) = true;
    204
    }
    205
    206
    // Query the coefficient of the transaction fee calculation formula.
    207
    rpc GetCalculateFeeCoefficientsForContract (google.protobuf.Int32Value) returns (CalculateFeeCoefficients) {
    208
    option (aelf.is_view) = true;
    209
    }
    210
    211
    // Query the coefficient of the transaction fee calculation formula.
    212
    rpc GetCalculateFeeCoefficientsForSender (google.protobuf.Empty) returns (CalculateFeeCoefficients) {
    213
    option (aelf.is_view) = true;
    214
    }
    215
    216
    // Query tokens that can pay transaction fees.
    217
    rpc GetSymbolsToPayTxSizeFee (google.protobuf.Empty) returns (SymbolListToPayTxSizeFee){
    218
    option (aelf.is_view) = true;
    219
    }
    220
    221
    // Query the hash of the last input of ClaimTransactionFees.
    222
    rpc GetLatestTotalTransactionFeesMapHash (google.protobuf.Empty) returns (aelf.Hash){
    223
    option (aelf.is_view) = true;
    224
    }
    225
    226
    // Query the hash of the last input of DonateResourceToken.
    227
    rpc GetLatestTotalResourceTokensMapsHash (google.protobuf.Empty) returns (aelf.Hash){
    228
    option (aelf.is_view) = true;
    229
    }
    230
    rpc IsTokenAvailableForMethodFee (google.protobuf.StringValue) returns (google.protobuf.BoolValue) {
    231
    option (aelf.is_view) = true;
    232
    }
    233
    rpc GetReservedExternalInfoKeyList (google.protobuf.Empty) returns (StringList) {
    234
    option (aelf.is_view) = true;
    235
    }
    236
    237
    rpc GetTransactionFeeDelegationsOfADelegatee(GetTransactionFeeDelegationsOfADelegateeInput) returns(TransactionFeeDelegations){
    238
    option (aelf.is_view) = true;
    239
    }
    240
    241
    rpc GetTokenAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) {
    242
    option (aelf.is_view) = true;
    243
    }
    244
    245
    rpc GetSymbolByAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) {
    246
    option (aelf.is_view) = true;
    247
    }
    248
    }
    249
    250
    message TokenInfo {
    251
    // The symbol of the token.f
    252
    string symbol = 1;
    253
    // The full name of the token.
    254
    string token_name = 2;
    255
    // The current supply of the token.
    256
    int64 supply = 3;
    257
    // The total supply of the token.
    258
    int64 total_supply = 4;
    259
    // The precision of the token.
    260
    int32 decimals = 5;
    261
    // The address that has permission to issue the token.
    262
    aelf.Address issuer = 6;
    263
    // A flag indicating if this token is burnable.
    264
    bool is_burnable = 7;
    265
    // The chain id of the token.
    266
    int32 issue_chain_id = 8;
    267
    // The amount of issued tokens.
    268
    int64 issued = 9;
    269
    // The external information of the token.
    270
    ExternalInfo external_info = 10;
    271
    // The address that owns the token.
    272
    aelf.Address owner = 11;
    273
    }
    274
    275
    message ExternalInfo {
    276
    map<string, string> value = 1;
    277
    }
    278
    279
    message CreateInput {
    280
    // The symbol of the token.
    281
    string symbol = 1;
    282
    // The full name of the token.
    283
    string token_name = 2;
    284
    // The total supply of the token.
    285
    int64 total_supply = 3;
    286
    // The precision of the token
    287
    int32 decimals = 4;
    288
    // The address that has permission to issue the token.
    289
    aelf.Address issuer = 5;
    290
    // A flag indicating if this token is burnable.
    291
    bool is_burnable = 6;
    292
    // A whitelist address list used to lock tokens.
    293
    repeated aelf.Address lock_white_list = 7;
    294
    // The chain id of the token.
    295
    int32 issue_chain_id = 8;
    296
    // The external information of the token.
    297
    ExternalInfo external_info = 9;
    298
    // The address that owns the token.
    299
    aelf.Address owner = 10;
    300
    }
    301
    302
    message SetPrimaryTokenSymbolInput {
    303
    // The symbol of the token.
    304
    string symbol = 1;
    305
    }
    306
    307
    message IssueInput {
    308
    // The token symbol to issue.
    309
    string symbol = 1;
    310
    // The token amount to issue.
    311
    int64 amount = 2;
    312
    // The memo.
    313
    string memo = 3;
    314
    // The target address to issue.
    315
    aelf.Address to = 4;
    316
    }
    317
    318
    message TransferInput {
    319
    // The receiver of the token.
    320
    aelf.Address to = 1;
    321
    // The token symbol to transfer.
    322
    string symbol = 2;
    323
    // The amount to to transfer.
    324
    int64 amount = 3;
    325
    // The memo.
    326
    string memo = 4;
    327
    }
    328
    329
    message LockInput {
    330
    // The one want to lock his token.
    331
    aelf.Address address = 1;
    332
    // Id of the lock.
    333
    aelf.Hash lock_id = 2;
    334
    // The symbol of the token to lock.
    335
    string symbol = 3;
    336
    // a memo.
    337
    string usage = 4;
    338
    // The amount of tokens to lock.
    339
    int64 amount = 5;
    340
    }
    341
    342
    message UnlockInput {
    343
    // The one want to un-lock his token.
    344
    aelf.Address address = 1;
    345
    // Id of the lock.
    346
    aelf.Hash lock_id = 2;
    347
    // The symbol of the token to un-lock.
    348
    string symbol = 3;
    349
    // a memo.
    350
    string usage = 4;
    351
    // The amount of tokens to un-lock.
    352
    int64 amount = 5;
    353
    }
    354
    355
    message TransferFromInput {
    356
    // The source address of the token.
    357
    aelf.Address from = 1;
    358
    // The destination address of the token.
    359
    aelf.Address to = 2;
    360
    // The symbol of the token to transfer.
    361
    string symbol = 3;
    362
    // The amount to transfer.
    363
    int64 amount = 4;
    364
    // The memo.
    365
    string memo = 5;
    366
    }
    367
    368
    message ApproveInput {
    369
    // The address that allowance will be increased.
    370
    aelf.Address spender = 1;
    371
    // The symbol of token to approve.
    372
    string symbol = 2;
    373
    // The amount of token to approve.
    374
    int64 amount = 3;
    375
    }
    376
    message BatchApproveInput {
    377
    repeated ApproveInput value = 1;
    378
    }
    379
    380
    message UnApproveInput {
    381
    // The address that allowance will be decreased.
    382
    aelf.Address spender = 1;
    383
    // The symbol of token to un-approve.
    384
    string symbol = 2;
    385
    // The amount of token to un-approve.
    386
    int64 amount = 3;
    387
    }
    388
    389
    message BurnInput {
    390
    // The symbol of token to burn.
    391
    string symbol = 1;
    392
    // The amount of token to burn.
    393
    int64 amount = 2;
    394
    }
    395
    396
    message ChargeResourceTokenInput {
    397
    // Collection of charge resource token, Symbol->Amount.
    398
    map<string, int64> cost_dic = 1;
    399
    // The sender of the transaction.
    400
    aelf.Address caller = 2;
    401
    }
    402
    403
    message TransactionFeeBill {
    404
    // The transaction fee dictionary, Symbol->fee.
    405
    map<string, int64> fees_map = 1;
    406
    }
    407
    408
    message TransactionFreeFeeAllowanceBill {
    409
    // The transaction free fee allowance dictionary, Symbol->fee.
    410
    map<string, int64> free_fee_allowances_map = 1;
    411
    }
    412
    413
    message CheckThresholdInput {
    414
    // The sender of the transaction.
    415
    aelf.Address sender = 1;
    416
    // The threshold to set, Symbol->Threshold.
    417
    map<string, int64> symbol_to_threshold = 2;
    418
    // Whether to check the allowance.
    419
    bool is_check_allowance = 3;
    420
    }
    421
    422
    message GetTokenInfoInput {
    423
    // The symbol of token.
    424
    string symbol = 1;
    425
    }
    426
    427
    message GetBalanceInput {
    428
    // The symbol of token.
    429
    string symbol = 1;
    430
    // The target address of the query.
    431
    aelf.Address owner = 2;
    432
    }
    433
    434
    message GetBalanceOutput {
    435
    // The symbol of token.
    436
    string symbol = 1;
    437
    // The target address of the query.
    438
    aelf.Address owner = 2;
    439
    // The balance of the owner.
    440
    int64 balance = 3;
    441
    }
    442
    443
    message GetAllowanceInput {
    444
    // The symbol of token.
    445
    string symbol = 1;
    446
    // The address of the token owner.
    447
    aelf.Address owner = 2;
    448
    // The address of the spender.
    449
    aelf.Address spender = 3;
    450
    }
    451
    452
    message GetAllowanceOutput {
    453
    // The symbol of token.
    454
    string symbol = 1;
    455
    // The address of the token owner.
    456
    aelf.Address owner = 2;
    457
    // The address of the spender.
    458
    aelf.Address spender = 3;
    459
    // The amount of allowance.
    460
    int64 allowance = 4;
    461
    }
    462
    463
    message CrossChainTransferInput {
    464
    // The receiver of transfer.
    465
    aelf.Address to = 1;
    466
    // The symbol of token.
    467
    string symbol = 2;
    468
    // The amount of token to transfer.
    469
    int64 amount = 3;
    470
    // The memo.
    471
    string memo = 4;
    472
    // The destination chain id.
    473
    int32 to_chain_id = 5;
    474
    // The chain id of the token.
    475
    int32 issue_chain_id = 6;
    476
    }
    477
    478
    message CrossChainReceiveTokenInput {
    479
    // The source chain id.
    480
    int32 from_chain_id = 1;
    481
    // The height of the transfer transaction.
    482
    int64 parent_chain_height = 2;
    483
    // The raw bytes of the transfer transaction.
    484
    bytes transfer_transaction_bytes = 3;
    485
    // The merkle path created from the transfer transaction.
    486
    aelf.MerklePath merkle_path = 4;
    487
    }
    488
    489
    message IsInWhiteListInput {
    490
    // The symbol of token.
    491
    string symbol = 1;
    492
    // The address to check.
    493
    aelf.Address address = 2;
    494
    }
    495
    496
    message SymbolToPayTxSizeFee{
    497
    // The symbol of token.
    498
    string token_symbol = 1;
    499
    // The charge weight of primary token.
    500
    int32 base_token_weight = 2;
    501
    // The new added token charge weight. For example, the charge weight of primary Token is set to 1.
    502
    // The newly added token charge weight is set to 10. If the transaction requires 1 unit of primary token,
    503
    // the user can also pay for 10 newly added tokens.
    504
    int32 added_token_weight = 3;
    505
    }
    506
    507
    message SymbolListToPayTxSizeFee{
    508
    // Transaction fee token information.
    509
    repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 1;
    510
    }
    511
    512
    message ChargeTransactionFeesInput {
    513
    // The method name of transaction.
    514
    string method_name = 1;
    515
    // The contract address of transaction.
    516
    aelf.Address contract_address = 2;
    517
    // The amount of transaction size fee.
    518
    int64 transaction_size_fee = 3;
    519
    // Transaction fee token information.
    520
    repeated SymbolToPayTxSizeFee symbols_to_pay_tx_size_fee = 4;
    521
    }
    522
    523
    message ChargeTransactionFeesOutput {
    524
    // Whether the charge was successful.
    525
    bool success = 1;
    526
    // The charging information.
    527
    string charging_information = 2;
    528
    }
    529
    530
    message CallbackInfo {
    531
    aelf.Address contract_address = 1;
    532
    string method_name = 2;
    533
    }
    534
    535
    message ExtraTokenListModified {
    536
    option (aelf.is_event) = true;
    537
    // Transaction fee token information.
    538
    SymbolListToPayTxSizeFee symbol_list_to_pay_tx_size_fee = 1;
    539
    }
    540
    541
    message GetLockedAmountInput {
    542
    // The address of the lock.
    543
    aelf.Address address = 1;
    544
    // The token symbol.
    545
    string symbol = 2;
    546
    // The id of the lock.
    547
    aelf.Hash lock_id = 3;
    548
    }
    549
    550
    message GetLockedAmountOutput {
    551
    // The address of the lock.
    552
    aelf.Address address = 1;
    553
    // The token symbol.
    554
    string symbol = 2;
    555
    // The id of the lock.
    556
    aelf.Hash lock_id = 3;
    557
    // The locked amount.
    558
    int64 amount = 4;
    559
    }
    560
    561
    message TokenInfoList {
    562
    // List of token information.
    563
    repeated TokenInfo value = 1;
    564
    }
    565
    566
    message GetCrossChainTransferTokenContractAddressInput {
    567
    // The chain id.
    568
    int32 chainId = 1;
    569
    }
    570
    571
    message CrossChainCreateTokenInput {
    572
    // The chain id of the chain on which the token was created.
    573
    int32 from_chain_id = 1;
    574
    // The height of the transaction that created the token.
    575
    int64 parent_chain_height = 2;
    576
    // The transaction that created the token.
    577
    bytes transaction_bytes = 3;
    578
    // The merkle path created from the transaction that created the transaction.
    579
    aelf.MerklePath merkle_path = 4;
    580
    }
    581
    582
    message InitializeFromParentChainInput {
    583
    // The amount of resource.
    584
    map<string, int32> resource_amount = 1;
    585
    // The token contract addresses.
    586
    map<int32, aelf.Address> registered_other_token_contract_addresses = 2;
    587
    // The creator the side chain.
    588
    aelf.Address creator = 3;
    589
    }
    590
    591
    message UpdateCoefficientsInput {
    592
    // The specify pieces gonna update.
    593
    repeated int32 piece_numbers = 1;
    594
    // Coefficients of one single type.
    595
    CalculateFeeCoefficients coefficients = 2;
    596
    }
    597
    598
    enum FeeTypeEnum {
    599
    READ = 0;
    600
    STORAGE = 1;
    601
    WRITE = 2;
    602
    TRAFFIC = 3;
    603
    TX = 4;
    604
    }
    605
    606
    message CalculateFeePieceCoefficients {
    607
    // Coefficients of one single piece.
    608
    // The first char is its type: liner / power.
    609
    // The second char is its piece upper bound.
    610
    repeated int32 value = 1;
    611
    }
    612
    613
    message CalculateFeeCoefficients {
    614
    // The resource fee type, like READ, WRITE, etc.
    615
    int32 fee_token_type = 1;
    616
    // Coefficients of one single piece.
    617
    repeated CalculateFeePieceCoefficients piece_coefficients_list = 2;
    618
    }
    619
    620
    message AllCalculateFeeCoefficients {
    621
    // The coefficients of fee Calculation.
    622
    repeated CalculateFeeCoefficients value = 1;
    623
    }
    624
    625
    message TotalTransactionFeesMap
    626
    {
    627
    // Token dictionary that charge transaction fee, Symbol->Amount.
    628
    map<string, int64> value = 1;
    629
    // The hash of the block processing the transaction.
    630
    aelf.Hash block_hash = 2;
    631
    // The height of the block processing the transaction.
    632
    int64 block_height = 3;
    633
    }
    634
    635
    message TotalResourceTokensMaps {
    636
    // Resource tokens to charge.
    637
    repeated ContractTotalResourceTokens value = 1;
    638
    // The hash of the block processing the transaction.
    639
    aelf.Hash block_hash = 2;
    640
    // The height of the block processing the transaction.
    641
    int64 block_height = 3;
    642
    }
    643
    644
    message ContractTotalResourceTokens {
    645
    // The contract address.
    646
    aelf.Address contract_address = 1;
    647
    // Resource tokens to charge.
    648
    TotalResourceTokensMap tokens_map = 2;
    649
    }
    650
    651
    message TotalResourceTokensMap
    652
    {
    653
    // Resource token dictionary, Symbol->Amount.
    654
    map<string, int64> value = 1;
    655
    }
    656
    657
    message StringList {
    658
    repeated string value = 1;
    659
    }
    660
    661
    message TransactionFeeDelegations{
    662
    // delegation, symbols and its' amount
    663
    map<string, int64> delegations = 1;
    664
    // height when added
    665
    int64 block_height = 2;
    666
    //Whether to pay transaction fee continuously
    667
    bool isUnlimitedDelegate = 3;
    668
    }
    669
    670
    message TransactionFeeDelegatees{
    671
    map<string,TransactionFeeDelegations> delegatees = 1;
    672
    }
    673
    674
    message SetTransactionFeeDelegationsInput {
    675
    // the delegator address
    676
    aelf.Address delegator_address = 1;
    677
    // delegation, symbols and its' amount
    678
    map<string, int64> delegations = 2;
    679
    }
    680
    681
    message SetTransactionFeeDelegationsOutput {
    682
    bool success = 1;
    683
    }
    684
    685
    message RemoveTransactionFeeDelegatorInput{
    686
    // the delegator address
    687
    aelf.Address delegator_address = 1;
    688
    }
    689
    690
    message RemoveTransactionFeeDelegateeInput {
    691
    // the delegatee address
    692
    aelf.Address delegatee_address = 1;
    693
    }
    694
    695
    message GetTransactionFeeDelegationsOfADelegateeInput {
    696
    aelf.Address delegatee_address = 1;
    697
    aelf.Address delegator_address = 2;
    698
    }
    699
    700
    message GetTransactionFeeDelegateesInput {
    701
    aelf.Address delegator_address = 1;
    702
    }
    703
    704
    message GetTransactionFeeDelegateesOutput {
    705
    repeated aelf.Address delegatee_addresses = 1;
    706
    }
    707
    708
    message SetSymbolAliasInput {
    709
    string symbol = 1;
    710
    string alias = 2;
    711
    }
    712
    713
    // Events
    714
    715
    message Transferred {
    716
    option (aelf.is_event) = true;
    717
    // The source address of the transferred token.
    718
    aelf.Address from = 1 [(aelf.is_indexed) = true];
    719
    // The destination address of the transferred token.
    720
    aelf.Address to = 2 [(aelf.is_indexed) = true];
    721
    // The symbol of the transferred token.
    722
    string symbol = 3 [(aelf.is_indexed) = true];
    723
    // The amount of the transferred token.
    724
    int64 amount = 4;
    725
    // The memo.
    726
    string memo = 5;
    727
    }
    728
    729
    message Approved {
    730
    option (aelf.is_event) = true;
    731
    // The address of the token owner.
    732
    aelf.Address owner = 1 [(aelf.is_indexed) = true];
    733
    // The address that allowance be increased.
    734
    aelf.Address spender = 2 [(aelf.is_indexed) = true];
    735
    // The symbol of approved token.
    736
    string symbol = 3 [(aelf.is_indexed) = true];
    737
    // The amount of approved token.
    738
    int64 amount = 4;
    739
    }
    740
    741
    message UnApproved {
    742
    option (aelf.is_event) = true;
    743
    // The address of the token owner.
    744
    aelf.Address owner = 1 [(aelf.is_indexed) = true];
    745
    // The address that allowance be decreased.
    746
    aelf.Address spender = 2 [(aelf.is_indexed) = true];
    747
    // The symbol of un-approved token.
    748
    string symbol = 3 [(aelf.is_indexed) = true];
    749
    // The amount of un-approved token.
    750
    int64 amount = 4;
    751
    }
    752
    753
    message Burned
    754
    {
    755
    option (aelf.is_event) = true;
    756
    // The address who wants to burn token.
    757
    aelf.Address burner = 1 [(aelf.is_indexed) = true];
    758
    // The symbol of burned token.
    759
    string symbol = 2 [(aelf.is_indexed) = true];
    760
    // The amount of burned token.
    761
    int64 amount = 3;
    762
    }
    763
    764
    message ChainPrimaryTokenSymbolSet {
    765
    option (aelf.is_event) = true;
    766
    // The symbol of token.
    767
    string token_symbol = 1;
    768
    }
    769
    770
    message CalculateFeeAlgorithmUpdated {
    771
    option (aelf.is_event) = true;
    772
    // All calculate fee coefficients after modification.
    773
    AllCalculateFeeCoefficients all_type_fee_coefficients = 1;
    774
    }
    775
    776
    message RentalCharged {
    777
    option (aelf.is_event) = true;
    778
    // The symbol of rental fee charged.
    779
    string symbol = 1;
    780
    // The amount of rental fee charged.
    781
    int64 amount = 2;
    782
    // The payer of rental fee.
    783
    aelf.Address payer = 3;
    784
    // The receiver of rental fee.
    785
    aelf.Address receiver = 4;
    786
    }
    787
    788
    message RentalAccountBalanceInsufficient {
    789
    option (aelf.is_event) = true;
    790
    // The symbol of insufficient rental account balance.
    791
    string symbol = 1;
    792
    // The balance of the account.
    793
    int64 amount = 2;
    794
    }
    795
    796
    message TokenCreated {
    797
    option (aelf.is_event) = true;
    798
    // The symbol of the token.
    799
    string symbol = 1;
    800
    // The full name of the token.
    801
    string token_name = 2;
    802
    // The total supply of the token.
    803
    int64 total_supply = 3;
    804
    // The precision of the token.
    805
    int32 decimals = 4;
    806
    // The address that has permission to issue the token.
    807
    aelf.Address issuer = 5;
    808
    // A flag indicating if this token is burnable.
    809
    bool is_burnable = 6;
    810
    // The chain id of the token.
    811
    int32 issue_chain_id = 7;
    812
    // The external information of the token.
    813
    ExternalInfo external_info = 8;
    814
    // The address that owns the token.
    815
    aelf.Address owner = 9;
    816
    }
    817
    818
    message Issued {
    819
    option (aelf.is_event) = true;
    820
    // The symbol of issued token.
    821
    string symbol = 1;
    822
    // The amount of issued token.
    823
    int64 amount = 2;
    824
    // The memo.
    825
    string memo = 3;
    826
    // The issued target address.
    827
    aelf.Address to = 4;
    828
    }
    829
    830
    message CrossChainTransferred {
    831
    option (aelf.is_event) = true;
    832
    // The source address of the transferred token.
    833
    aelf.Address from = 1;
    834
    // The destination address of the transferred token.
    835
    aelf.Address to = 2;
    836
    // The symbol of the transferred token.
    837
    string symbol = 3;
    838
    // The amount of the transferred token.
    839
    int64 amount = 4;
    840
    // The memo.
    841
    string memo = 5;
    842
    // The destination chain id.
    843
    int32 to_chain_id = 6;
    844
    // The chain id of the token.
    845
    int32 issue_chain_id = 7;
    846
    }
    847
    848
    message CrossChainReceived {
    849
    option (aelf.is_event) = true;
    850
    // The source address of the transferred token.
    851
    aelf.Address from = 1;
    852
    // The destination address of the transferred token.
    853
    aelf.Address to = 2;
    854
    // The symbol of the received token.
    855
    string symbol = 3;
    856
    // The amount of the received token.
    857
    int64 amount = 4;
    858
    // The memo.
    859
    string memo = 5;
    860
    // The destination chain id.
    861
    int32 from_chain_id = 6;
    862
    // The chain id of the token.
    863
    int32 issue_chain_id = 7;
    864
    // The parent chain height of the transfer transaction.
    865
    int64 parent_chain_height = 8;
    866
    // The id of transfer transaction.
    867
    aelf.Hash transfer_transaction_id =9;
    868
    }
    869
    870
    message TransactionFeeDelegationAdded {
    871
    option (aelf.is_event) = true;
    872
    aelf.Address delegator = 1 [(aelf.is_indexed) = true];
    873
    aelf.Address delegatee = 2 [(aelf.is_indexed) = true];
    874
    aelf.Address caller = 3 [(aelf.is_indexed) = true];
    875
    }
    876
    877
    message TransactionFeeDelegationCancelled {
    878
    option (aelf.is_event) = true;
    879
    aelf.Address delegator = 1 [(aelf.is_indexed) = true];
    880
    aelf.Address delegatee = 2 [(aelf.is_indexed) = true];
    881
    aelf.Address caller = 3 [(aelf.is_indexed) = true];
    882
    }
    883
    884
    message SymbolAliasAdded {
    885
    option (aelf.is_event) = true;
    886
    string symbol = 1 [(aelf.is_indexed) = true];
    887
    string alias = 2 [(aelf.is_indexed) = true];
    888
    }
    889
    890
    message SymbolAliasDeleted {
    891
    option (aelf.is_event) = true;
    892
    string symbol = 1 [(aelf.is_indexed) = true];
    893
    string alias = 2 [(aelf.is_indexed) = true];
    894
    }

    Contract Reference State#

  • Navigate to src
  • Create a new file ContractReferences.cs
  • The implementation of file src/ContractRefefrence.cs is as follows:
  • 1
    using AElf.Contracts.MultiToken;
    2
    3
    namespace AElf.Contracts.LotteryGame
    4
    {
    5
    public partial class LotteryGameState
    6
    {
    7
    internal TokenContractContainer.TokenContractReferenceState TokenContract { get; set; }
    8
    }
    9
    }

    Implement Lottery Game Smart Contract#

    Navigate to src/LotteryGame.cs

    1
    using AElf.Contracts.MultiToken;
    2
    using AElf.Sdk.CSharp;
    3
    using AElf.Types;
    4
    using Google.Protobuf.WellKnownTypes;
    5
    6
    namespace AElf.Contracts.LotteryGame
    7
    {
    8
    // Contract class must inherit the base class generated from the proto file
    9
    public class LotteryGame : LotteryGameContainer.LotteryGameBase
    10
    {
    11
    private const string TokenContractAddress = "ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx"; // tDVW token contract address
    12
    private const string TokenSymbol = "ELF";
    13
    private const long MinimumPlayAmount = 1_000_000; // 0.01 ELF
    14
    private const long MaximumPlayAmount = 1_000_000_000; // 10 ELF
    15
    16
    // Initializes the contract
    17
    public override Empty Initialize(Empty input)
    18
    {
    19
    // Check if the contract is already initialized
    20
    Assert(State.Initialized.Value == false, "Already initialized.");
    21
    // Set the contract state
    22
    State.Initialized.Value = true;
    23
    // Set the owner address
    24
    State.Owner.Value = Context.Sender;
    25
    26
    // Initialize the token contract
    27
    State.TokenContract.Value = Address.FromBase58(TokenContractAddress);
    28
    29
    return new Empty();
    30
    }
    31
    32
    // Plays the lottery game with a specified amount of tokens.
    33
    // The method checks if the play amount is within the limit.
    34
    // If the player wins, tokens are transferred from the contract to the sender and a PlayOutcomeEvent is fired with the won amount.
    35
    // If the player loses, tokens are transferred from the sender to the contract and a PlayOutcomeEvent is fired with the lost amount.
    36
    public override Empty Play(Int64Value input)
    37
    {
    38
    var playAmount = input.Value;
    39
    40
    // Check if input amount is within the limit
    41
    Assert(playAmount is >= MinimumPlayAmount and <= MaximumPlayAmount, "Invalid play amount.");
    42
    43
    // Check if the sender has enough tokens
    44
    var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
    45
    {
    46
    Owner = Context.Sender,
    47
    Symbol = TokenSymbol
    48
    }).Balance;
    49
    Assert(balance >= playAmount, "Insufficient balance.");
    50
    51
    // Check if the contract has enough tokens
    52
    var contractBalance = State.TokenContract.GetBalance.Call(new GetBalanceInput
    53
    {
    54
    Owner = Context.Self,
    55
    Symbol = TokenSymbol
    56
    }).Balance;
    57
    Assert(contractBalance >= playAmount, "Insufficient contract balance.");
    58
    59
    if(IsWinner())
    60
    {
    61
    // Transfer the token from the contract to the sender
    62
    State.TokenContract.Transfer.Send(new TransferInput
    63
    {
    64
    To = Context.Sender,
    65
    Symbol = TokenSymbol,
    66
    Amount = playAmount
    67
    });
    68
    69
    // Emit an event to notify listeners about the outcome
    70
    Context.Fire(new PlayOutcomeEvent
    71
    {
    72
    Amount = input.Value,
    73
    Won = playAmount
    74
    });
    75
    }
    76
    else
    77
    {
    78
    // Transfer the token from the sender to the contract
    79
    State.TokenContract.TransferFrom.Send(new TransferFromInput
    80
    {
    81
    From = Context.Sender,
    82
    To = Context.Self,
    83
    Symbol = TokenSymbol,
    84
    Amount = playAmount
    85
    });
    86
    87
    // Emit an event to notify listeners about the outcome
    88
    Context.Fire(new PlayOutcomeEvent
    89
    {
    90
    Amount = input.Value,
    91
    Won = -playAmount
    92
    });
    93
    }
    94
    95
    return new Empty();
    96
    }
    97
    98
    // Withdraws a specified amount of tokens from the contract.
    99
    // This method can only be called by the owner of the contract.
    100
    // After the tokens are transferred, a WithdrawEvent is fired to notify any listeners about the withdrawal.
    101
    public override Empty Withdraw(Int64Value input)
    102
    {
    103
    AssertIsOwner();
    104
    105
    // Transfer the token from the contract to the sender
    106
    State.TokenContract.Transfer.Send(new TransferInput
    107
    {
    108
    To = Context.Sender,
    109
    Symbol = TokenSymbol,
    110
    Amount = input.Value
    111
    });
    112
    113
    // Emit an event to notify listeners about the withdrawal
    114
    Context.Fire(new WithdrawEvent
    115
    {
    116
    Amount = input.Value,
    117
    From = Context.Self,
    118
    To = State.Owner.Value
    119
    });
    120
    121
    return new Empty();
    122
    }
    123
    124
    // Deposits a specified amount of tokens into the contract.
    125
    // This method can only be called by the owner of the contract.
    126
    // After the tokens are transferred, a DepositEvent is fired to notify any listeners about the deposit.
    127
    public override Empty Deposit(Int64Value input)
    128
    {
    129
    AssertIsOwner();
    130
    131
    // Transfer the token from the sender to the contract
    132
    State.TokenContract.TransferFrom.Send(new TransferFromInput
    133
    {
    134
    From = Context.Sender,
    135
    To = Context.Self,
    136
    Symbol = TokenSymbol,
    137
    Amount = input.Value
    138
    });
    139
    140
    // Emit an event to notify listeners about the deposit
    141
    Context.Fire(new DepositEvent
    142
    {
    143
    Amount = input.Value,
    144
    From = Context.Sender,
    145
    To = Context.Self
    146
    });
    147
    148
    return new Empty();
    149
    }
    150
    151
    // Transfers the ownership of the contract to a new owner.
    152
    // This method can only be called by the current owner of the contract.
    153
    public override Empty TransferOwnership(Address input)
    154
    {
    155
    AssertIsOwner();
    156
    157
    // Set the new owner address
    158
    State.Owner.Value = input;
    159
    160
    return new Empty();
    161
    }
    162
    163
    // A method that read the contract's play amount limit
    164
    public override PlayAmountLimitMessage GetPlayAmountLimit(Empty input)
    165
    {
    166
    // Wrap the value in the return type
    167
    return new PlayAmountLimitMessage
    168
    {
    169
    MinimumAmount = MinimumPlayAmount,
    170
    MaximumAmount = MaximumPlayAmount
    171
    };
    172
    }
    173
    174
    // A method that read the contract's current balance
    175
    public override Int64Value GetContractBalance(Empty input)
    176
    {
    177
    // Get the balance of the contract
    178
    var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput
    179
    {
    180
    Owner = Context.Self,
    181
    Symbol = TokenSymbol
    182
    }).Balance;
    183
    184
    // Wrap the value in the return type
    185
    return new Int64Value
    186
    {
    187
    Value = balance
    188
    };
    189
    }
    190
    191
    // A method that read the contract's owner
    192
    public override StringValue GetOwner(Empty input)
    193
    {
    194
    return State.Owner.Value == null ? new StringValue() : new StringValue {Value = State.Owner.Value.ToBase58()};
    195
    }
    196
    197
    // Determines if the player is a winner.
    198
    // This method generates a random number based on the current block height and checks if it's equal to 0.
    199
    // If the random number is 0, the player is considered a winner.
    200
    private bool IsWinner()
    201
    {
    202
    var randomNumber = Context.CurrentHeight % 2;
    203
    return randomNumber == 0;
    204
    }
    205
    206
    // This method is used to ensure that only the owner of the contract can perform certain actions.
    207
    // If the context sender is not the owner, an exception is thrown with the message "Unauthorized to perform the action."
    208
    private void AssertIsOwner()
    209
    {
    210
    Assert(Context.Sender == State.Owner.Value, "Unauthorized to perform the action.");
    211
    }
    212
    }
    213
    214
    }

    Building Smart Contract#

  • Build the new code with the following commands inside src folder:
  • 1
    dotnet build

    You should see LotteryGame.dll.patched in the directory lottery_game/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.
  • 1
    aelf-command create
  • You will be prompted to save your account, please do save your account as shown below:
  • 1
    ? Save account info into a file? (Y/n) Y

    Make sure to choose Y to save your account information.

  • Next, enter and confirm your password. Then export your wallet password as shown below:
  • 1
    export 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

  • CLI
  • Web
  • 1. Get Testnet ELF Tokens:

    To receive testnet ELF tokens, run this command after replacing $WALLET_ADDRESS and $WALLET_PASSWORD with your wallet details:

    1
    export WALLET_ADDRESS="YOUR_WALLET_ADDRESS"
    2
    curl -X POST "https://faucet.aelf.dev/api/claim?walletAddress=$WALLET_ADDRESS" -H "accept: application/json" -d ""

    2. Check ELF Balance:

    To check your ELF balance, use:

    1
    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:

    1
    Enter the required param <symbol>: ELF
    2
    Enter the required param <owner>: $WALLET_ADDRESS

    You should see the result displaying your wallet's ELF balance.

    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 LotteryGame.dll.patched to CONTRACT_PATH.

    1
    export CONTRACT_PATH=$(find ~+ . -path "*patched*" | head -n 1)
    1
    aelf-deploy -a $WALLET_ADDRESS -p $WALLET_PASSWORD -c $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.

    Export your smart contract address:

    1
    export CONTRACT_ADDRESS="YOUR_SMART_CONTRACT_ADDRESS e.g. 2LUmicHyH4RXrMjG4beDwuDsiWJESyLkgkwPdGTR8kahRzq5XS"

    Step 4 - Interact with Your Deployed Smart Contract#

    Approving Smart Contract Spending#

    1
    aelf-command send ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Approve

    When prompted, enter the following parameters to approve the spending of 90 ELF tokens:

    1
    Enter the params one by one, type `Enter` to skip optional param:
    2
    ? Enter the required param <spender>: "INSERT_YOUR_CONTRACT_ADDRESS_HERE"
    3
    ? Enter the required param <symbol>: ELF
    4
    ? Enter the required param <amount>: 9000000000

    Initializing Lottery Game Contract#

    1
    aelf-command send $CONTRACT_ADDRESS -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Initialize

    Depositing funds into the Lottery Game Contract#

    1
    aelf-command send $CONTRACT_ADDRESS -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Deposit

    Playing the Lottery Game#

    1
    aelf-command send $CONTRACT_ADDRESS -a $WALLET_ADDRESS -p $WALLET_PASSWORD -e https://tdvw-test-node.aelf.io Play

    Let's check the balance

    1
    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:

    1
    Enter the required param <symbol>: ELF
    2
    Enter the required param <owner>: $WALLET_ADDRESS

    Edited on: 18 July 2024 03:45:20 GMT+0