diff --git a/contracts/examples/lottery-esdt/scenarios/buy-all-tickets-different-accounts.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-all-tickets-different-accounts.scen.json index 97ac4a1238..e2922a0e62 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-all-tickets-different-accounts.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-all-tickets-different-accounts.scen.json @@ -51,7 +51,7 @@ "id": "buy-ticket-acc1", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -77,7 +77,7 @@ "id": "buy-ticket-acc2", "tx": { "from": "address:acc2", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -103,7 +103,7 @@ "id": "buy-ticket-acc3", "tx": { "from": "address:acc3", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -129,7 +129,7 @@ "id": "buy-ticket-acc4", "tx": { "from": "address:acc4", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -155,7 +155,7 @@ "id": "buy-ticket-acc5", "tx": { "from": "address:acc5", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -184,6 +184,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -209,7 +214,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -223,19 +228,31 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:2|u8:75|u8:25", - "6-prize_pool": "biguint:500" + "6-prize_pool": "biguint:500", + "7-unawarded_amount": "biguint:500" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "5", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "address:acc2", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:3": "address:acc3", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:4": "address:acc4", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:5": "address:acc5", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc2": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc3": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc4": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc5": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "2", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:3": "3", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:4": "4", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:5": "5", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:2": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:3": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:4": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:5": "1", + "str:addressToIdMapper|str:lastId": "5", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2", + "str:addressToIdMapper|str:addr|address:acc3": "3", + "str:addressToIdMapper|str:addrId|u64:3": "address:acc3", + "str:addressToIdMapper|str:addr|address:acc4": "4", + "str:addressToIdMapper|str:addrId|u64:4": "address:acc4", + "str:addressToIdMapper|str:addr|address:acc5": "5", + "str:addressToIdMapper|str:addrId|u64:5": "address:acc5", "+": "" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/buy-more-tickets-than-allowed.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-more-tickets-than-allowed.scen.json index 0a4db9473b..d126467bf5 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-more-tickets-than-allowed.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-more-tickets-than-allowed.scen.json @@ -23,7 +23,7 @@ "id": "buy-more-tickets-than-allowed", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -53,6 +53,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "2", "balance": "1,000,000", @@ -66,7 +71,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -80,11 +85,18 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:2|u8:75|u8:25", - "6-prize_pool": "biguint:100" + "6-prize_pool": "biguint:100", + "7-unawarded_amount": "biguint:100" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "2", + "str:addressToIdMapper|str:lastId": "3", + "str:addressToIdMapper|str:addr|address:my_address": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:my_address", + "str:addressToIdMapper|str:addr|address:acc1": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "3", + "str:addressToIdMapper|str:addrId|u64:3": "address:acc2", "+": "" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-deadline.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-deadline.scen.json index 275c1ebbdb..dd31f101fe 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-deadline.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-deadline.scen.json @@ -26,7 +26,7 @@ "id": "buy-ticket-after-deadline", "tx": { "from": "address:acc2", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -56,6 +56,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -69,7 +74,7 @@ }, "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -83,11 +88,15 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:100" + "6-prize_pool": "biguint:100", + "7-unawarded_amount": "biguint:100" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-determined-winner.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-determined-winner.scen.json index f1fe7fbc7f..c7e5f29e0f 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-determined-winner.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-determined-winner.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket-after-announced-winner", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -49,7 +49,12 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "3", + "nonce": "2", + "balance": "1,000,000", + "storage": {} + }, + "address:other_shard_address#41": { + "nonce": "1", "balance": "1,000,000", "storage": {} }, @@ -66,10 +71,16 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", - "storage": {}, + "storage": { + "str:addressToIdMapper|str:lastId": "2", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2" + }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } } diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-sold-out.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-sold-out.scen.json index 9f3b23542b..6830a698dc 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-sold-out.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-after-sold-out.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket-after-sold-out", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -53,6 +53,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "2", "balance": "1,000,000", @@ -66,7 +71,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -80,13 +85,19 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:200" + "6-prize_pool": "biguint:200", + "7-unawarded_amount": "biguint:200" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "2", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "address:acc2", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc2": "1" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "2", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:2": "1", + "str:addressToIdMapper|str:lastId": "2", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-all-options.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-all-options.scen.json index 181bb2b6ae..441006ab70 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-all-options.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-all-options.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -52,6 +52,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -62,7 +67,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -76,11 +81,13 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:2|u8:75|u8:25", - "6-prize_pool": "biguint:100" + "6-prize_pool": "biguint:100", + "7-unawarded_amount": "biguint:100" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "2", + "str:addressToIdMapper|str:addr|address:my_address": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:my_address", "+": "" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-another-account.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-another-account.scen.json index 98d9e37bf2..1fbcd6f725 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-another-account.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-another-account.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket-2-another-account", "tx": { "from": "address:acc2", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -52,6 +52,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -62,7 +67,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -76,13 +81,19 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:200" + "6-prize_pool": "biguint:200", + "7-unawarded_amount": "biguint:200" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "2", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "address:acc2", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc2": "1" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "2", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:2": "1", + "str:addressToIdMapper|str:lastId": "2", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-not-on-whitelist.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-not-on-whitelist.scen.json index 0fa30523d7..fe4b320a9d 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-not-on-whitelist.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-not-on-whitelist.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket-not-on-whitelist", "tx": { "from": "address:acc3", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -53,6 +53,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -71,7 +76,7 @@ }, "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -82,7 +87,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:2|u8:75|u8:25", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" }, "+": "" }, diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-same-account.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-same-account.scen.json index 485b5746ad..c5a24adaad 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-same-account.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-same-account.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket-2-same-account", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -52,6 +52,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "2", "balance": "1,000,000", @@ -62,7 +67,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -76,12 +81,16 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:200" + "6-prize_pool": "biguint:200", + "7-unawarded_amount": "biguint:200" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "2", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "2" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "2", + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-second-lottery.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-second-lottery.scen.json index 84b5e87b55..8743c3accb 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-second-lottery.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-second-lottery.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket-2nd-lottery", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTO-123456", @@ -52,6 +52,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "2", "balance": "1,000,000", @@ -62,7 +67,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -76,7 +81,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" }, "str:lotteryInfo|nested:str:lottery_$$$$": { "0-token_identifier": "nested:str:LOTTO-123456", @@ -85,11 +91,15 @@ "3-deadline": "u64:234,567", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:500" + "6-prize_pool": "biguint:500", + "7-unawarded_amount": "biguint:500" }, "str:ticketHolder|nested:str:lottery_$$$$|str:.len": "1", - "str:ticketHolder|nested:str:lottery_$$$$|str:.item|u32:1": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_$$$$|address:acc1": "1" + "str:ticketHolder|nested:str:lottery_$$$$|str:.item|u32:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_$$$$|u64:1": "1", + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket-wrong-fee.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket-wrong-fee.scen.json index f07677239e..6ae0e7e1a8 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket-wrong-fee.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket-wrong-fee.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -53,6 +53,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -66,7 +71,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -77,7 +82,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/buy-ticket.scen.json b/contracts/examples/lottery-esdt/scenarios/buy-ticket.scen.json index fec1ff000c..df2fd13eaa 100644 --- a/contracts/examples/lottery-esdt/scenarios/buy-ticket.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/buy-ticket.scen.json @@ -23,7 +23,7 @@ "id": "buy-ticket", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -52,6 +52,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -62,7 +67,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -76,11 +81,15 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:100" + "6-prize_pool": "biguint:100", + "7-unawarded_amount": "biguint:100" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/complex-prize-distribution.scen.json b/contracts/examples/lottery-esdt/scenarios/complex-prize-distribution.scen.json index 60d817e065..eb3a7927a8 100644 --- a/contracts/examples/lottery-esdt/scenarios/complex-prize-distribution.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/complex-prize-distribution.scen.json @@ -13,6 +13,11 @@ "nonce": "1", "balance": "0" }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "0" @@ -53,7 +58,7 @@ "nonce": "0", "balance": "0" }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "60700", "storage": { @@ -64,29 +69,51 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:10|u8:50|u8:25|u8:10|u8:5|u8:5|u8:1|u8:1|u8:1|u8:1|u8:1", - "6-prize_pool": "biguint:60700" + "6-prize_pool": "biguint:60700", + "7-unawarded_amount": "biguint:60700" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "10", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "address:acc2", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:3": "address:acc3", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:4": "address:acc4", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:5": "address:acc5", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:6": "address:acc6", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:7": "address:acc7", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:8": "address:acc8", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:9": "address:acc9", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:10": "address:acc10", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc2": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc3": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc4": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc5": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc6": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc7": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc8": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc9": "1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc10": "1" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "2", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:3": "3", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:4": "4", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:5": "5", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:6": "6", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:7": "7", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:8": "8", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:9": "9", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:10": "10", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:2": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:3": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:4": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:5": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:6": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:7": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:8": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:9": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:10": "1", + "str:addressToIdMapper|str:lastId": "10", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2", + "str:addressToIdMapper|str:addr|address:acc3": "3", + "str:addressToIdMapper|str:addrId|u64:3": "address:acc3", + "str:addressToIdMapper|str:addr|address:acc4": "4", + "str:addressToIdMapper|str:addrId|u64:4": "address:acc4", + "str:addressToIdMapper|str:addr|address:acc5": "5", + "str:addressToIdMapper|str:addrId|u64:5": "address:acc5", + "str:addressToIdMapper|str:addr|address:acc6": "6", + "str:addressToIdMapper|str:addrId|u64:6": "address:acc6", + "str:addressToIdMapper|str:addr|address:acc7": "7", + "str:addressToIdMapper|str:addrId|u64:7": "address:acc7", + "str:addressToIdMapper|str:addr|address:acc8": "8", + "str:addressToIdMapper|str:addrId|u64:8": "address:acc8", + "str:addressToIdMapper|str:addr|address:acc9": "9", + "str:addressToIdMapper|str:addrId|u64:9": "address:acc9", + "str:addressToIdMapper|str:addr|address:acc10": "01", + "str:addressToIdMapper|str:addrId|u64:10": "address:acc10" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } @@ -100,7 +127,7 @@ "id": "determine-winner-same-ticket-holder", "tx": { "from": "address:my_address", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "determine_winner", "arguments": [ "str:lottery_name" @@ -109,7 +136,9 @@ "gasPrice": "0" }, "expect": { - "out": [], + "out": [ + "1" + ], "status": "0", "gas": "*", "refund": "*" @@ -173,7 +202,7 @@ "balance": "3,035", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": {}, diff --git a/contracts/examples/lottery-esdt/scenarios/determine-winner-different-ticket-holders-winner-acc1.scen.json b/contracts/examples/lottery-esdt/scenarios/determine-winner-different-ticket-holders-winner-acc1.scen.json index c835331c1a..4cce58a60b 100644 --- a/contracts/examples/lottery-esdt/scenarios/determine-winner-different-ticket-holders-winner-acc1.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/determine-winner-different-ticket-holders-winner-acc1.scen.json @@ -15,8 +15,8 @@ { "step": "scCall", "tx": { - "from": "address:my_address", - "to": "sc:lottery", + "from": "address:other_shard_address#41", + "to": "sc:lottery#42", "function": "determine_winner", "arguments": [ "str:lottery_name" @@ -24,6 +24,27 @@ "gasLimit": "100,000,000", "gasPrice": "0" }, + "expect": { + "out": [ + "1" + ], + "status": "0", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "tx": { + "from": "address:acc1", + "to": "sc:lottery#42", + "function": "claim_rewards", + "arguments": [ + "str:LOTTERY-123456" + ], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, "expect": { "out": [], "status": "0", @@ -35,13 +56,18 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "3", + "nonce": "2", "balance": "1,000,000", "storage": {} }, - "address:acc1": { + "address:other_shard_address#41": { "nonce": "1", "balance": "1,000,000", + "storage": {} + }, + "address:acc1": { + "nonce": "2", + "balance": "1,000,000", "esdt": { "str:LOTTERY-123456": "200" }, @@ -50,12 +76,21 @@ "address:acc2": { "nonce": "1", "balance": "1,000,000", + "esdt": { + "str:LOTTERY-123456": "0" + }, "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", - "storage": {}, + "storage": { + "str:addressToIdMapper|str:lastId": "2", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2" + }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } } diff --git a/contracts/examples/lottery-esdt/scenarios/determine-winner-early.scen.json b/contracts/examples/lottery-esdt/scenarios/determine-winner-early.scen.json index 0fcad25ec0..d19ab4da87 100644 --- a/contracts/examples/lottery-esdt/scenarios/determine-winner-early.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/determine-winner-early.scen.json @@ -17,7 +17,7 @@ "id": "status-test", "tx": { "from": "address:my_address", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "status", "arguments": [ "str:lottery_name" @@ -39,7 +39,7 @@ "id": "determine-winner-early", "tx": { "from": "address:my_address", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "determine_winner", "arguments": [ "str:lottery_name" @@ -63,6 +63,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -73,7 +78,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -87,11 +92,15 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:100" + "6-prize_pool": "biguint:100", + "7-unawarded_amount": "biguint:100" }, "str:ticketHolder|nested:str:lottery_name|str:.len": "1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "1" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "1", + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } diff --git a/contracts/examples/lottery-esdt/scenarios/determine-winner-same-ticket-holder.scen.json b/contracts/examples/lottery-esdt/scenarios/determine-winner-same-ticket-holder.scen.json index 64f4d475ab..14c4ffa741 100644 --- a/contracts/examples/lottery-esdt/scenarios/determine-winner-same-ticket-holder.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/determine-winner-same-ticket-holder.scen.json @@ -17,7 +17,7 @@ "id": "determine-winner-same-ticket-holder", "tx": { "from": "address:my_address", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "determine_winner", "arguments": [ "str:lottery_name" @@ -25,6 +25,27 @@ "gasLimit": "100,000,000", "gasPrice": "0" }, + "expect": { + "out": [ + "1" + ], + "status": "0", + "gas": "*", + "refund": "*" + } + }, + { + "step": "scCall", + "tx": { + "from": "address:acc1", + "to": "sc:lottery#42", + "function": "claim_rewards", + "arguments": [ + "str:LOTTERY-123456" + ], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, "expect": { "out": [], "status": "0", @@ -40,8 +61,13 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { - "nonce": "2", + "nonce": "3", "balance": "1,000,000", "esdt": { "str:LOTTERY-123456": "200" @@ -53,10 +79,14 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", - "storage": {}, + "storage": { + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" + }, "code": "mxsc:../output/lottery-esdt.mxsc.json" } } diff --git a/contracts/examples/lottery-esdt/scenarios/determine-winner-split-prize-pool.scen.json b/contracts/examples/lottery-esdt/scenarios/determine-winner-split-prize-pool.scen.json index 7071e71f3c..bd29bd11e5 100644 --- a/contracts/examples/lottery-esdt/scenarios/determine-winner-split-prize-pool.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/determine-winner-split-prize-pool.scen.json @@ -18,7 +18,7 @@ "id": "determine-winner-with-split-prize-pool", "tx": { "from": "address:acc4", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "determine_winner", "arguments": [ "str:lottery_name" @@ -27,7 +27,9 @@ "gasPrice": "0" }, "expect": { - "out": [], + "out": [ + "1" + ], "status": "0", "gas": "*", "refund": "*" @@ -41,6 +43,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -72,7 +79,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": {}, diff --git a/contracts/examples/lottery-esdt/scenarios/lottery-init.scen.json b/contracts/examples/lottery-esdt/scenarios/lottery-init.scen.json index 607af2736e..dc6fd2e377 100644 --- a/contracts/examples/lottery-esdt/scenarios/lottery-init.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/lottery-init.scen.json @@ -9,6 +9,10 @@ "nonce": "0", "balance": "1,000,000" }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000" + }, "address:acc1": { "nonce": "0", "balance": "1,000,000" @@ -22,7 +26,7 @@ { "creatorAddress": "address:my_address", "creatorNonce": "0", - "newAddress": "sc:lottery" + "newAddress": "sc:lottery#42" } ] }, @@ -51,6 +55,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -61,7 +70,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": {}, diff --git a/contracts/examples/lottery-esdt/scenarios/lottery-with-burn-percentage.scen.json b/contracts/examples/lottery-esdt/scenarios/lottery-with-burn-percentage.scen.json index 9d4876f388..4922763e37 100644 --- a/contracts/examples/lottery-esdt/scenarios/lottery-with-burn-percentage.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/lottery-with-burn-percentage.scen.json @@ -12,8 +12,8 @@ "comment": "try starting lottery without setting local burn role", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -47,7 +47,7 @@ "str:LOTTERY-123456": "200" } }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -67,8 +67,8 @@ "comment": "start lottery after setting local burn role", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -94,7 +94,7 @@ { "step": "checkState", "accounts": { - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -112,7 +112,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" }, "str:burnPercentageForLottery|nested:str:lottery_name": "50" }, @@ -126,7 +127,7 @@ "id": "buy first ticket", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -153,7 +154,7 @@ "id": "buy second ticket", "tx": { "from": "address:acc1", - "to": "sc:lottery", + "to": "sc:lottery#42", "esdtValue": [ { "tokenIdentifier": "str:LOTTERY-123456", @@ -186,7 +187,7 @@ }, "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -210,13 +211,17 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:200" + "6-prize_pool": "biguint:200", + "7-unawarded_amount": "biguint:200" }, "str:burnPercentageForLottery|nested:str:lottery_name": "50", "str:ticketHolder|nested:str:lottery_name|str:.len": "2", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "address:acc1", - "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "address:acc1", - "str:numberOfEntriesForUser|u32:12|str:lottery_name|address:acc1": "2" + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:1": "1", + "str:ticketHolder|nested:str:lottery_name|str:.item|u32:2": "1", + "str:numberOfEntriesForUser|u32:12|str:lottery_name|u64:1": "2", + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1" }, "code": "mxsc:../output/lottery-esdt.mxsc.json" }, @@ -228,7 +233,7 @@ "id": "determine-winner-same-ticket-holder", "tx": { "from": "address:my_address", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "determine_winner", "arguments": [ "str:lottery_name" @@ -237,26 +242,47 @@ "gasPrice": "0" }, "expect": { - "out": [], + "out": [ + "1" + ], "status": "0", "message": "", "gas": "*", "refund": "*" } }, + { + "step": "scCall", + "tx": { + "from": "address:acc1", + "to": "sc:lottery#42", + "function": "claim_rewards", + "arguments": [ + "str:LOTTERY-123456" + ], + "gasLimit": "100,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "0", + "gas": "*", + "refund": "*" + } + }, { "step": "checkState", "comment": "check that 50% was burned, and 50% returned to acc1", "accounts": { "address:acc1": { - "nonce": "2", + "nonce": "3", "balance": "1,000,000", "esdt": { "str:LOTTERY-123456": "100" }, "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "esdt": { @@ -267,6 +293,9 @@ } }, "storage": { + "str:addressToIdMapper|str:lastId": "1", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", "str:lotteryInfo|nested:str:lottery_name": "", "str:burnPercentageForLottery|nested:str:lottery_name": "0" }, diff --git a/contracts/examples/lottery-esdt/scenarios/start-after-announced-winner.scen.json b/contracts/examples/lottery-esdt/scenarios/start-after-announced-winner.scen.json index 1d5e5c5a1f..7e4d6475a8 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-after-announced-winner.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-after-announced-winner.scen.json @@ -11,8 +11,8 @@ "id": "start limited tickets, fixed deadline. Again.", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -37,13 +37,18 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "4", + "nonce": "3", "balance": "1,000,000", "storage": {} }, - "address:acc1": { + "address:other_shard_address#41": { "nonce": "1", "balance": "1,000,000", + "storage": {} + }, + "address:acc1": { + "nonce": "2", + "balance": "1,000,000", "esdt": { "str:LOTTERY-123456": "200" }, @@ -54,10 +59,15 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { + "str:addressToIdMapper|str:lastId": "2", + "str:addressToIdMapper|str:addr|address:acc1": "1", + "str:addressToIdMapper|str:addrId|u64:1": "address:acc1", + "str:addressToIdMapper|str:addr|address:acc2": "2", + "str:addressToIdMapper|str:addrId|u64:2": "address:acc2", "str:lotteryInfo|nested:str:lottery_name": { "0-token_identifier": "nested:str:LOTTERY-123456", "1-ticket_price": "biguint:1000", @@ -65,7 +75,8 @@ "3-deadline": "u64:12345678905", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/start-all-options-bigger-whitelist.scen.json b/contracts/examples/lottery-esdt/scenarios/start-all-options-bigger-whitelist.scen.json index 05fc6dcd4f..347cc3d7a1 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-all-options-bigger-whitelist.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-all-options-bigger-whitelist.scen.json @@ -11,8 +11,8 @@ "id": "start with all options, bigger whitelist", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -58,6 +58,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -83,7 +88,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -94,7 +99,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:2|u8:75|u8:25", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" }, "+": "" }, diff --git a/contracts/examples/lottery-esdt/scenarios/start-alternative-function-name.scen.json b/contracts/examples/lottery-esdt/scenarios/start-alternative-function-name.scen.json index 234780df81..2533120853 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-alternative-function-name.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-alternative-function-name.scen.json @@ -11,7 +11,7 @@ "id": "start with no options altenrative name", "tx": { "from": "address:my_address", - "to": "sc:lottery", + "to": "sc:lottery#42", "function": "createLotteryPool", "arguments": [ "str:lottery_name", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:2592000", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/start-fixed-deadline.scen.json b/contracts/examples/lottery-esdt/scenarios/start-fixed-deadline.scen.json index 8f0506c3be..8a995e16a4 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-fixed-deadline.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-fixed-deadline.scen.json @@ -11,8 +11,8 @@ "id": "start fixed deadline", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-deadline.scen.json b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-deadline.scen.json index 353bb29ad7..4aecdbffa0 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-deadline.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-deadline.scen.json @@ -17,8 +17,8 @@ "id": "start limited tickets, fixed deadline, invalid deadline arg", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -48,6 +48,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -58,7 +63,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": {}, diff --git a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-ticket-price-arg.scen.json b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-ticket-price-arg.scen.json index 284841d451..d65bb92326 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-ticket-price-arg.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline-invalid-ticket-price-arg.scen.json @@ -11,8 +11,8 @@ "id": "start limited tickets, fixed deadline, invalid ticket price arg", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -42,6 +42,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -52,7 +57,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": {}, diff --git a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline.scen.json b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline.scen.json index 583f7db682..0b064d8931 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets-and-fixed-deadline.scen.json @@ -11,8 +11,8 @@ "id": "start limited tickets, fixed deadline", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets.scen.json b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets.scen.json index e1fbfd225e..5ae7d2b1d0 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-limited-tickets.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-limited-tickets.scen.json @@ -11,8 +11,8 @@ "id": "start limited tickets", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:2592000", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/start-second-lottery.scen.json b/contracts/examples/lottery-esdt/scenarios/start-second-lottery.scen.json index ee6aa28aa8..680220a9ff 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-second-lottery.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-second-lottery.scen.json @@ -11,8 +11,8 @@ "id": "start 2nd limited tickets, fixed deadline", "tx": { "from": "address:acc1", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_$$$$", "str:LOTTO-123456", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "1", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" }, "str:lotteryInfo|nested:str:lottery_$$$$": { "0-token_identifier": "nested:str:LOTTO-123456", @@ -71,7 +77,8 @@ "3-deadline": "u64:234,567", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/scenarios/start-with-all-options.scen.json b/contracts/examples/lottery-esdt/scenarios/start-with-all-options.scen.json index 7c2ea2c097..6f80e128af 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-with-all-options.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-with-all-options.scen.json @@ -11,8 +11,8 @@ "id": "start with all options", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:123,456", "4-max_entries_per_user": "u32:1", "5-prize_distribution": "u32:2|u8:75|u8:25", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" }, "+": "" }, diff --git a/contracts/examples/lottery-esdt/scenarios/start-with-no-options.scen.json b/contracts/examples/lottery-esdt/scenarios/start-with-no-options.scen.json index 14c9deae2f..e04359aef4 100644 --- a/contracts/examples/lottery-esdt/scenarios/start-with-no-options.scen.json +++ b/contracts/examples/lottery-esdt/scenarios/start-with-no-options.scen.json @@ -11,8 +11,8 @@ "id": "start with no options", "tx": { "from": "address:my_address", - "to": "sc:lottery", - "function": "start", + "to": "sc:lottery#42", + "function": "createLotteryPool", "arguments": [ "str:lottery_name", "str:LOTTERY-123456", @@ -41,6 +41,11 @@ "balance": "1,000,000", "storage": {} }, + "address:other_shard_address#41": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, "address:acc1": { "nonce": "0", "balance": "1,000,000", @@ -51,7 +56,7 @@ "balance": "1,000,000", "storage": {} }, - "sc:lottery": { + "sc:lottery#42": { "nonce": "0", "balance": "0", "storage": { @@ -62,7 +67,8 @@ "3-deadline": "u64:2592000", "4-max_entries_per_user": "u32:800", "5-prize_distribution": "nested:u8:100", - "6-prize_pool": "biguint:0" + "6-prize_pool": "biguint:0", + "7-unawarded_amount": "biguint:0" } }, "code": "mxsc:../output/lottery-esdt.mxsc.json" diff --git a/contracts/examples/lottery-esdt/src/basics/constants.rs b/contracts/examples/lottery-esdt/src/basics/constants.rs new file mode 100644 index 0000000000..0bd62754d9 --- /dev/null +++ b/contracts/examples/lottery-esdt/src/basics/constants.rs @@ -0,0 +1,4 @@ +pub const PERCENTAGE_TOTAL: u32 = 100; +pub const THIRTY_DAYS_IN_SECONDS: u64 = 60 * 60 * 24 * 30; +pub const MAX_TICKETS: usize = 800; +pub const MAX_OPERATIONS: usize = 50; diff --git a/contracts/examples/lottery-esdt/src/basics/mod.rs b/contracts/examples/lottery-esdt/src/basics/mod.rs new file mode 100644 index 0000000000..9efc59385f --- /dev/null +++ b/contracts/examples/lottery-esdt/src/basics/mod.rs @@ -0,0 +1,4 @@ +pub mod constants; +pub mod storage; +pub mod utils; +pub mod views; diff --git a/contracts/examples/lottery-esdt/src/basics/storage.rs b/contracts/examples/lottery-esdt/src/basics/storage.rs new file mode 100644 index 0000000000..aff1483a2d --- /dev/null +++ b/contracts/examples/lottery-esdt/src/basics/storage.rs @@ -0,0 +1,42 @@ +use multiversx_sc::imports::*; + +#[multiversx_sc::module] +pub trait StorageModule { + #[storage_mapper("ticketHolder")] + fn ticket_holders(&self, lottery_name: &ManagedBuffer) -> VecMapper; + + #[storage_mapper("accumulatedRewards")] + fn accumulated_rewards( + &self, + token_id: &EgldOrEsdtTokenIdentifier, + user_id: &u64, + ) -> SingleValueMapper; + + #[storage_mapper("totalWinning_tickets")] + fn total_winning_tickets(&self, lottery_name: &ManagedBuffer) -> SingleValueMapper; + + #[storage_mapper("indexLastWinner")] + fn index_last_winner(&self, lottery_name: &ManagedBuffer) -> SingleValueMapper; + + #[storage_mapper("accumulatedRewards")] + fn user_accumulated_token_rewards( + &self, + user_id: &u64, + ) -> UnorderedSetMapper; + + #[storage_mapper("numberOfEntriesForUser")] + fn number_of_entries_for_user( + &self, + lottery_name: &ManagedBuffer, + user_id: &u64, + ) -> SingleValueMapper; + + #[storage_mapper("addressToIdMapper")] + fn addres_to_id_mapper(&self) -> AddressToIdMapper; + + #[storage_mapper("burnPercentageForLottery")] + fn burn_percentage_for_lottery( + &self, + lottery_name: &ManagedBuffer, + ) -> SingleValueMapper; +} diff --git a/contracts/examples/lottery-esdt/src/basics/utils.rs b/contracts/examples/lottery-esdt/src/basics/utils.rs new file mode 100644 index 0000000000..d944c56c0d --- /dev/null +++ b/contracts/examples/lottery-esdt/src/basics/utils.rs @@ -0,0 +1,25 @@ +use multiversx_sc::imports::*; + +use crate::constants::PERCENTAGE_TOTAL; + +#[multiversx_sc::module] +pub trait UtilsModule { + fn sum_array(&self, array: &ManagedVec) -> u32 { + let mut sum = 0; + + for item in array { + sum += item as u32; + } + + sum + } + + /// does not check if max - min >= amount, that is the caller's job + fn get_distinct_random(&self, min: usize, max: usize) -> usize { + let mut rand = RandomnessSource::new(); + rand.next_usize_in_range(min, max) + } + fn calculate_percentage_of(&self, value: &BigUint, percentage: &BigUint) -> BigUint { + value * percentage / PERCENTAGE_TOTAL + } +} diff --git a/contracts/examples/lottery-esdt/src/basics/views.rs b/contracts/examples/lottery-esdt/src/basics/views.rs new file mode 100644 index 0000000000..1dbd73cda3 --- /dev/null +++ b/contracts/examples/lottery-esdt/src/basics/views.rs @@ -0,0 +1,31 @@ +use crate::{LotteryInfo, Status}; +use multiversx_sc::imports::*; + +#[multiversx_sc::module] +pub trait ViewsModule { + #[view] + fn status(&self, lottery_name: &ManagedBuffer) -> Status { + if self.lottery_info(lottery_name).is_empty() { + return Status::Inactive; + } + + let info = self.lottery_info(lottery_name).get(); + let current_time = self.blockchain().get_block_timestamp(); + if current_time > info.deadline || info.tickets_left == 0 { + return Status::Ended; + } + + Status::Running + } + + #[view(getLotteryInfo)] + #[storage_mapper("lotteryInfo")] + fn lottery_info( + &self, + lottery_name: &ManagedBuffer, + ) -> SingleValueMapper>; + + #[view(getLotteryWhitelist)] + #[storage_mapper("lotteryWhitelist")] + fn lottery_whitelist(&self, lottery_name: &ManagedBuffer) -> UnorderedSetMapper; +} diff --git a/contracts/examples/lottery-esdt/src/lottery.rs b/contracts/examples/lottery-esdt/src/lottery.rs index 3c5d9040b1..c0542a03e8 100644 --- a/contracts/examples/lottery-esdt/src/lottery.rs +++ b/contracts/examples/lottery-esdt/src/lottery.rs @@ -1,391 +1,26 @@ #![no_std] +use basics::{constants, storage, utils, views}; use multiversx_sc::imports::*; +use specific::{award, awarding_status, buy, claim, lottery_info, setup, status}; -mod lottery_info; -mod status; +mod basics; +mod specific; +use awarding_status::AwardingStatus; use lottery_info::LotteryInfo; use status::Status; -const PERCENTAGE_TOTAL: u32 = 100; -const THIRTY_DAYS_IN_SECONDS: u64 = 60 * 60 * 24 * 30; -const MAX_TICKETS: usize = 800; - #[multiversx_sc::contract] -pub trait Lottery { +pub trait Lottery: + award::AwardingModule + + views::ViewsModule + + storage::StorageModule + + utils::UtilsModule + + claim::ClaimModule + + buy::BuyTicketModule + + setup::SetupModule +{ #[init] fn init(&self) {} - - #[allow_multiple_var_args] - #[endpoint] - fn start( - &self, - lottery_name: ManagedBuffer, - token_identifier: EgldOrEsdtTokenIdentifier, - ticket_price: BigUint, - opt_total_tickets: Option, - opt_deadline: Option, - opt_max_entries_per_user: Option, - opt_prize_distribution: ManagedOption>, - opt_whitelist: ManagedOption>, - opt_burn_percentage: OptionalValue, - ) { - self.start_lottery( - lottery_name, - token_identifier, - ticket_price, - opt_total_tickets, - opt_deadline, - opt_max_entries_per_user, - opt_prize_distribution, - opt_whitelist, - opt_burn_percentage, - ); - } - - #[allow_multiple_var_args] - #[endpoint(createLotteryPool)] - fn create_lottery_pool( - &self, - lottery_name: ManagedBuffer, - token_identifier: EgldOrEsdtTokenIdentifier, - ticket_price: BigUint, - opt_total_tickets: Option, - opt_deadline: Option, - opt_max_entries_per_user: Option, - opt_prize_distribution: ManagedOption>, - opt_whitelist: ManagedOption>, - opt_burn_percentage: OptionalValue, - ) { - self.start_lottery( - lottery_name, - token_identifier, - ticket_price, - opt_total_tickets, - opt_deadline, - opt_max_entries_per_user, - opt_prize_distribution, - opt_whitelist, - opt_burn_percentage, - ); - } - - #[allow_multiple_var_args] - #[allow(clippy::too_many_arguments)] - fn start_lottery( - &self, - lottery_name: ManagedBuffer, - token_identifier: EgldOrEsdtTokenIdentifier, - ticket_price: BigUint, - opt_total_tickets: Option, - opt_deadline: Option, - opt_max_entries_per_user: Option, - opt_prize_distribution: ManagedOption>, - opt_whitelist: ManagedOption>, - opt_burn_percentage: OptionalValue, - ) { - require!(!lottery_name.is_empty(), "Name can't be empty!"); - - let timestamp = self.blockchain().get_block_timestamp(); - let total_tickets = opt_total_tickets.unwrap_or(MAX_TICKETS); - let deadline = opt_deadline.unwrap_or(timestamp + THIRTY_DAYS_IN_SECONDS); - let max_entries_per_user = opt_max_entries_per_user.unwrap_or(MAX_TICKETS); - let prize_distribution = opt_prize_distribution - .unwrap_or_else(|| ManagedVec::from_single_item(PERCENTAGE_TOTAL as u8)); - - require!( - self.status(&lottery_name) == Status::Inactive, - "Lottery is already active!" - ); - require!(!lottery_name.is_empty(), "Can't have empty lottery name!"); - require!(token_identifier.is_valid(), "Invalid token name provided!"); - require!(ticket_price > 0, "Ticket price must be higher than 0!"); - require!( - total_tickets > 0, - "Must have more than 0 tickets available!" - ); - require!( - total_tickets <= MAX_TICKETS, - "Only 800 or less total tickets per lottery are allowed!" - ); - require!(deadline > timestamp, "Deadline can't be in the past!"); - require!( - deadline <= timestamp + THIRTY_DAYS_IN_SECONDS, - "Deadline can't be later than 30 days from now!" - ); - require!( - max_entries_per_user > 0, - "Must have more than 0 max entries per user!" - ); - require!( - self.sum_array(&prize_distribution) == PERCENTAGE_TOTAL, - "Prize distribution must add up to exactly 100(%)!" - ); - - match opt_burn_percentage { - OptionalValue::Some(burn_percentage) => { - require!(!token_identifier.is_egld(), "EGLD can't be burned!"); - - let roles = self - .blockchain() - .get_esdt_local_roles(&token_identifier.clone().unwrap_esdt()); - require!( - roles.has_role(&EsdtLocalRole::Burn), - "The contract can't burn the selected token!" - ); - - require!( - burn_percentage < PERCENTAGE_TOTAL, - "Invalid burn percentage!" - ); - self.burn_percentage_for_lottery(&lottery_name) - .set(burn_percentage); - }, - OptionalValue::None => {}, - } - - if let Some(whitelist) = opt_whitelist.as_option() { - let mut mapper = self.lottery_whitelist(&lottery_name); - for addr in &*whitelist { - mapper.insert(addr); - } - } - - let info = LotteryInfo { - token_identifier, - ticket_price, - tickets_left: total_tickets, - deadline, - max_entries_per_user, - prize_distribution, - prize_pool: BigUint::zero(), - }; - - self.lottery_info(&lottery_name).set(&info); - } - - #[endpoint] - #[payable("*")] - fn buy_ticket(&self, lottery_name: ManagedBuffer) { - let (token_identifier, payment) = self.call_value().egld_or_single_fungible_esdt(); - - match self.status(&lottery_name) { - Status::Inactive => sc_panic!("Lottery is currently inactive."), - Status::Running => { - self.update_after_buy_ticket(&lottery_name, &token_identifier, &payment) - }, - Status::Ended => { - sc_panic!("Lottery entry period has ended! Awaiting winner announcement.") - }, - }; - } - - #[endpoint] - fn determine_winner(&self, lottery_name: ManagedBuffer) { - match self.status(&lottery_name) { - Status::Inactive => sc_panic!("Lottery is inactive!"), - Status::Running => sc_panic!("Lottery is still running!"), - Status::Ended => { - self.distribute_prizes(&lottery_name); - self.clear_storage(&lottery_name); - }, - }; - } - - #[view] - fn status(&self, lottery_name: &ManagedBuffer) -> Status { - if self.lottery_info(lottery_name).is_empty() { - return Status::Inactive; - } - - let info = self.lottery_info(lottery_name).get(); - let current_time = self.blockchain().get_block_timestamp(); - if current_time > info.deadline || info.tickets_left == 0 { - return Status::Ended; - } - - Status::Running - } - - fn update_after_buy_ticket( - &self, - lottery_name: &ManagedBuffer, - token_identifier: &EgldOrEsdtTokenIdentifier, - payment: &BigUint, - ) { - let info_mapper = self.lottery_info(lottery_name); - let mut info = info_mapper.get(); - let caller = self.blockchain().get_caller(); - let whitelist = self.lottery_whitelist(lottery_name); - - require!( - whitelist.is_empty() || whitelist.contains(&caller), - "You are not allowed to participate in this lottery!" - ); - require!( - token_identifier == &info.token_identifier && payment == &info.ticket_price, - "Wrong ticket fee!" - ); - - let entries_mapper = self.number_of_entries_for_user(lottery_name, &caller); - let mut entries = entries_mapper.get(); - require!( - entries < info.max_entries_per_user, - "Ticket limit exceeded for this lottery!" - ); - - self.ticket_holders(lottery_name).push(&caller); - - entries += 1; - info.tickets_left -= 1; - info.prize_pool += &info.ticket_price; - - entries_mapper.set(entries); - info_mapper.set(&info); - } - - fn distribute_prizes(&self, lottery_name: &ManagedBuffer) { - let mut info = self.lottery_info(lottery_name).get(); - let ticket_holders_mapper = self.ticket_holders(lottery_name); - let total_tickets = ticket_holders_mapper.len(); - - if total_tickets == 0 { - return; - } - - let burn_percentage = self.burn_percentage_for_lottery(lottery_name).get(); - if burn_percentage > 0 { - let burn_amount = self.calculate_percentage_of(&info.prize_pool, &burn_percentage); - - // Prevent crashing if the role was unset while the lottery was running - // The tokens will simply remain locked forever - let esdt_token_id = info.token_identifier.clone().unwrap_esdt(); - let roles = self.blockchain().get_esdt_local_roles(&esdt_token_id); - if roles.has_role(&EsdtLocalRole::Burn) { - self.send().esdt_local_burn(&esdt_token_id, 0, &burn_amount); - } - - info.prize_pool -= burn_amount; - } - - // if there are less tickets than the distributed prize pool, - // the 1st place gets the leftover, maybe could split between the remaining - // but this is a rare case anyway and it's not worth the overhead - let total_winning_tickets = if total_tickets < info.prize_distribution.len() { - total_tickets - } else { - info.prize_distribution.len() - }; - let total_prize = info.prize_pool.clone(); - let winning_tickets = self.get_distinct_random(1, total_tickets, total_winning_tickets); - - // distribute to the first place last. Laws of probability say that order doesn't matter. - // this is done to mitigate the effects of BigUint division leading to "spare" prize money being left out at times - // 1st place will get the spare money instead. - for i in (1..total_winning_tickets).rev() { - let winning_ticket_id = winning_tickets[i]; - let winner_address = ticket_holders_mapper.get(winning_ticket_id); - let prize = self.calculate_percentage_of( - &total_prize, - &BigUint::from(info.prize_distribution.get(i)), - ); - - self.tx() - .to(&winner_address) - .egld_or_single_esdt(&info.token_identifier, 0, &prize) - .transfer(); - info.prize_pool -= prize; - } - - // send leftover to first place - let first_place_winner = ticket_holders_mapper.get(winning_tickets[0]); - self.tx() - .to(&first_place_winner) - .egld_or_single_esdt(&info.token_identifier, 0, &info.prize_pool) - .transfer(); - } - - fn clear_storage(&self, lottery_name: &ManagedBuffer) { - let mut ticket_holders_mapper = self.ticket_holders(lottery_name); - let current_ticket_number = ticket_holders_mapper.len(); - - for i in 1..=current_ticket_number { - let addr = ticket_holders_mapper.get(i); - self.number_of_entries_for_user(lottery_name, &addr).clear(); - } - - ticket_holders_mapper.clear(); - self.lottery_info(lottery_name).clear(); - self.lottery_whitelist(lottery_name).clear(); - self.burn_percentage_for_lottery(lottery_name).clear(); - } - - fn sum_array(&self, array: &ManagedVec) -> u32 { - let mut sum = 0; - - for item in array { - sum += item as u32; - } - - sum - } - - /// does not check if max - min >= amount, that is the caller's job - fn get_distinct_random( - &self, - min: usize, - max: usize, - amount: usize, - ) -> ArrayVec { - let mut rand_numbers = ArrayVec::new(); - - for num in min..=max { - rand_numbers.push(num); - } - - let total_numbers = rand_numbers.len(); - let mut rand = RandomnessSource::new(); - - for i in 0..amount { - let rand_index = rand.next_usize_in_range(0, total_numbers); - rand_numbers.swap(i, rand_index); - } - - rand_numbers - } - - fn calculate_percentage_of(&self, value: &BigUint, percentage: &BigUint) -> BigUint { - value * percentage / PERCENTAGE_TOTAL - } - - // storage - - #[view(getLotteryInfo)] - #[storage_mapper("lotteryInfo")] - fn lottery_info( - &self, - lottery_name: &ManagedBuffer, - ) -> SingleValueMapper>; - - #[view(getLotteryWhitelist)] - #[storage_mapper("lotteryWhitelist")] - fn lottery_whitelist(&self, lottery_name: &ManagedBuffer) - -> UnorderedSetMapper; - - #[storage_mapper("ticketHolder")] - fn ticket_holders(&self, lottery_name: &ManagedBuffer) -> VecMapper; - - #[storage_mapper("numberOfEntriesForUser")] - fn number_of_entries_for_user( - &self, - lottery_name: &ManagedBuffer, - user: &ManagedAddress, - ) -> SingleValueMapper; - - #[storage_mapper("burnPercentageForLottery")] - fn burn_percentage_for_lottery( - &self, - lottery_name: &ManagedBuffer, - ) -> SingleValueMapper; } diff --git a/contracts/examples/lottery-esdt/src/specific/award.rs b/contracts/examples/lottery-esdt/src/specific/award.rs new file mode 100644 index 0000000000..061d2e0859 --- /dev/null +++ b/contracts/examples/lottery-esdt/src/specific/award.rs @@ -0,0 +1,196 @@ +use crate::{ + constants::MAX_OPERATIONS, lottery_info::LotteryInfo, storage, utils, views, AwardingStatus, + Status, +}; +use multiversx_sc::imports::*; + +#[multiversx_sc::module] +pub trait AwardingModule: views::ViewsModule + storage::StorageModule + utils::UtilsModule { + #[endpoint] + fn determine_winner(&self, lottery_name: ManagedBuffer) -> AwardingStatus { + let sc_address = self.blockchain().get_sc_address(); + let sc_address_shard = self.blockchain().get_shard_of_address(&sc_address); + let caller = self.blockchain().get_caller(); + let caller_shard = self.blockchain().get_shard_of_address(&caller); + require!( + sc_address_shard != caller_shard, + "Caller needs to be on a remote shard" + ); + + match self.status(&lottery_name) { + Status::Inactive => sc_panic!("Lottery is inactive!"), + Status::Running => sc_panic!("Lottery is still running!"), + Status::Ended => self.handle_awarding(&lottery_name), + } + } + + fn handle_awarding(&self, lottery_name: &ManagedBuffer) -> AwardingStatus { + if self.total_winning_tickets(lottery_name).is_empty() { + self.prepare_awarding(lottery_name); + } + self.distribute_prizes(lottery_name) + } + + fn prepare_awarding(&self, lottery_name: &ManagedBuffer) { + let mut info = self.lottery_info(lottery_name).get(); + let ticket_holders_mapper = self.ticket_holders(lottery_name); + let total_tickets = ticket_holders_mapper.len(); + + if total_tickets == 0 { + return; + } + + self.burn_prize_percentage(lottery_name, &mut info); + + // if there are less tickets than the distributed prize pool, + // the 1st place gets the leftover, maybe could split between the remaining + // but this is a rare case anyway and it's not worth the overhead + let total_winning_tickets = if total_tickets < info.prize_distribution.len() { + total_tickets + } else { + info.prize_distribution.len() + }; + + self.total_winning_tickets(lottery_name) + .set(total_winning_tickets); + self.index_last_winner(lottery_name).set(1); + + self.lottery_info(lottery_name).set(info); + } + + fn burn_prize_percentage( + &self, + lottery_name: &ManagedBuffer, + info: &mut LotteryInfo, + ) { + let burn_percentage = self.burn_percentage_for_lottery(lottery_name).get(); + if burn_percentage == 0 { + return; + } + + let burn_amount = self.calculate_percentage_of(&info.prize_pool, &burn_percentage); + + // Prevent crashing if the role was unset while the lottery was running + // The tokens will simply remain locked forever + let esdt_token_id = info.token_identifier.clone().unwrap_esdt(); + let roles = self.blockchain().get_esdt_local_roles(&esdt_token_id); + if roles.has_role(&EsdtLocalRole::Burn) { + self.send().esdt_local_burn(&esdt_token_id, 0, &burn_amount); + } + + info.prize_pool -= &burn_amount; + info.unawarded_amount -= burn_amount; + } + + fn distribute_prizes(&self, lottery_name: &ManagedBuffer) -> AwardingStatus { + let mut info = self.lottery_info(lottery_name).get(); + let ticket_holders_mapper = self.ticket_holders(lottery_name); + let total_tickets = ticket_holders_mapper.len(); + + let mut index_last_winner = self.index_last_winner(lottery_name).get(); + let total_winning_tickets = self.total_winning_tickets(lottery_name).get(); + require!( + index_last_winner <= total_winning_tickets, + "Awarding has ended" + ); + + let mut iterations = 0; + while index_last_winner <= total_winning_tickets && iterations < MAX_OPERATIONS { + self.award_winner( + lottery_name, + &index_last_winner, + total_tickets, + total_winning_tickets, + &mut info, + ); + index_last_winner += 1; + iterations += 1; + } + self.lottery_info(lottery_name).set(info); + self.index_last_winner(lottery_name).set(index_last_winner); + if index_last_winner > total_winning_tickets { + self.clear_storage(lottery_name); + return AwardingStatus::Finished; + } + AwardingStatus::Ongoing + } + + fn clear_storage(&self, lottery_name: &ManagedBuffer) { + let mut ticket_holders_mapper = self.ticket_holders(lottery_name); + let current_ticket_number = ticket_holders_mapper.len(); + + for i in 1..=current_ticket_number { + let addr = ticket_holders_mapper.get(i); + self.number_of_entries_for_user(lottery_name, &addr).clear(); + } + + ticket_holders_mapper.clear(); + self.lottery_info(lottery_name).clear(); + self.lottery_whitelist(lottery_name).clear(); + self.total_winning_tickets(lottery_name).clear(); + self.index_last_winner(lottery_name).clear(); + self.burn_percentage_for_lottery(lottery_name).clear(); + } + + fn award_winner( + &self, + lottery_name: &ManagedBuffer, + index_last_winner: &usize, + total_tickets: usize, + total_winning_tickets: usize, + info: &mut LotteryInfo, + ) { + let rand_index = self.get_distinct_random(*index_last_winner, total_tickets); + let ticket_holders_mapper = self.ticket_holders(lottery_name); + + // swap indexes of the winner addresses - we are basically bringing the winners in the first indexes of the mapper + let winner_address = self.ticket_holders(lottery_name).get(rand_index); + let last_index_winner_address = self.ticket_holders(lottery_name).get(*index_last_winner); + + self.ticket_holders(lottery_name) + .set(rand_index, &last_index_winner_address); + self.ticket_holders(lottery_name) + .set(*index_last_winner, &winner_address); + + // distribute to the first place last. Laws of probability say that order doesn't matter. + // this is done to mitigate the effects of BigUint division leading to "spare" prize money being left out at times + // 1st place will get the spare money instead. + if *index_last_winner <= total_winning_tickets { + let prize = self.calculate_percentage_of( + &info.prize_pool, + &BigUint::from( + info.prize_distribution + .get(total_winning_tickets - *index_last_winner), + ), + ); + if prize == 0 { + return; + } + + self.assign_prize_to_winner(info.token_identifier.clone(), &prize, &winner_address); + + info.unawarded_amount -= prize; + } else { + // insert token in accumulated rewards first place + let first_place_winner = ticket_holders_mapper.get(*index_last_winner); + + self.assign_prize_to_winner( + info.token_identifier.clone(), + &info.unawarded_amount, + &first_place_winner, + ); + } + } + + fn assign_prize_to_winner( + &self, + token_id: EgldOrEsdtTokenIdentifier, + amount: &BigUint, + winner_id: &u64, + ) { + self.accumulated_rewards(&token_id, winner_id) + .update(|value| *value += amount); + self.user_accumulated_token_rewards(winner_id) + .insert(token_id); + } +} diff --git a/contracts/examples/lottery-esdt/src/specific/awarding_status.rs b/contracts/examples/lottery-esdt/src/specific/awarding_status.rs new file mode 100644 index 0000000000..9d195509d3 --- /dev/null +++ b/contracts/examples/lottery-esdt/src/specific/awarding_status.rs @@ -0,0 +1,8 @@ +use multiversx_sc::derive_imports::*; + +#[type_abi] +#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, PartialEq)] +pub enum AwardingStatus { + Ongoing, + Finished, +} diff --git a/contracts/examples/lottery-esdt/src/specific/buy.rs b/contracts/examples/lottery-esdt/src/specific/buy.rs new file mode 100644 index 0000000000..5321b8e25c --- /dev/null +++ b/contracts/examples/lottery-esdt/src/specific/buy.rs @@ -0,0 +1,63 @@ +use multiversx_sc::imports::*; + +use crate::basics::{storage, views}; + +use super::status::Status; + +#[multiversx_sc::module] +pub trait BuyTicketModule: storage::StorageModule + views::ViewsModule { + #[endpoint] + #[payable("*")] + fn buy_ticket(&self, lottery_name: ManagedBuffer) { + let (token_identifier, payment) = self.call_value().egld_or_single_fungible_esdt(); + + match self.status(&lottery_name) { + Status::Inactive => sc_panic!("Lottery is currently inactive."), + Status::Running => { + self.update_after_buy_ticket(&lottery_name, &token_identifier, &payment) + }, + Status::Ended => { + sc_panic!("Lottery entry period has ended! Awaiting winner announcement.") + }, + }; + } + + fn update_after_buy_ticket( + &self, + lottery_name: &ManagedBuffer, + token_identifier: &EgldOrEsdtTokenIdentifier, + payment: &BigUint, + ) { + let info_mapper = self.lottery_info(lottery_name); + let mut info = info_mapper.get(); + let caller = self.blockchain().get_caller(); + let caller_id = self.addres_to_id_mapper().get_id_or_insert(&caller); + let whitelist = self.lottery_whitelist(lottery_name); + + require!( + whitelist.is_empty() || whitelist.contains(&caller_id), + "You are not allowed to participate in this lottery!" + ); + require!( + token_identifier == &info.token_identifier && payment == &info.ticket_price, + "Wrong ticket fee!" + ); + + let entries_mapper = self.number_of_entries_for_user(lottery_name, &caller_id); + let mut entries = entries_mapper.get(); + require!( + entries < info.max_entries_per_user, + "Ticket limit exceeded for this lottery!" + ); + + self.ticket_holders(lottery_name).push(&caller_id); + + entries += 1; + info.tickets_left -= 1; + info.prize_pool += &info.ticket_price; + info.unawarded_amount += &info.ticket_price; + + entries_mapper.set(entries); + info_mapper.set(&info); + } +} diff --git a/contracts/examples/lottery-esdt/src/specific/claim.rs b/contracts/examples/lottery-esdt/src/specific/claim.rs new file mode 100644 index 0000000000..48804fae20 --- /dev/null +++ b/contracts/examples/lottery-esdt/src/specific/claim.rs @@ -0,0 +1,112 @@ +use multiversx_sc::imports::*; + +use crate::basics::storage; + +#[multiversx_sc::module] +pub trait ClaimModule: storage::StorageModule { + #[endpoint] + fn claim_rewards(&self, tokens: MultiValueEncoded) { + let caller = self.blockchain().get_caller(); + let caller_id = self.addres_to_id_mapper().get_id_or_insert(&caller); + require!( + !self.user_accumulated_token_rewards(&caller_id).is_empty(), + "You have no rewards to claim" + ); + + let mut accumulated_egld_rewards = BigUint::zero(); + let mut accumulated_esdt_rewards = ManagedVec::::new(); + + // to save reviewers time, these 2 iterators have different generics, so it was not possible to make just 1 for loop + + if tokens.is_empty() { + // if wanted tokens were not specified claim all, and clear user_accumulated_token_rewards storage mapper + self.handle_claim_with_unspecified_tokens( + &caller_id, + &mut accumulated_egld_rewards, + &mut accumulated_esdt_rewards, + ); + } else { + // otherwise claim just what was requested and remove those tokens from the user_accumulated_token_rewards storage mapper + + self.claim_rewards_user( + tokens.to_vec(), + &caller_id, + &mut accumulated_egld_rewards, + &mut accumulated_esdt_rewards, + ) + }; + if !accumulated_esdt_rewards.is_empty() { + self.tx() + .to(&caller) + .multi_esdt(accumulated_esdt_rewards) + .transfer(); + } + + if accumulated_egld_rewards > 0u64 { + self.tx() + .to(&caller) + .egld(accumulated_egld_rewards) + .transfer(); + } + } + + fn handle_claim_with_unspecified_tokens( + &self, + caller_id: &u64, + accumulated_egld_rewards: &mut BigUint, + accumulated_esdt_rewards: &mut ManagedVec, + ) { + let mut all_tokens: ManagedVec = ManagedVec::new(); + + for token_id in self.user_accumulated_token_rewards(caller_id).iter() { + require!( + !self.accumulated_rewards(&token_id, caller_id).is_empty(), + "Token requested not available for claim" + ); + all_tokens.push(token_id); + } + + self.claim_rewards_user( + all_tokens, + caller_id, + accumulated_egld_rewards, + accumulated_esdt_rewards, + ) + } + + fn claim_rewards_user( + &self, + tokens: ManagedVec, + caller_id: &u64, + accumulated_egld_rewards: &mut BigUint, + accumulated_esdt_rewards: &mut ManagedVec, + ) { + for token_id in tokens.iter().rev() { + let _ = &self + .user_accumulated_token_rewards(caller_id) + .swap_remove(&token_id); + + self.prepare_token_for_claim( + token_id, + caller_id, + accumulated_egld_rewards, + accumulated_esdt_rewards, + ); + } + } + + fn prepare_token_for_claim( + &self, + token_id: EgldOrEsdtTokenIdentifier, + caller_id: &u64, + accumulated_egld_rewards: &mut BigUint, + accumulated_esdt_rewards: &mut ManagedVec, + ) { + let value = self.accumulated_rewards(&token_id, caller_id).take(); + if token_id.is_egld() { + *accumulated_egld_rewards += value; + } else { + accumulated_esdt_rewards.push(EsdtTokenPayment::new(token_id.unwrap_esdt(), 0, value)); + } + } +} diff --git a/contracts/examples/lottery-esdt/src/lottery_info.rs b/contracts/examples/lottery-esdt/src/specific/lottery_info.rs similarity index 93% rename from contracts/examples/lottery-esdt/src/lottery_info.rs rename to contracts/examples/lottery-esdt/src/specific/lottery_info.rs index 4ec2a6055f..319f2ec5c2 100644 --- a/contracts/examples/lottery-esdt/src/lottery_info.rs +++ b/contracts/examples/lottery-esdt/src/specific/lottery_info.rs @@ -15,4 +15,5 @@ pub struct LotteryInfo { pub max_entries_per_user: usize, pub prize_distribution: ManagedVec, pub prize_pool: BigUint, + pub unawarded_amount: BigUint, } diff --git a/contracts/examples/lottery-esdt/src/specific/mod.rs b/contracts/examples/lottery-esdt/src/specific/mod.rs new file mode 100644 index 0000000000..1405caa87b --- /dev/null +++ b/contracts/examples/lottery-esdt/src/specific/mod.rs @@ -0,0 +1,7 @@ +pub mod award; +pub mod awarding_status; +pub mod buy; +pub mod claim; +pub mod lottery_info; +pub mod setup; +pub mod status; diff --git a/contracts/examples/lottery-esdt/src/specific/setup.rs b/contracts/examples/lottery-esdt/src/specific/setup.rs new file mode 100644 index 0000000000..55b1483c89 --- /dev/null +++ b/contracts/examples/lottery-esdt/src/specific/setup.rs @@ -0,0 +1,138 @@ +use multiversx_sc::imports::*; + +use crate::{ + basics::{ + constants::{MAX_TICKETS, PERCENTAGE_TOTAL, THIRTY_DAYS_IN_SECONDS}, + storage, utils, views, + }, + specific::{lottery_info::LotteryInfo, status::Status}, +}; + +#[multiversx_sc::module] +pub trait SetupModule: storage::StorageModule + views::ViewsModule + utils::UtilsModule { + #[allow_multiple_var_args] + #[endpoint(createLotteryPool)] + fn create_lottery_pool( + &self, + lottery_name: ManagedBuffer, + token_identifier: EgldOrEsdtTokenIdentifier, + ticket_price: BigUint, + opt_total_tickets: Option, + opt_deadline: Option, + opt_max_entries_per_user: Option, + opt_prize_distribution: ManagedOption>, + opt_whitelist: ManagedOption>, + opt_burn_percentage: OptionalValue, + ) { + self.start_lottery( + lottery_name, + token_identifier, + ticket_price, + opt_total_tickets, + opt_deadline, + opt_max_entries_per_user, + opt_prize_distribution, + opt_whitelist, + opt_burn_percentage, + ); + } + + #[allow_multiple_var_args] + #[allow(clippy::too_many_arguments)] + fn start_lottery( + &self, + lottery_name: ManagedBuffer, + token_identifier: EgldOrEsdtTokenIdentifier, + ticket_price: BigUint, + opt_total_tickets: Option, + opt_deadline: Option, + opt_max_entries_per_user: Option, + opt_prize_distribution: ManagedOption>, + opt_whitelist: ManagedOption>, + opt_burn_percentage: OptionalValue, + ) { + require!(!lottery_name.is_empty(), "Name can't be empty!"); + + let timestamp = self.blockchain().get_block_timestamp(); + let total_tickets = opt_total_tickets.unwrap_or(MAX_TICKETS); + let deadline = opt_deadline.unwrap_or(timestamp + THIRTY_DAYS_IN_SECONDS); + let max_entries_per_user = opt_max_entries_per_user.unwrap_or(MAX_TICKETS); + let prize_distribution = opt_prize_distribution + .unwrap_or_else(|| ManagedVec::from_single_item(PERCENTAGE_TOTAL as u8)); + + require!( + total_tickets > prize_distribution.len(), + "Number of winners should be smaller than the number of available tickets" + ); + require!( + self.status(&lottery_name) == Status::Inactive, + "Lottery is already active!" + ); + require!(token_identifier.is_valid(), "Invalid token name provided!"); + require!(ticket_price > 0, "Ticket price must be higher than 0!"); + require!( + total_tickets > 0, + "Must have more than 0 tickets available!" + ); + require!( + total_tickets <= MAX_TICKETS, + "Only 800 or less total tickets per lottery are allowed!" + ); + require!(deadline > timestamp, "Deadline can't be in the past!"); + require!( + deadline <= timestamp + THIRTY_DAYS_IN_SECONDS, + "Deadline can't be later than 30 days from now!" + ); + require!( + max_entries_per_user > 0, + "Must have more than 0 max entries per user!" + ); + require!( + self.sum_array(&prize_distribution) == PERCENTAGE_TOTAL, + "Prize distribution must add up to exactly 100(%)!" + ); + + match opt_burn_percentage { + OptionalValue::Some(burn_percentage) => { + require!(!token_identifier.is_egld(), "EGLD can't be burned!"); + + let roles = self + .blockchain() + .get_esdt_local_roles(&token_identifier.clone().unwrap_esdt()); + require!( + roles.has_role(&EsdtLocalRole::Burn), + "The contract can't burn the selected token!" + ); + + require!( + burn_percentage < PERCENTAGE_TOTAL, + "Invalid burn percentage!" + ); + self.burn_percentage_for_lottery(&lottery_name) + .set(burn_percentage); + }, + OptionalValue::None => {}, + } + + if let Some(whitelist) = opt_whitelist.as_option() { + let mut mapper = self.lottery_whitelist(&lottery_name); + for addr in &*whitelist { + let addr_id = self.addres_to_id_mapper().get_id_or_insert(&addr); + mapper.insert(addr_id); + } + } + + let info = LotteryInfo { + token_identifier, + ticket_price, + tickets_left: total_tickets, + deadline, + max_entries_per_user, + prize_distribution, + prize_pool: BigUint::zero(), + unawarded_amount: BigUint::zero(), + }; + + self.lottery_info(&lottery_name).set(&info); + } +} diff --git a/contracts/examples/lottery-esdt/src/status.rs b/contracts/examples/lottery-esdt/src/specific/status.rs similarity index 100% rename from contracts/examples/lottery-esdt/src/status.rs rename to contracts/examples/lottery-esdt/src/specific/status.rs diff --git a/contracts/examples/lottery-esdt/wasm/src/lib.rs b/contracts/examples/lottery-esdt/wasm/src/lib.rs index a1184cee72..468182c673 100644 --- a/contracts/examples/lottery-esdt/wasm/src/lib.rs +++ b/contracts/examples/lottery-esdt/wasm/src/lib.rs @@ -18,13 +18,13 @@ multiversx_sc_wasm_adapter::endpoints! { lottery_esdt ( init => init - start => start - createLotteryPool => create_lottery_pool - buy_ticket => buy_ticket determine_winner => determine_winner status => status getLotteryInfo => lottery_info getLotteryWhitelist => lottery_whitelist + claim_rewards => claim_rewards + buy_ticket => buy_ticket + createLotteryPool => create_lottery_pool ) } diff --git a/contracts/examples/nft-minter/src/lib.rs b/contracts/examples/nft-minter/src/lib.rs index bf73acee3a..5b3870af5a 100644 --- a/contracts/examples/nft-minter/src/lib.rs +++ b/contracts/examples/nft-minter/src/lib.rs @@ -48,7 +48,7 @@ pub trait NftMinter: nft_module::NftModule { } }; - let attributes = ExampleAttributes { + let attributes: ExampleAttributes = ExampleAttributes { creation_timestamp: self.blockchain().get_block_timestamp(), }; self.create_nft_with_attributes(