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 aelf-command
  • 1
    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.

    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.

    1
    cd src

    Defining Methods and Messages#

    Firstly, rename 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

    Next, 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/ The implementation of file 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 and 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

    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
  • Run the following command to get testnet ELF tokens from faucet. Remember to either export your wallet address and wallet password or replace π‘Šπ΄πΏπΏπΈπ‘‡π΄π·π·π‘…πΈπ‘†π‘†π‘Žπ‘›π‘‘WALLETADDRESSandWALLET_ADDRESS with your wallet address and wallet password respectively.

    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 ""

    To check your wallet's current ELF 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

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

    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: 12 July 2024 06:02:04 GMT+0