From 1c7b37193f73f5b6687652b7f46d491434783ee9 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Sat, 2 Jan 2021 23:05:55 +0300 Subject: [PATCH 01/12] STO Contract moved to Mainnet folder --- Mainnet/STOContract/README.md | 11 + .../STOContract.Tests/InMemoryState.cs | 74 + .../STOContract.Tests.csproj | 19 + .../STOContract.Tests/STOContractTests.cs | 379 +++++ Mainnet/STOContract/STOContract.sln | 31 + .../STOContract/STOContract/STOContract.cs | 1361 +++++++++++++++++ .../STOContract/STOContract.csproj | 17 + 7 files changed, 1892 insertions(+) create mode 100644 Mainnet/STOContract/README.md create mode 100644 Mainnet/STOContract/STOContract.Tests/InMemoryState.cs create mode 100644 Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj create mode 100644 Mainnet/STOContract/STOContract.Tests/STOContractTests.cs create mode 100644 Mainnet/STOContract/STOContract.sln create mode 100644 Mainnet/STOContract/STOContract/STOContract.cs create mode 100644 Mainnet/STOContract/STOContract/STOContract.csproj diff --git a/Mainnet/STOContract/README.md b/Mainnet/STOContract/README.md new file mode 100644 index 00000000..dda80e07 --- /dev/null +++ b/Mainnet/STOContract/README.md @@ -0,0 +1,11 @@ +# STO Smart Contract + +**Contract Hash** +``` +0afb2de46b2c3800e65e367171991671f85ce644efd5fe6e9e3e27a8767e55d6 +``` + +**Contract Byte Code** +``` +4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0102008862EED20000000000000000E00022200B013000004A000000020000000000002E68000000200000008000000000001000200000000200000400000000000000040000000000000000A000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000DC6700004F000000000000000000000000000000000000000000000000000000008000000C000000C06700001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E746578740000003448000000200000004A000000020000000000000000000000000000200000602E72656C6F6300000C0000000080000000020000004C000000000000000000000000000040000042000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010680000000000004800000002000500803A0000402D000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004602280600000A72010000706F0700000A2A4A02280600000A7201000070036F0800000A2A4602280600000A72130000706F0900000A2A4A02280600000A7213000070036F0A00000A2A4602280600000A722D0000706F0900000A2A4A02280600000A722D000070036F0A00000A2A4602280600000A72430000706F0900000A2A4A02280600000A7243000070036F0A00000A2A4602280600000A725F0000706F0700000A2A4A02280600000A725F000070036F0800000A2A4602280600000A72790000706F0B00000A2A4A02280600000A7279000070036F0C00000A2A8202280100000602280D00000A6F0E00000A370B022809000006166AFE032A162A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A72AB0000706F0100002B2A4A02280600000A72AB000070036F1000000A2A00000013300500D3000000010000110203281100000A020519FE0572C30000707215010070281200000A281300000A0202280600000A0E076F1400000A7229010070281300000A02281500000A0E096F0200002B0A0206281C000006050B02070E040E050E0628130000060C02086F1700000A7277010070281300000A021203FE15090000021203086F1800000A7D0800000409280300002B020E072806000006020E08280800000602086F1800000A2804000006020718FE01280C0000060202280B0000062D040E042B02156A280A0000060204280F0000060206281A0000062A00133006006900000000000000032C0603172E252B4602166A198D0B0000012516048C15000001A2251705A225180E04A2166A280400002B2A02166A198D0B0000012516048C15000001A2251705A225180E04A2166A280500002B2A02166A188D0B000001251605A225170E04A2166A280600002B2A22022815000006262A00001330080026010000020000110202280D00000672B7010070281300000A0202281B00000A6F1C00000A166AFE0372E3010070281300000A02281600000602281D0000060A02280B0000062D07722F0200702B0572450200700B02022803000006166A07188D0B000001251602281B00000A6F1D00000A8C07000001A22517067B070000048C15000001A2166A281E00000A0C02086F1F00000A2C0D086F2000000AA5170000012B01167255020070281300000A021203FE150A000002120302281B00000A6F1D00000A7D090000041203067B050000047D0A0000041203067B070000047D0B0000041203067B060000047D0C00000409280700002B02022809000006067B07000004DB280A000006067B06000004166A36180202281B00000A6F1D00000A067B06000004282100000A26172A000013300800C90000000300001102022807000006166A7283020070178D0B000001251602281B00000A6F1D00000A8C07000001A2166A281E00000A0A066F1F00000A3982000000066F2000000A0C08750700000114FE03252D0B1203FE1507000001092B0608A5070000010B2C5B077E2200000A282300000A2C4E02022805000006166A72AB020070188D0B0000012516078C07000001A22517198C18000001A2166A281E00000A0A02066F1F00000A2C0B066F2000000A14FE032B011672BD020070281300000A2A021672F1020070281300000A2A000000133003004D000000000000000202281B00000A6F1D00000A02280E000006282400000A7229030070281300000A0202280D00000616FE017279030070281300000A0202280E00000602282500000A282100000A6F1F00000A2A00000013300800BB000000040000110202280B00000616FE0172A50300707215040070281200000A281300000A0202281B00000A6F1D00000A02280E000006282400000A7233040070281300000A0202280D00000616FE017279030070281300000A02022803000006166A722F020070188D0B000001251602281B00000A6F1D00000A8C07000001A225170228090000068C15000001A2166A281E00000A0A02166A280A00000602066F1F00000A2C0D066F2000000AA5170000012B01167255020070281300000A172A00133002003E000000050000111200FE15070000020228100000060B160C2B230708A3070000020D097B0300000402280D00000A6F0E00000A3704090A2B0A0817580C08078E6932D7062A00001330040026000000060000110203281B0000060A020628110000060206068E6917DA8F070000027B0300000428020000062A0000133004005C00000007000011038E698D070000020A02280D00000A6F0E00000A0B160C2B3B0308A3060000020D07097B01000004D70B06081204FE15070000021204077D030000041204097B020000047D040000041104A4070000020817D60C08038E6932BF062A133003003E0000000800001102038E16FE037285040070281300000A030A160B2B210607A3060000020C02087B01000004166AFE0372D1040070281300000A0717580B07068E6932D92A00001330020086000000090000110228190000060A02281B00000A6F1C00000A067B040000045C0B0228090000060C0708363C08067B04000004D90D02281B00000A6F1C00000A09DB13041205FE15080000021205097D05000004120511047D060000041205087D0700000411052A1205FE1508000002120502281B00000A6F1C00000A7D050000041205077D0700000411052AC20203281100000A0204282400000602052822000006020E0428200000060202281B00000A6F1D00000A0428260000062A4602280600000A721F0500706F2600000A2A4A02280600000A721F050070036F2700000A2A4602280600000A722D0500706F2600000A2A4A02280600000A722D050070036F2700000A2A4602280600000A72370500706F0700000A2A4A02280600000A7237050070036F0800000A2A7202280600000A724F050070038C07000001281200000A6F0700000A2A7602280600000A724F050070038C07000001281200000A046F0800000A2A00000013300400A60000000A000011042D34021201FE150D000002120102281B00000A6F1D00000A7D140000041201037D150000041201166A7D1600000407280800002B172A0202281B00000A6F1D00000A28250000060A06043402162A0202281B00000A6F1D00000A0604DB282600000602030203282500000604D72826000006021201FE150D000002120102281B00000A6F1D00000A7D140000041201037D150000041201047D1600000407280800002B172A000013300500AA0000000B000011052D2A021202FE150D0000021202037D140000041202047D150000041202166A7D1600000408280800002B172A020302281B00000A6F1D00000A282B0000060A020328250000060B0605370407053402162A020302281B00000A6F1D00000A0605DB282A00000602030705DB282600000602040204282500000605D72826000006021202FE150D0000021202037D140000041202047D150000041202057D1600000408280800002B172A000013300400650000000C0000110202281B00000A6F1D00000A03282B000006042E02162A0202281B00000A6F1D00000A0305282A000006021200FE150E000002120002281B00000A6F1D00000A7D170000041200037D180000041200057D1A0000041200047D1900000406280900002B172A8E02280600000A7267050070038C07000001048C07000001282800000A056F0800000A2A8A02280600000A7267050070038C07000001048C07000001282800000A6F0700000A2A4602280600000A728B0500706F0700000A2A4A02280600000A728B050070036F0800000A2A7202280600000A729F050070038C07000001281200000A6F0A00002B2A7602280600000A729F050070038C07000001281200000A046F0B00002B2AC20203281100000A020428400000060205283E000006020E04283C0000060202281B00000A6F1D00000A0428420000062A660202282C00000602281B00000A6F1C00000AD7282D0000062A8E0202281B00000A6F1D00000A283400000626020328340000062602030428430000062A6A020328340000062602042834000006260203040528440000062A0000133003003C0000000D0000110203282E0000060A02030628350000060B07166A36241200067B1B00000407D77D1B000004120002282C0000067D1D000004020306282F000006062A13300200210000000E00001102282C000006047B1D000004DB0A0203284100000606D90B047B1B00000407D72A4A0202281B00000A6F1D00000A28370000062A13300300180000000F0000110203282E0000060A020306283500000602283F0000065C2A4A0202281B00000A6F1D00000A28390000062A00133003001F0000000F0000110203282E0000060A0203062835000006067B1C000004D702283F0000065C2A001330030093000000100000110202281B00000A6F1D00000A28340000060A067B1B00000402283F0000065C0B067B1B00000402283F0000065E0C0207166AFE0372B7050070281300000A1200067B1C000004067B1B000004D708DB7D1C0000041200087D1B0000040202281B00000A6F1D00000A06282F0000060202281B00000A6F1D00000A07282100000A0D02096F1F00000A72F3050070281300000A2A4602280600000A721F0500706F2600000A2A4A02280600000A721F050070036F2700000A2A4602280600000A722D0500706F2600000A2A4A02280600000A722D050070036F2700000A2A4602280600000A72370500706F0700000A2A4A02280600000A7237050070036F0800000A2A7202280600000A724F050070038C07000001281200000A6F0700000A2A7602280600000A724F050070038C07000001281200000A046F0800000A2A00000013300400A600000011000011042D34021201FE1510000002120102281B00000A6F1D00000A7D1E0000041201037D1F0000041201166A7D2000000407280C00002B172A0202281B00000A6F1D00000A28410000060A06043402162A0202281B00000A6F1D00000A0604DB284200000602030203284100000604D72842000006021201FE1510000002120102281B00000A6F1D00000A7D1E0000041201037D1F0000041201047D2000000407280C00002B172A000013300500AA00000012000011052D2A021202FE15100000021202037D1E0000041202047D1F0000041202166A7D2000000408280C00002B172A020302281B00000A6F1D00000A28470000060A020328410000060B0605370407053402162A020302281B00000A6F1D00000A0605DB284600000602030705DB284200000602040204284100000605D72842000006021202FE15100000021202037D1E0000041202047D1F0000041202057D2000000408280C00002B172A00001330040065000000130000110202281B00000A6F1D00000A032847000006042E02162A0202281B00000A6F1D00000A03052846000006021200FE1511000002120002281B00000A6F1D00000A7D210000041200037D220000041200057D240000041200047D2300000406280D00002B172A8E02280600000A7267050070038C07000001048C07000001282800000A056F0800000A2A8A02280600000A7267050070038C07000001048C07000001282800000A6F0700000A2A7202280600000A7215060070038C18000001281200000A6F0B00000A2A7602280600000A7215060070038C18000001281200000A046F0C00000A2A467243060070038C15000001281200000A2A4E02280600000A0203284A0000066F0900000A2A5202280600000A0203284A000006046F0A00000A2A46725F060070038C15000001281200000A2A4E02280600000A0203284D0000066F0900000A2A5202280600000A0203284D000006046F0A00000A2A7202280600000A7281060070038C07000001281200000A6F0700000A2A7602280600000A7281060070038C07000001281200000A046F0800000A2A8A02280600000A72B1060070038C07000001048C07000001282800000A6F0B00000A2A8E02280600000A72B1060070038C07000001048C07000001282800000A056F0C00000A2A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A722D0500706F2600000A2A4A02280600000A722D050070036F2700000A2A4602280600000A721F0500706F2600000A2A4A02280600000A721F050070036F2700000A2A4602280600000A72E10600706F0700000A2A4A02280600000A72E1060070036F0800000A2A4672F9060070038C15000001281200000A2A4E02280600000A0203285C0000066F0700000A2A5202280600000A0203285C000006046F0800000A2A4E02280600000A0203285C0000066F2B00000A2A46721B070070038C15000001281200000A2A4E02280600000A020328600000066F0700000A2A5202280600000A02032860000006046F0800000A2A4E02280600000A020328600000066F2B00000A2A5E723D070070038C07000001048C15000001282800000A2A5202280600000A02030428640000066F0700000A2A5602280600000A0203042864000006056F0800000A2A5202280600000A02030428640000066F2B00000A2A5E7275070070038C07000001048C15000001282800000A2A5202280600000A02030428680000066F0700000A2A5602280600000A0203042868000006056F0800000A2A5202280600000A02030428680000066F2B00000A2A4602280600000A72370500706F0700000A2A4A02280600000A7237050070036F0800000A2A00001330030057000000000000000203281100000A021717284900000602181728490000060219162849000006021A172849000006021B17284900000602042857000006020528590000060202281B00000A6F1D00000A285500000602176A285B0000062A72020302286C000006FE0572AD070070281300000A0203285D0000062A7A020402032850000006FE0572AD070070281300000A02030428650000062A22020328480000062A32020304050E04287E0000062A4202030405168D19000001287E0000062A0000133003003800000014000011020528840000060205284B0000060A0206288A0000060204288A000006020603282400000A72D9070070281300000A020405287B0000062A133004003A0000001400001102042883000006020428850000060204284B0000060A020306282300000A72D9070070281300000A020403284F0000060206030428810000062A9E0202281B00000A6F1D00000A030428530000060202281B00000A6F1D00000A030428820000062A3E0203288A000006020328500000062A00001330020011000000140000110203284B0000060A0206288A000006062A3E020328850000060203284E0000062A2602030428520000062A001330040029000000140000110204284B0000060A0204287F000006020604287C000006020304287D0000060206030428800000062A000000133004007B00000015000011020204284B00000603282400000A72D9070070281300000A020328500000060A020306176ADB285100000602280600000A0204284A0000066F2B00000A02030428690000060B06176ADB0C07082E1B02030828650000060D02030907286A0000060203070928660000060203082867000006020304286B0000062A00133004004C0000000E000011020204284B0000067E2200000A282400000A72D9070070281300000A020403284C000006020328500000060A020306176AD72851000006060B02030407286A0000060203070428660000062A133008009D0000001600001102052884000006020528850000060205284B0000060A020603282400000A72D9070070281300000A0204288A000006020405287B00000602280600000A046F1400000A2C570204166A72F70700701A8D0B000001251602281B00000A6F1D00000A8C07000001A22517038C07000001A22518058C15000001A225190E04A2166A281E00000A0B02076F2000000AA51700000172D9070070281300000A2A9A0203284E0000067E2200000A282300000A2C1202280600000A0203284D0000066F2B00000A2A133003002800000017000011021200FE15120000021200037D250000041200047D260000041200057D2700000406280E00002B2A133003002800000018000011021200FE15130000021200037D280000041200047D290000041200057D2A00000406280F00002B2A133003002800000019000011021200FE15140000021200037D2B0000041200047D2C0000041200057D2D00000406281000002B2A133004003C000000140000110203284B0000060A020602281B00000A6F1D00000A282400000A2D14020602281B00000A6F1D00000A28520000062B011772D9070070281300000A2A1330040055000000140000110203284B0000060A020602281B00000A6F1D00000A282400000A2D2D0203284E00000602281B00000A6F1D00000A282400000A2D14020602281B00000A6F1D00000A28520000062B011772D9070070281300000A2A0000001330020010000000140000110203284B0000060A0206288A0000062A13300400520000001A00001102288700000602037E2200000A282300000A722D0800707295080070281200000A281300000A021200FE151500000212000228540000067D2E0000041200037D2F00000406281100002B020328550000062A8A0202281B00000A6F1D00000A022854000006282400000A72A1080070281300000A2A00000013300300980000001B0000110228870000060203288A0000060204166AFE0372FD080070281300000A02286C0000060A0604D70B02285A0000060C2B4D020308287D000006020608285E0000060208062862000006021203FE151200000212037E2200000A7D250000041203037D260000041203087D2700000409280E00002B06176AD70A08176AD70C060737AF0202286C00000604D7286D0000060208285B0000062A13300300AB0000001C0000110203284B0000060A0206288A000006020602281B00000A6F1D00000A282400000A7249090070281300000A0203287F000006020603287C000006020328610000060B02286C000006176ADB1304021104286D00000611040C0208285D0000060D020709285E00000602090728620000060208285F00000602032863000006021205FE15120000021205067D2500000412057E2200000A7D260000041205037D270000041105280E00002B2A5E02037E2200000A282300000A7293090070281300000A2A0042534A4201000100000000000C00000076342E302E33303331390000000005006C0000007C130000237E0000E8130000F40B000023537472696E677300000000DC1F0000D009000023555300AC290000100000002347554944000000BC2900008403000023426C6F620000000000000002000001571FA201090A000000FA0133001600000100000019000000150000002F0000008A000000BE000000020000002B00000003000000170000001C000000040000001500000029000000010000000300000010000000110000000000090401000000000006001D02F70706005C02F70706000902BF060F00170800000A004C0212090A00730912090A00020912090A00B70112090A009C0912090A00AA0912090600810948040600870148040A003D02120906007E0448040E00BF04A8070A00DE0112090A002C0312090600060B48040600CE0248040A00780612090600150048040A005B01120906008304480406000100480406007A024804000000001C000000000001000100010010004809000019000100010001001000C0040000190001001E0001001000B1040000190001002C0001001000EB0400001900010048000A0110005A0A0000310001008B000A011000CF000000310003008B000A011000C6050000310005008B000A0110000A030000310008008B000A01100022030000310009008B000A0110004F04000031000D008B000201000091010000390010008B000A01100016030000310014008B000A011000EC020000310017008B000A011000040A000031001B008B000A0110001603000031001E008B000A011000EC020000310021008B000A01100016030000310025008B000A011000EC020000310028008B000A011000F802000031002B008B000A011000D502000031002E008B000600260814020600FC04140206004E0314020600FC0414020600AE00140206001E0A140206002B0A140206008E08F0000600FE05F0000600AE00140206002B0A14020600890014020600820B17020600960517020600A4001A02060631001D025680C00420025680B10420025680EB04200206007404F0000600C005F00006003E0A140206006C06F00006000506F0000600140A140206003E0A140206000901140206002B0714020600EE06140206007404F0000600C005F00006003E0A140206006C06F00006000506F0000600140A140206003E0A140206007404F0000600C005F00006007900140206006C06F0000600BA00F00006007900140206006C06F0000600B006F0000600BA001A0206005B06F00006006906F00050200000000086083D033D00010062200000000081084A0324020100752000000000860879089000020087200000000081088A08290202009A20000000008608500890000300AC200000000081085F0829020300BF200000000086089B0890000400D120000000008108AD0829020400E42000000000860819013D000500F6200000000081082A01240205000921000000008608CE048C0006001B21000000008108E5042F0206002E2100000000860889058C0007004F210000000086082D069000070061210000000081083706290207007421000000008608590734020800862100000000810869073A0208009C21000000008618B906410209007C22000000008100540953021200F12200000000C6009F0206001600FC22000000008600530A8C001600302400000000810092000600160008250000000086004B078C001600642500000000860035088C0016002C26000000008100DA005D0216007826000000008100950762021600AC2600000000810029096902170014270000000081008507620218006027000000008100C30572021900F227000000008618B9067702190023280000000086081B0480021D0035280000000081082604A9011D004828000000008608700180021E005A280000000081087901A9011E006D2800000000E609960B3D001F007F28000000008108A60B24021F00922800000000E6013B0184022000AF2800000000810046018A022100D02800000000E601A70591022300842900000000E6015904980225003C2A00000000E601A702A1022800AD2A000000008100BB03A9022B00D12A00000000E6015101B2022E00F42A000000008608D2063D003000062B000000008108E00624023000192B000000008100F609BA023100362B000000008100010AC1023200542B000000008618B90677023400852B00000000C6009F02060038009F2B00000000E601A70591023800C32B00000000E601590498023A00E02B000000008100E809BA023D00282C0000000081000007C9023E00552C0000000086003E073D004000682C0000000086003E07840240008C2C00000000860019073D004100A02C000000008600190784024100CC2C0000000086006A0A060042006B2D0000000086081B04800242007D2D0000000081082604A9014200902D000000008608700180024300A22D0000000081087901A9014300B52D00000000E609960B3D004400C72D000000008108A60B24024400DA2D00000000E6013B0184024500F72D00000000810046018A024600182E000000008100B20591024800CC2E000000008100660498024A00842F00000000E601A702A1024D00F52F000000008100BB03A9025000193000000000E6015101B20253003C30000000008100C907D10255005930000000008100E007D60256007730000000008100490BDC02580089300000000081004106E10259009D300000000081004E06E7025A00B2300000000081000C0BDC025C00C4300000000081008D03E1025D00D8300000000081009D03E7025E00ED30000000008100BA09840260000A31000000008100D1098A02610028310000000081009306EE0263004B31000000008100A606F60265006F310000000086082D069000680081310000000081083706290268009431000000008608700180026900A6310000000081087901A9016900B9310000000086081B0480026A00CB310000000081082604A9016A00DE3100000000810861003D006B00F031000000008108710024026B000332000000008100590BDC026C001532000000008100850AFF026D002932000000008100950A04036E003E32000000008100730A240270005232000000008100360BDC02710064320000000081006305FF02720078320000000081007305040373008D32000000008100510524027500A1320000000081006C0B0A037600B932000000008100BE0A11037800CE32000000008100D50A18037A00E432000000008100A50A8A027D00F9320000000081001F0B0A037F00113300000000810023051103810026330000000081003A05180383003C330000000081000A058A0286005133000000008608960B3D0088006333000000008108A60B240288007833000000008618B90620038900DB33000000008600980AFF028C00F833000000008600D80A11038D001734000000008600EB00D1028F0020340000000086005504280390002D340000000086005504A902940040340000000086005904A90297008434000000008600A7028A029A00CA34000000008600EA0333039C00F234000000008600AF0284029E000435000000008600B902E1029F002135000000008600B700E102A0003135000000008600C703EE02A1003C3500000000810070038A02A3007435000000008100A3048A02A500FC350000000081008B048A02A700543600000000810057032803A900FD36000000008100AD032402AD0024370000000081001506A902AE0058370000000081008103A902B1008C37000000008100D803F602B400C037000000008100AC012402B700083800000000810021062402B8006C3800000000810096042402B9008838000000008600D7052902BA00E638000000008100860B0600BB000C39000000008600FC038A02BB00B039000000008600A2052402BD00673A000000008600DC0B2902BE0000000100990200000100990200000100990200000100990200000100990200000100990200000100990200000100990200000100CB01000002007206000003009B0100000400B60B000005008201000006003104000007006E0800000800BF08000009007907000001009B0100000200B60B00000300820100000400310400000100A00700000100A00700000100A00700000100CB0100000200B60B000003008201000004003104000001009902000001009902000001009902000001000A09000001000A0900000200990200000100D40500000200450A00000100790400000200D40500000300450A000001000D0600000200370A00000300450A000001007206000002000D06000003009902000001007206000002000D06000001009902000001000A09000001000A09000002000C0A00000100030200000200B60B00000300820100000400310400000100D40500000200450A00000100790400000200D40500000300450A000001000A09000001000A09000002000C0A000001000A09000001000A09000001009902000001009902000001009902000001000A09000001000A0900000200990200000100D40500000200450A00000100790400000200D40500000300450A000001000D0600000200370A00000300450A000001007206000002000D06000003009902000001007206000002000D0600000100550000000100550000000200990200000100CC0000000100CC0000000100CC0000000200990200000100CC0000000100CC0000000100CC00000002009902000001000A09000001000A0900000200990200000100720600000200CD0800000100720600000200CD0800000300990200000100990200000100990200000100990200000100990200000100EC0A00000100EC0A00000100EC0A00000200830500000100EC0A00000100830500000100830500000100830500000200EC0A000001008305000001000A0900000200EC0A000001000A0900000200EC0A00000100720600000200EC0A00000300810000000100720600000200EC0A00000100720600000200810000000100720600000200810000000100720600000200810000000300EC0A00000100720600000200810000000100990200000100030200000200820100000300310400000100EC0A00000100720600000200EC0A00000100250000000100790400000200D40500000300810000000400390000000100790400000200D40500000300810000000100790400000200D40500000300810000000100C30000000200810000000100CD0800000200C30000000100720600000100810000000100810000000100720600000200CD0800000100D40500000200810000000100790400000200810000000100D40500000200810000000100790400000200D40500000300810000000400390000000100810000000100790400000200D40500000300810000000100720600000200C30000000300810000000100720600000200CD0800000300C300000001008100000001008100000001008100000001007206000001000A0900000200450A000001008100000001000A0903003D0004003D000900B90601001100B90606001900B9060A002900B90606006900B90606003100EF011000810008001500810012001A008100F40820008100FF082600810038042D008100400432003100330338008900EF053D008100FA0A41008100030B4E003100B906610099004109670031004C0A6D00810068097300310084067900A100F20A7E00490044088C004900DD0890003100280395003100A501A10031006401C400B1007F023D00B100FA05900031000404C900510044088C0051008902D50031002406DE003900CF05F0003900CE0BF4003900C20BF4003100FD003D008100C10232018100CB023701990041095901810088096001810092096C018100E905A901090044000502090048000A0209004C000F022E000B0051032E0013005A032E001B007903430023000A0221012B000A0281022B000A02A1022B000A02E1022B000A0201032B000A02C1032B000A02E1032B000A0221042B000A0241042B000A02A1042B000A02C1042B000A02E1042B000A0201052B000A0221052B000A0241052B000A0261052B000A0281052B000A02C1052B000A02E1052B000A025500BA00E600FC0001010C0112011E0127013D0148014F0174017A017F0184018D0198019F01AE01B301BA01C101CB01D501DF01E901F1010200010003000A0004000D000500110000004E033A0300008E083E03000063083E030000B1083E0300002E013A030000E904430300008D05430300006C063E0300006D07470300002A044D0300007D014D030000AA0B3A03000041073A0300002A044D0300007D014D030000AA0B3A0300006C063E0300007D014D0300002A044D03000075003A030000AA0B3A03020001000300010002000300020003000500010004000500020005000700010006000700020007000900010008000900020009000B0001000A000B0002000B000D0001000C000D0002000D000F0002000E00110001000F00110002001000130001001100130002001F00150001002000150002002100170001002200170002002300190001002400190002002C001B0001002D001B0002003B001D0001003C001D0002003D001F0001003E001F0002003F00210001004000210002005400230001005500230002005600250001005700250002005800270001005900270002005A00290001005B00290002006C002B0001006D002B0004800000000000000000000000000000000073090000040000000000000000000000FC013E000000000001000200010000000000000000001209000000000100000000000000000000000000A80700000000060002000700020008000200090002000A0002000B0002000C0002000D0003000E0003000F0004001000040011000400120005001300050014000500150005001F0049002D00870033009C003500AB003500B0003500B5003300D90033004301330054015300670155006701330093013300A4013300C6013300D0013300DA013300E401000000000055496E7433320047657455496E7436340053657455496E743634003C4D6F64756C653E00696E7465726661636549440076616C75655F5F00646174610053797374656D2E507269766174652E436F72654C696200696E746572666163654964006765745F4E657874546F6B656E4964007365745F4E657874546F6B656E496400746F6B656E496400526566756E64656400456E737572654B796356657269666965640049735265766F6B656400496E76657374656400476574417070726F76656400617070726F7665640069640053616C65506572696F640047657443757272656E74506572696F6400537570706F727473496E74657266616365006765745F42616C616E6365004469766964656E6442616C616E6365006765745F546F6B656E42616C616E6365007365745F546F6B656E42616C616E63650047657442616C616E63650053657442616C616E636500416C6C6F77616E636500494D657373616765006765745F4D657373616765006765745F4E616D65007365745F4E616D65006E616D650056616C75655479706500546F6B656E5479706500746F6B656E54797065004372656174650043616E4F7065726174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E745374617465006765745F50657273697374656E7453746174650073746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E646578417474726962757465004465706C6F794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650042797465006765745F56616C7565006765745F52657475726E56616C75650076616C7565005265636569766500417070726F76650042616C616E63654F66004F776E65724F6600476574537472696E6700536574537472696E67004F776E6572736869705472616E7366657265644C6F6700417070726F76616C4C6F6700417070726F76616C466F72416C6C4C6F670053544F53657475704C6F67005472616E736665724C6F6700496E766573744C6F670049426C6F636B006765745F426C6F636B006765745F456E64426C6F636B007365745F456E64426C6F636B00536166655472616E7366657246726F6D496E7465726E616C005472616E73666572496E7465726E616C004C6F67417070726F76616C004765744964546F417070726F76616C005365744964546F417070726F76616C00436C656172417070726F76616C00536574417070726F76616C004973417070726F766564466F72416C6C004C6F67417070726F76616C466F72416C6C00536574417070726F76616C466F72416C6C004D696E74416C6C0043616C6C00536D617274436F6E74726163742E646C6C006765745F53796D626F6C007365745F53796D626F6C0073796D626F6C00476574426F6F6C00536574426F6F6C0053797374656D00436C61696D00536166655472616E7366657246726F6D005472616E73666572546F6B656E7346726F6D0066726F6D00456E756D00426F6F6C65616E004164644E46546F6B656E0056616C69644E46546F6B656E0052656D6F76654E46546F6B656E004469766964656E64546F6B656E00495374616E64617264546F6B656E006765745F49734E6F6E46756E6769626C65546F6B656E007365745F49734E6F6E46756E6769626C65546F6B656E005072696365506572546F6B656E00436C656172496E6465784F664F776E65724279546F6B656E00476574496E6465784F664F776E65724279546F6B656E00536574496E6465784F664F776E65724279546F6B656E00436C656172496E6465784279546F6B656E00476574496E6465784279546F6B656E00536574496E6465784279546F6B656E00746F6B656E006765745F53616C654F70656E004465736372697074696F6E004275726E005472616E73666572546F005472616E73666572546F6B656E73546F0047657453616C65496E666F005A65726F00746F005472616E736665724F776E65727368697000436C656172006765745F4E756D626572006765745F53656E646572005370656E646572007370656E646572004C6F675472616E736665720043616E5472616E73666572006765745F4F776E6572007365745F4F776E6572004765744964546F4F776E6572005365744964546F4F776E65720050726576696F75734F776E6572004E65774F776E6572006F776E6572004953657269616C697A6572006765745F53657269616C697A6572004765744F776E6572546F4F70657261746F72005365744F776E6572546F4F70657261746F72002E63746F720053797374656D2E446961676E6F7374696373006765745F4469766964656E6473007365745F4469766964656E64730043726564697465644469766964656E647300476574576974686472617761626C654469766964656E647300476574546F74616C4469766964656E64730057697468647261776E4469766964656E6473004765744469766964656E647300576974686472617746756E6473006765745F53616C65506572696F6473007365745F53616C65506572696F64730073616C65506572696F64730056616C6964617465506572696F647300536574506572696F647300706572696F647300537472617469732E536D617274436F6E7472616374732E5374616E646172647300476574537570706F72746564496E746572666163657300536574537570706F72746564496E74657266616365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004475726174696F6E426C6F636B73005769746864726177546F6B656E73006765745F53756363657373006765745F4B594341646472657373007365745F4B594341646472657373006B796341646472657373006765745F546F6B656E41646472657373007365745F546F6B656E41646472657373006765745F4D617070657241646472657373007365745F4D617070657241646472657373006D617070657241646472657373006F70657261746F7241646472657373006765745F4E6577436F6E74726163744164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300436F6E7665727453616C65506572696F64496E7075747300466F726D61740053544F436F6E747261637400437265617465546F6B656E436F6E7472616374004973436F6E747261637400536D617274436F6E7472616374004F626A65637400476574537472756374005365745374727563740049437265617465526573756C7400495472616E73666572526573756C74004765744F776E6572546F4E46546F6B656E436F756E74005365744F776E6572546F4E46546F6B656E436F756E74005570646174654163636F756E74004765744163636F756E74005365744163636F756E74006163636F756E74004F6C64416D6F756E7400526566756E64416D6F756E7400546F6B656E416D6F756E740063757272656E74416D6F756E7400616D6F756E740041737365727400496E766573740053616C65506572696F64496E70757400576974686472617700436C656172546F6B656E4279496E64657800476574546F6B656E4279496E64657800536574546F6B656E4279496E64657800436C656172546F6B656E4F664F776E65724279496E64657800476574546F6B656E4F664F776E65724279496E64657800536574546F6B656E4F664F776E65724279496E64657800696E64657800546F4172726179004765744172726179005365744172726179004765744964546F417070726F76616C4B657900496E6465784F664F776E65724279546F6B656E4B657900476574496E6465784279546F6B656E4B6579004765744964546F4F776E65724B657900476574546F6B656E4279496E6465784B657900476574546F6B656E4F664F776E65724279496E6465784B657900456E737572654F776E65724F6E6C79006765745F546F74616C537570706C79007365745F546F74616C537570706C7900746F74616C537570706C79006F705F457175616C697479006F705F496E657175616C69747900456E737572654164647265737349734E6F74456D70747900001145006E00640042006C006F0063006B00001954006F006B0065006E00410064006400720065007300730000154B00590043004100640064007200650073007300001B4D00610070007000650072004100640064007200650073007300001954006F006B0065006E00420061006C0061006E00630065000025490073004E006F006E00460075006E006700690062006C00650054006F006B0065006E00000B4F0077006E00650072000017530061006C00650050006500720069006F0064007300005154006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006200650020006200650074007700650065006E0020003000200061006E00640020003200001374006F006B0065006E005400790070006500004D54006800650020006B007900630041006400720065007300730020006900730020006E006F00740020006100200063006F006E00740072006100630074002000610064007200650073007300003F4300720065006100740069006E006700200074006F006B0065006E00200063006F006E007400720061006300740020006600610069006C00650064002E00002B5400680065002000530054004F00200069007300200063006F006D0070006C0065007400650064002E00004B540068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000155400720061006E00730066006500720054006F00000F4D0069006E00740041006C006C00002D54006F006B0065006E0020007400720061006E00730066006500720020006600610069006C00650064002E0000274700650074005300650063006F006E0064006100720079004100640064007200650073007300001147006500740043006C00610069006D00003359006F007500720020004B005900430020006900730020006E006F0074002000760065007200690066006900650064002E00003754006800650020006100640064007200650073007300200068006100730020006E006F0020006D0061007000700069006E0067002E00004F4F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E0073006600650072002000660075006E00640073002E00002B530054004F0020006900730020006E006F007400200065006E0064006500640020007900650074002E00006F54006800650020007B0030007D0020006D006500740068006F00640020006900730020006E006F007400200073007500700070006F007200740065006400200066006F00720020004E006F006E002D00460075006E006700690062006C006500200054006F006B0065006E002E00011D5700690074006800640072006100770054006F006B0065006E00730000514F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E007300660065007200200074006F006B0065006E0073002E00004B50006C0065006100730065002000700072006F00760069006400650020006100740020006C006500610073007400200031002000730061006C006500200070006500720069006F006400004D4400750072006100740069006F006E0042006C006F0063006B0073002000730068006F0075006C006400200068006900670068006500720020007400680061006E0020007A00650072006F00000D530079006D0062006F006C0000094E0061006D006500001754006F00740061006C0053007500700070006C0079000017420061006C0061006E00630065003A007B0030007D00002341006C006C006F00770061006E00630065003A007B0030007D003A007B0031007D0000134400690076006900640065006E006400730000174100630063006F0075006E0074003A007B0030007D00003B54006800650020006100630063006F0075006E007400200068006100730020006E006F0020006400690076006900640065006E00640073002E0000215400720061006E00730066006500720020006600610069006C00650064002E00002D53007500700070006F00720074006500640049006E0074006500720066006100630065003A007B0030007D00001B4900640054006F004F0077006E00650072003A007B0030007D0000214900640054006F0041007000700072006F00760061006C003A007B0030007D00002F4F0077006E006500720054006F004E00460054006F006B0065006E0043006F0075006E0074003A007B0030007D00002F4F0077006E006500720054006F004F00700065007200610074006F0072003A007B0030007D003A007B0031007D0000174E0065007800740054006F006B0065006E0049006400002154006F006B0065006E004200790049006E006400650078003A007B0030007D00002149006E006400650078004200790054006F006B0065006E003A007B0030007D00003754006F006B0065006E004F0066004F0077006E00650072004200790049006E006400650078003A007B0030007D003A007B0031007D00003749006E006400650078004F0066004F0077006E00650072004200790054006F006B0065006E003A007B0030007D003A007B0031007D00002B540068006500200069006E00640065007800200069007300200069006E00760061006C00690064002E00001D41007300730065007200740020006600610069006C00650064002E0000354F006E004E006F006E00460075006E006700690062006C00650054006F006B0065006E0052006500630065006900760065006400006754006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006E006F0074002000620065002000640065006600610075006C00740028007A00650072006F002900200061006400640072006500730073002E00000B6F0077006E0065007200005B4F006E006C00790020006F0077006E006500720020006F0066002000740068006500200063006F006E00740072006100630074002000630061006E00200073006500740020006E006500770020006F0077006E00650072002E00004B740068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000494F006E006C007900200074006F006B0065006E0020006F0077006E00650072002000630061006E0020006200750072006E002000740068006500200074006F006B0065006E002E000039540068006500200061006400640072006500730073002000630061006E0020006E006F00740020006200650020007A00650072006F002E0000000000FD646BC443B245489ABE08ABE6FFCD940004200101080320000105200101111104200012410420010B0E052002010E0B052001111D0E062002010E111D042001020E052002010E0204200012450320000B073001011D1E000E040A01111C062002010E12490B07041D11181130122511240520010112210500020E0E1C05200201020E05200102111D0420001251083001011D1E001D05040A01111803200002042000111D06300101011E00040A0111240930010312250B1D1C0B040A01120C040A011210040A01121409070411200E1229112804200012590B20051229111D0B0E1D1C0B0320001C040A0111280720021229111D0B0907041229111D1C111D0306111D07000202111D111D04070112290A0704111C1D111C08111C0507011D111C0B07051D111C0B081118111C0807031D11180811180A0706111C0B0B0B0B11200420010E0E052002010E0E0507020B1134040A0111340607030B0B11340407011138040A0111380600030E0E1C1C063001011E000E040A01113C07300102010E1E00050702113C0B0407020B0B040701113C080704113C0B0B12290507020B1140040A0111400607030B0B11400407011144040A011144042001010E040701111D0607040B0B0B0B060702111D12290407011148040A011148040701114C040A01114C0407011150040A0111500407011154040A0111540707040B0B0B11480A0706111D0B0B0B0B1148087CEC85D7BEA7798E04000000000401000000040200000002060B02060E02060202060903061130042001010B05200101111D04200101020520001D111C062001011D111C112009011221111D090B0E0E111D111D1D05092004122511300B0E0E042000111C062001011D11180820011D111C1D111804200011200820040112210B0E0E0320000E0520010B111D06200201111D0B06200202111D0B08200302111D111D0B07200302111D0B0B08200301111D111D0B0720020B111D111D062001113C111D07200201111D113C0720020B111D113C04200102090520020109020420010E0B052001111D0B062002010B111D07200202111D111D08200301111D111D020420010B0B052002010B0B0620020E111D0B0620020B111D0B07200301111D0B0B0720030112210E0E0A200401111D111D0B1D0506200201111D020328000B042800111D032800020528001D111C0328000E0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000200000000000000000000000000000000000000100000000000000000000000000000000468000000000000000000001E68000000200000000000000000000000000000000000000000000010680000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000C000000303800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +``` diff --git a/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs b/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs new file mode 100644 index 00000000..b5f4087c --- /dev/null +++ b/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs @@ -0,0 +1,74 @@ +using NBitcoin; +using Stratis.SmartContracts; +using System; +using System.Collections.Generic; +using static STOContract; + +namespace STOContractTests +{ + public class InMemoryState : IPersistentState + { + private readonly Dictionary storage = new Dictionary(); + + public bool IsContractResult { get; set; } + + public void Clear(string key) => this.storage.Remove(key); + + public T GetValue(string key) => (T)this.storage.GetValueOrDefault(key, default(T)); + + public Address GetAddress(string key) => this.GetValue
(key); + + public T[] GetArray(string key) => this.GetValue(key); + + public bool GetBool(string key) => this.GetValue(key); + + public byte[] GetBytes(byte[] key) => throw new NotImplementedException(); + + public byte[] GetBytes(string key) => this.GetValue(key); + + public char GetChar(string key) => this.GetValue(key); + + public int GetInt32(string key) => this.GetValue(key); + + public long GetInt64(string key) => this.GetValue(key); + + public string GetString(string key) => this.GetValue(key); + + public T GetStruct(string key) + where T : struct => this.GetValue(key); + + public uint GetUInt32(string key) => this.GetValue(key); + + public ulong GetUInt64(string key) => this.GetValue(key); + + public bool IsContract(Address address) => this.IsContractResult; + + public void SetAddress(string key, Address value) => this.storage.AddOrReplace(key, value); + + public void SetArray(string key, Array a) => this.storage.AddOrReplace(key, a); + + public void SetBool(string key, bool value) => this.storage.AddOrReplace(key, value); + + public void SetBytes(byte[] key, byte[] value) + { + throw new NotImplementedException(); + } + + public void SetBytes(string key, byte[] value) => this.storage.AddOrReplace(key, value); + + public void SetChar(string key, char value) => this.storage.AddOrReplace(key, value); + + public void SetInt32(string key, int value) => this.storage.AddOrReplace(key, value); + + public void SetInt64(string key, long value) => this.storage.AddOrReplace(key, value); + + public void SetString(string key, string value) => this.storage.AddOrReplace(key, value); + + public void SetStruct(string key, T value) + where T : struct => this.storage.AddOrReplace(key, value); + + public void SetUInt32(string key, uint value) => this.storage.AddOrReplace(key, value); + + public void SetUInt64(string key, ulong value) => this.storage.AddOrReplace(key, value); + } +} \ No newline at end of file diff --git a/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj b/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj new file mode 100644 index 00000000..f570e31e --- /dev/null +++ b/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.1 + STOContractTests + + + + + + + + + + + + + diff --git a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs new file mode 100644 index 00000000..55277fb4 --- /dev/null +++ b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs @@ -0,0 +1,379 @@ +namespace STOContractTests +{ + using Moq; + using NBitcoin; + using Stratis.SmartContracts; + using Stratis.SmartContracts.CLR; + using Stratis.SmartContracts.CLR.Serialization; + using Xunit; + using SalePeriod = STOContract.SalePeriod; + using SalePeriodInput = STOContract.SalePeriodInput; + using TokenType = STOContract.TokenType; + + public class STOContractTests + { + private const ulong Satoshis = 100_000_000; + + private readonly Mock mContractState; + private readonly Mock mContractLogger; + private readonly Mock mTransactionExecutor; + private readonly Mock mBlock; + private readonly ICreateResult createSuccess; + private Address sender; + private Address owner; + private Address investor; + private Address identity; + private Address contract; + private Address tokenContract; + private Address kycContract; + private Address mapperContract; + + private InMemoryState persistentState; + private string name; + private string symbol; + private ulong totalSupply; + private Mock network; + private Serializer serializer; + + public STOContractTests() + { + this.mContractLogger = new Mock(); + this.mContractState = new Mock(); + this.mTransactionExecutor = new Mock(); + this.persistentState = new InMemoryState(); + this.network = new Mock(); + this.mBlock = new Mock(); + this.mContractState.Setup(s => s.Block).Returns(this.mBlock.Object); + this.mContractState.Setup(s => s.PersistentState).Returns(this.persistentState); + this.mContractState.Setup(s => s.ContractLogger).Returns(this.mContractLogger.Object); + this.mContractState.Setup(s => s.InternalTransactionExecutor).Returns(this.mTransactionExecutor.Object); + this.serializer = new Serializer(new ContractPrimitiveSerializer(this.network.Object)); + this.mContractState.Setup(s => s.Serializer).Returns(this.serializer); + this.sender = "0x0000000000000000000000000000000000000001".HexToAddress(); + this.owner = "0x0000000000000000000000000000000000000002".HexToAddress(); + this.investor = "0x0000000000000000000000000000000000000003".HexToAddress(); + this.identity = "0x0000000000000000000000000000000000000004".HexToAddress(); + this.contract = "0x0000000000000000000000000000000000000005".HexToAddress(); + this.tokenContract = "0x0000000000000000000000000000000000000006".HexToAddress(); + this.kycContract = "0x0000000000000000000000000000000000000007".HexToAddress(); + this.mapperContract = "0x0000000000000000000000000000000000000008".HexToAddress(); + this.createSuccess = CreateResult.Succeeded(this.tokenContract); + this.name = "Test Token"; + this.symbol = "TST"; + this.totalSupply = 100 * Satoshis; + this.persistentState.IsContractResult = true; + } + + [Fact] + public void Constructor_IsContract_ReturnsFalse_ThrowsAssertException() + { + this.persistentState.IsContractResult = false; + + Assert.Throws(() => this.Create(TokenType.StandardToken)); + } + + [Fact] + public void Constructor_TokenType_HigherThan2_ThrowsAssertException() + { + var tokenType = (TokenType)3; + + Assert.Throws(() => this.Create(tokenType)); + } + + [Fact] + public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() + { + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(CreateResult.Failed()); + Assert.Throws(() => this.Create(TokenType.StandardToken)); + + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny()), Times.Once); + } + + [Fact] + public void Constructor_TokenTypeIsStandardToken_Success() + { + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + var (contract, periods) = this.Create(TokenType.StandardToken); + + Assert.Equal(this.totalSupply, contract.TokenBalance); + Assert.Equal(this.owner, contract.Owner); + Assert.Equal(this.tokenContract, contract.TokenAddress); + Assert.Equal(4ul, contract.EndBlock); + Assert.Equal(periods, contract.SalePeriods); + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, 0), Times.Once); + } + + [Fact] + public void Constructor_TokenTypeIsDividendToken_Success() + { + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + var (contract, periods) = this.Create(TokenType.DividendToken); + + Assert.Equal(this.totalSupply, contract.TokenBalance); + Assert.Equal(this.owner, contract.Owner); + Assert.Equal(this.tokenContract, contract.TokenAddress); + Assert.Equal(4ul, contract.EndBlock); + Assert.Equal(periods, contract.SalePeriods); + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, 0), Times.Once); + } + + [Fact] + public void Constructor_TokenTypeIsNonFungibleToken_Success() + { + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + var (contract, periods) = this.Create(TokenType.NonFungibleToken); + + Assert.Equal(ulong.MaxValue, contract.TokenBalance); + Assert.Equal(this.owner, contract.Owner); + Assert.Equal(this.tokenContract, contract.TokenAddress); + Assert.Equal(4ul, contract.EndBlock); + Assert.Equal(periods, contract.SalePeriods); + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, 0), Times.Once); + } + + public (STOContract contract, SalePeriod[] periods) Create(TokenType tokenType) + { + var periodInputs = new[] + { + new SalePeriodInput { PricePerToken = 3 * Satoshis, DurationBlocks = 1 }, + new SalePeriodInput { PricePerToken = 5 * Satoshis, DurationBlocks = 2 } + }; + var periods = new[] + { + new SalePeriod { PricePerToken = 3 * Satoshis, EndBlock = 2 }, + new SalePeriod { PricePerToken = 5 * Satoshis, EndBlock = 4 } + }; + + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, 0)); + this.mBlock.Setup(s => s.Number).Returns(1); + var contract = new STOContract(this.mContractState.Object, this.owner, (uint)tokenType, this.totalSupply, this.name, this.symbol, this.kycContract,this.mapperContract, this.serializer.Serialize(periodInputs)); + return (contract, periods); + } + + [Fact] + public void Invest_CalledForStandardToken_Success() + { + var amount = 15 * Satoshis; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, 5ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + Assert.True(contract.Invest()); + + Assert.Equal(this.totalSupply - 5ul, contract.TokenBalance); + this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + + this.mBlock.Setup(s => s.Number).Returns(4); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, 3ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + Assert.True(contract.Invest()); + + Assert.Equal(this.totalSupply - 8ul, contract.TokenBalance); + this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + } + + [Fact] + public void Invest_CalledForNonFungibleToken_Success() + { + var amount = 15 * Satoshis; + var totalSupply = ulong.MaxValue; + + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.NonFungibleToken); + + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 5ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + Assert.True(contract.Invest()); + + Assert.Equal(totalSupply - 5ul, contract.TokenBalance); + this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + + this.mBlock.Setup(s => s.Number).Returns(4); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 3ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + Assert.True(contract.Invest()); + + Assert.Equal(totalSupply - 8ul, contract.TokenBalance); + this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + } + + [Fact] + public void Invest_Refunds_Oversold_Tokens() + { + this.totalSupply = 60; + var amount = 190 * Satoshis; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, this.totalSupply }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + Assert.True(contract.Invest()); + + Assert.Equal(0ul, contract.TokenBalance); // All tokens are sold + this.mTransactionExecutor.Verify(s => s.Transfer(this.mContractState.Object, this.investor, 10 * Satoshis), Times.Once); + } + + [Fact] + public void Invest_Fails_If_TokenBalance_Is_Zero() + { + var amount = 1 * Satoshis; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.investor, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + this.persistentState.SetUInt64(nameof(STOContract.TokenBalance), 0); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void Invest_Fails_If_EndBlock_Reached() + { + var amount = 1 * Satoshis; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + this.mBlock.Setup(s => s.Number).Returns(5); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void Invest_Fails_If_Investment_Amount_Is_Zero() + { + var amount = 0ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() + { + var amount = 10ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Failed()); + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() + { + var amount = 10ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(Address.Zero)); + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void Invest_Fails_If_GetClaim_Call_Fails() + { + var amount = 10ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Failed()); + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void Invest_Fails_If_GetClaim_Call_Returns_Null() + { + var amount = 10ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(null)); + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + + Assert.Throws(() => contract.Invest()); + } + + [Fact] + public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() + { + var amount = 0ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + this.mBlock.Setup(m => m.Number).Returns(5); + + Assert.Throws(() => contract.WithdrawFunds()); + } + + [Fact] + public void WithdrawFunds_Fails_If_Sale_Is_Open() + { + var amount = 0ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); + this.mBlock.Setup(m => m.Number).Returns(4); + + Assert.Throws(() => contract.WithdrawFunds()); + } + + [Fact] + public void WithdrawFunds_Called_By_Owner_Success() + { + var amount = 0ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); + this.mBlock.Setup(m => m.Number).Returns(4); + + Assert.Throws(() => contract.WithdrawFunds()); + } + + [Fact] + public void WithdrawTokens_Called_By_Owner_After_Sale_Is_Closed_Success() + { + var amount = 0ul; + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + + var (contract, _) = this.Create(TokenType.StandardToken); + this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); + this.mBlock.Setup(m => m.Number).Returns(5); + this.persistentState.SetUInt64(nameof(contract.TokenBalance), 100); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, 100ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + + var success = contract.WithdrawTokens(); + + Assert.True(success); + Assert.Equal(0ul, this.persistentState.GetUInt64(nameof(contract.TokenBalance))); + this.mTransactionExecutor.Verify(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, 100ul }, 0)); + } + } +} diff --git a/Mainnet/STOContract/STOContract.sln b/Mainnet/STOContract/STOContract.sln new file mode 100644 index 00000000..c41d8b23 --- /dev/null +++ b/Mainnet/STOContract/STOContract.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29920.165 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STOContract", "STOContract\STOContract.csproj", "{5A9239B7-6AB3-40AC-BC6A-42B6F6E7FE76}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "STOContract.Tests", "STOContract.Tests\STOContract.Tests.csproj", "{658848FE-FF0B-43CE-8B58-96CCAD85CD8E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5A9239B7-6AB3-40AC-BC6A-42B6F6E7FE76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A9239B7-6AB3-40AC-BC6A-42B6F6E7FE76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A9239B7-6AB3-40AC-BC6A-42B6F6E7FE76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A9239B7-6AB3-40AC-BC6A-42B6F6E7FE76}.Release|Any CPU.Build.0 = Release|Any CPU + {658848FE-FF0B-43CE-8B58-96CCAD85CD8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {658848FE-FF0B-43CE-8B58-96CCAD85CD8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {658848FE-FF0B-43CE-8B58-96CCAD85CD8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {658848FE-FF0B-43CE-8B58-96CCAD85CD8E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {91890BE7-62E8-4268-ADB3-2AD475A31C1E} + EndGlobalSection +EndGlobal diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs new file mode 100644 index 00000000..c515ab3c --- /dev/null +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -0,0 +1,1361 @@ +using Stratis.SmartContracts; +using Stratis.SmartContracts.Standards; + +[Deploy] +public class STOContract : SmartContract +{ + public ulong EndBlock + { + get => this.PersistentState.GetUInt64(nameof(EndBlock)); + private set => this.PersistentState.SetUInt64(nameof(EndBlock), value); + } + + public Address TokenAddress + { + get => this.PersistentState.GetAddress(nameof(TokenAddress)); + private set => this.PersistentState.SetAddress(nameof(TokenAddress), value); + } + + public Address KYCAddress + { + get => this.PersistentState.GetAddress(nameof(KYCAddress)); + private set => this.PersistentState.SetAddress(nameof(KYCAddress), value); + } + + public Address MapperAddress + { + get => this.PersistentState.GetAddress(nameof(MapperAddress)); + private set => this.PersistentState.SetAddress(nameof(MapperAddress), value); + } + + public ulong TokenBalance + { + get => this.PersistentState.GetUInt64(nameof(TokenBalance)); + private set => this.PersistentState.SetUInt64(nameof(TokenBalance), value); + } + + public bool IsNonFungibleToken + { + get => this.PersistentState.GetBool(nameof(IsNonFungibleToken)); + private set => this.PersistentState.SetBool(nameof(IsNonFungibleToken), value); + } + + public bool SaleOpen => EndBlock >= this.Block.Number && TokenBalance > 0; + + public Address Owner + { + get => this.PersistentState.GetAddress(nameof(Owner)); + private set => this.PersistentState.SetAddress(nameof(Owner), value); + } + + public SalePeriod[] SalePeriods + { + get => PersistentState.GetArray(nameof(SalePeriods)); + private set => PersistentState.SetArray(nameof(SalePeriods), value); + } + + public STOContract(ISmartContractState smartContractState, + Address owner, + uint tokenType, + ulong totalSupply, + string name, + string symbol, + Address kycAddress, + Address mapperAddress, + byte[] salePeriods) : base(smartContractState) + { + Assert(tokenType < 3, $"The {nameof(tokenType)} parameter can be between 0 and 2"); + + Assert(PersistentState.IsContract(kycAddress), "The kycAdress is not a contract adress"); + + var periods = Serializer.ToArray(salePeriods); + + ValidatePeriods(periods); + var tokenTypeEnum = (TokenType)tokenType; + var result = CreateTokenContract(tokenTypeEnum, totalSupply, name, symbol); + + Assert(result.Success, "Creating token contract failed."); + + Log(new STOSetupLog { TokenAddress = result.NewContractAddress }); + + KYCAddress = kycAddress; + MapperAddress = mapperAddress; + TokenAddress = result.NewContractAddress; + IsNonFungibleToken = tokenTypeEnum == TokenType.NonFungibleToken; + TokenBalance = IsNonFungibleToken ? ulong.MaxValue : totalSupply; + Owner = owner; + SetPeriods(periods); + } + + private ICreateResult CreateTokenContract(TokenType tokenType, ulong totalSupply, string name, string symbol) + { + switch (tokenType) + { + case TokenType.StandardToken: return Create(parameters: new object[] { totalSupply, name, symbol }); + case TokenType.DividendToken: return Create(parameters: new object[] { totalSupply, name, symbol }); + default: return Create(parameters: new object[] { name, symbol }); + } + } + public override void Receive() => Invest(); + + public bool Invest() + { + Assert(SaleOpen, "The STO is completed."); + Assert(Message.Value > 0, "The amount should be higher than zero"); + + EnsureKycVerified(); + + var saleInfo = GetSaleInfo(); + + var method = IsNonFungibleToken ? nameof(NonFungibleToken.MintAll) : nameof(IStandardToken.TransferTo); + var result = Call(TokenAddress, 0, method, new object[] { Message.Sender, saleInfo.TokenAmount }); + + Assert(result.Success && (bool)result.ReturnValue, "Token transfer failed."); + + Log(new InvestLog { Sender = Message.Sender, Invested = saleInfo.Invested, TokenAmount = saleInfo.TokenAmount, Refunded = saleInfo.RefundAmount }); + + TokenBalance = checked(TokenBalance - saleInfo.TokenAmount); + + if (saleInfo.RefundAmount > 0) // refund over sold amount + Transfer(Message.Sender, saleInfo.RefundAmount); + + return true; + } + + private void EnsureKycVerified() + { + var result = Call(MapperAddress, 0, "GetSecondaryAddress", new object[] { Message.Sender }); + + if (result.Success && result.ReturnValue is Address identityAddress && identityAddress != Address.Zero) + { + result = Call(KYCAddress, 0, "GetClaim", new object[] { identityAddress, (uint)3 /*shufti kyc*/ }); + + Assert(result.Success && result.ReturnValue != null, "Your KYC is not verified."); + + return; + } + + Assert(false, "The address has no mapping."); + } + + public bool WithdrawFunds() + { + Assert(Message.Sender == Owner, "Only contract owner can transfer funds."); + Assert(!SaleOpen, "STO is not ended yet."); + + var result = Transfer(this.Owner, Balance); + + return result.Success; + } + + public bool WithdrawTokens() + { + Assert(!IsNonFungibleToken, $"The {nameof(WithdrawTokens)} method is not supported for Non-Fungible Token."); + Assert(Message.Sender == Owner, "Only contract owner can transfer tokens."); + Assert(!SaleOpen, "STO is not ended yet."); + + var result = Call(TokenAddress, 0, nameof(StandardToken.TransferTo), new object[] { Message.Sender, TokenBalance }); + + TokenBalance = 0; + + Assert(result.Success && (bool)result.ReturnValue, "Token transfer failed."); + + return true; + } + + private SalePeriod GetCurrentPeriod() + { + var result = default(SalePeriod); + + foreach (var period in SalePeriods) + { + if (period.EndBlock >= Block.Number) + { + result = period; + break; + } + } + + return result; + } + private void SetPeriods(SalePeriodInput[] periods) + { + var salePeriods = ConvertSalePeriodInputs(periods); + + SalePeriods = salePeriods; + EndBlock = salePeriods[salePeriods.Length - 1].EndBlock; + } + + private SalePeriod[] ConvertSalePeriodInputs(SalePeriodInput[] periods) + { + var result = new SalePeriod[periods.Length]; + var blockNumber = Block.Number; + for (int i = 0; i < periods.Length; i++) + { + var input = periods[i]; + blockNumber = checked(blockNumber + input.DurationBlocks); + result[i] = new SalePeriod + { + EndBlock = blockNumber, + PricePerToken = input.PricePerToken + }; + } + + return result; + } + + private void ValidatePeriods(SalePeriodInput[] periods) + { + Assert(periods.Length > 0, "Please provide at least 1 sale period"); + + foreach (var period in periods) + { + Assert(period.DurationBlocks > 0, "DurationBlocks should higher than zero"); + } + } + + private SaleInfo GetSaleInfo() + { + var period = GetCurrentPeriod(); + + var tokenAmount = Message.Value / period.PricePerToken; + + var tokenBalance = TokenBalance; + if (tokenAmount > tokenBalance) // refund over sold amount + { + var spend = checked(tokenBalance * period.PricePerToken); + var refund = Message.Value - spend; + + return new SaleInfo { Invested = spend, RefundAmount = refund, TokenAmount = tokenBalance }; + } + + return new SaleInfo { Invested = Message.Value, TokenAmount = tokenAmount }; + } + + public struct SalePeriodInput + { + public ulong DurationBlocks; + public ulong PricePerToken; + } + + public struct SalePeriod + { + public ulong EndBlock; + public ulong PricePerToken; + } + + public struct SaleInfo + { + public ulong Invested; + public ulong RefundAmount; + public ulong TokenAmount; + + } + + public struct STOSetupLog + { + public Address TokenAddress; + } + + public struct InvestLog + { + [Index] + public Address Sender; + public ulong Invested; + public ulong TokenAmount; + public ulong Refunded; + } + + public struct Claim + { + public string Key; + + public string Description; + + public bool IsRevoked; + } + + public enum TokenType : uint + { + StandardToken, + DividendToken, + NonFungibleToken + } +} + +/// +/// Implementation of a standard token contract for the Stratis Platform. +/// +public class StandardToken : SmartContract, IStandardToken +{ + /// + /// Constructor used to create a new instance of the token. Assigns the total token supply to the creator of the contract. + /// + /// The execution state for the contract. + /// The total token supply. + /// The name of the token. + /// The symbol used to identify the token. + public StandardToken(ISmartContractState smartContractState, ulong totalSupply, string name, string symbol) + : base(smartContractState) + { + this.TotalSupply = totalSupply; + this.Name = name; + this.Symbol = symbol; + this.SetBalance(Message.Sender, totalSupply); + } + + public string Symbol + { + get => PersistentState.GetString(nameof(this.Symbol)); + private set => PersistentState.SetString(nameof(this.Symbol), value); + } + + public string Name + { + get => PersistentState.GetString(nameof(this.Name)); + private set => PersistentState.SetString(nameof(this.Name), value); + } + + /// + public ulong TotalSupply + { + get => PersistentState.GetUInt64(nameof(this.TotalSupply)); + private set => PersistentState.SetUInt64(nameof(this.TotalSupply), value); + } + + /// + public ulong GetBalance(Address address) + { + return PersistentState.GetUInt64($"Balance:{address}"); + } + + private void SetBalance(Address address, ulong value) + { + PersistentState.SetUInt64($"Balance:{address}", value); + } + + /// + public bool TransferTo(Address to, ulong amount) + { + if (amount == 0) + { + Log(new TransferLog { From = Message.Sender, To = to, Amount = 0 }); + + return true; + } + + ulong senderBalance = GetBalance(Message.Sender); + + if (senderBalance < amount) + { + return false; + } + + SetBalance(Message.Sender, senderBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = Message.Sender, To = to, Amount = amount }); + + return true; + } + + /// + public bool TransferFrom(Address from, Address to, ulong amount) + { + if (amount == 0) + { + Log(new TransferLog { From = from, To = to, Amount = 0 }); + + return true; + } + + ulong senderAllowance = Allowance(from, Message.Sender); + ulong fromBalance = GetBalance(from); + + if (senderAllowance < amount || fromBalance < amount) + { + return false; + } + + SetApproval(from, Message.Sender, senderAllowance - amount); + + SetBalance(from, fromBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = from, To = to, Amount = amount }); + + return true; + } + + /// + public bool Approve(Address spender, ulong currentAmount, ulong amount) + { + if (Allowance(Message.Sender, spender) != currentAmount) + { + return false; + } + + SetApproval(Message.Sender, spender, amount); + + Log(new ApprovalLog { Owner = Message.Sender, Spender = spender, Amount = amount, OldAmount = currentAmount }); + + return true; + } + + private void SetApproval(Address owner, Address spender, ulong value) + { + PersistentState.SetUInt64($"Allowance:{owner}:{spender}", value); + } + + /// + public ulong Allowance(Address owner, Address spender) + { + return PersistentState.GetUInt64($"Allowance:{owner}:{spender}"); + } + + public struct TransferLog + { + [Index] + public Address From; + + [Index] + public Address To; + + public ulong Amount; + } + + public struct ApprovalLog + { + [Index] + public Address Owner; + + [Index] + public Address Spender; + + public ulong OldAmount; + + public ulong Amount; + } +} + +public class DividendToken : SmartContract, IStandardToken +{ + public ulong Dividends + { + get => PersistentState.GetUInt64(nameof(this.Dividends)); + private set => PersistentState.SetUInt64(nameof(this.Dividends), value); + } + + private Account GetAccount(Address address) => PersistentState.GetStruct($"Account:{address}"); + + private void SetAccount(Address address, Account account) => PersistentState.SetStruct($"Account:{address}", account); + + + public DividendToken(ISmartContractState state, ulong totalSupply, string name, string symbol) + : base(state) + { + this.TotalSupply = totalSupply; + this.Name = name; + this.Symbol = symbol; + this.SetBalance(Message.Sender, totalSupply); + } + + /// + /// It is advised that deposit amount should to be evenly divided by total supply, + /// otherwise small amount of satoshi may lost(burn) + /// + public override void Receive() + { + Dividends += Message.Value; + } + + public bool TransferTo(Address to, ulong amount) + { + UpdateAccount(Message.Sender); + UpdateAccount(to); + + return TransferTokensTo(to, amount); + } + + public bool TransferFrom(Address from, Address to, ulong amount) + { + UpdateAccount(from); + UpdateAccount(to); + + return TransferTokensFrom(from, to, amount); + } + + Account UpdateAccount(Address address) + { + var account = GetAccount(address); + var newDividends = GetWithdrawableDividends(address, account); + + if (newDividends > 0) + { + account.DividendBalance = checked(account.DividendBalance + newDividends); + account.CreditedDividends = Dividends; + SetAccount(address, account); + } + + return account; + } + + private ulong GetWithdrawableDividends(Address address, Account account) + { + var newDividends = Dividends - account.CreditedDividends; + var notCreditedDividends = checked(GetBalance(address) * newDividends); + + return checked(account.DividendBalance + notCreditedDividends); //Delay divide by TotalSupply to final stage for avoid decimal value loss. + } + + /// + /// Get Withdrawable dividends + /// + /// + public ulong GetDividends() => GetDividends(Message.Sender); + + /// + /// Get Withdrawable dividends + /// + /// + /// + public ulong GetDividends(Address address) + { + var account = GetAccount(address); + + return GetWithdrawableDividends(address, account) / TotalSupply; + } + + /// + /// Get the all divididends since beginning (Withdrawable Dividends + Withdrawn Dividends) + /// + /// + public ulong GetTotalDividends() => GetTotalDividends(Message.Sender); + + /// + /// Get the all divididends since beginning (Withdrawable Dividends + Withdrawn Dividends) + /// + /// + /// + public ulong GetTotalDividends(Address address) + { + var account = GetAccount(address); + return checked(GetWithdrawableDividends(address, account) + account.WithdrawnDividends) / TotalSupply; + } + + /// + /// Withdraws all dividends + /// + public void Withdraw() + { + var account = UpdateAccount(Message.Sender); + var balance = account.DividendBalance / TotalSupply; + var remainder = account.DividendBalance % TotalSupply; + + Assert(balance > 0, "The account has no dividends."); + + account.WithdrawnDividends = checked(account.WithdrawnDividends + account.DividendBalance - remainder); + account.DividendBalance = remainder; + + SetAccount(Message.Sender, account); + + var transfer = Transfer(Message.Sender, balance); + + Assert(transfer.Success, "Transfer failed."); + } + + public struct Account + { + /// + /// Withdrawable Dividend Balance. Exact value should to divided by + /// + public ulong DividendBalance; + + /// + /// + /// + + public ulong WithdrawnDividends; + + /// + /// Dividends computed and added to + /// + + public ulong CreditedDividends; + } + + #region StandardToken code is inlined + + public string Symbol + { + get => PersistentState.GetString(nameof(this.Symbol)); + private set => PersistentState.SetString(nameof(this.Symbol), value); + } + + public string Name + { + get => PersistentState.GetString(nameof(this.Name)); + private set => PersistentState.SetString(nameof(this.Name), value); + } + + /// + public ulong TotalSupply + { + get => PersistentState.GetUInt64(nameof(this.TotalSupply)); + private set => PersistentState.SetUInt64(nameof(this.TotalSupply), value); + } + /// + public ulong GetBalance(Address address) + { + return PersistentState.GetUInt64($"Balance:{address}"); + } + + private void SetBalance(Address address, ulong value) + { + PersistentState.SetUInt64($"Balance:{address}", value); + } + + /// + private bool TransferTokensTo(Address to, ulong amount) + { + if (amount == 0) + { + Log(new TransferLog { From = Message.Sender, To = to, Amount = 0 }); + + return true; + } + + ulong senderBalance = GetBalance(Message.Sender); + + if (senderBalance < amount) + { + return false; + } + + SetBalance(Message.Sender, senderBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = Message.Sender, To = to, Amount = amount }); + + return true; + } + + /// + private bool TransferTokensFrom(Address from, Address to, ulong amount) + { + if (amount == 0) + { + Log(new TransferLog { From = from, To = to, Amount = 0 }); + + return true; + } + + ulong senderAllowance = Allowance(from, Message.Sender); + ulong fromBalance = GetBalance(from); + + if (senderAllowance < amount || fromBalance < amount) + { + return false; + } + + SetApproval(from, Message.Sender, senderAllowance - amount); + + SetBalance(from, fromBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = from, To = to, Amount = amount }); + + return true; + } + + /// + public bool Approve(Address spender, ulong currentAmount, ulong amount) + { + if (Allowance(Message.Sender, spender) != currentAmount) + { + return false; + } + + SetApproval(Message.Sender, spender, amount); + + Log(new ApprovalLog { Owner = Message.Sender, Spender = spender, Amount = amount, OldAmount = currentAmount }); + + return true; + } + + private void SetApproval(Address owner, Address spender, ulong value) + { + PersistentState.SetUInt64($"Allowance:{owner}:{spender}", value); + } + + /// + public ulong Allowance(Address owner, Address spender) + { + return PersistentState.GetUInt64($"Allowance:{owner}:{spender}"); + } + + public struct TransferLog + { + [Index] + public Address From; + + [Index] + public Address To; + + public ulong Amount; + } + + public struct ApprovalLog + { + [Index] + public Address Owner; + + [Index] + public Address Spender; + + public ulong OldAmount; + + public ulong Amount; + } + #endregion +} + +public class NonFungibleToken : SmartContract +{ + public struct TransferLog + { + [Index] + public Address From; + [Index] + public Address To; + [Index] + public ulong TokenId; + } + + public struct ApprovalLog + { + [Index] + public Address Owner; + [Index] + public Address Approved; + [Index] + public ulong TokenId; + } + + public struct ApprovalForAllLog + { + [Index] + public Address Owner; + [Index] + public Address Operator; + + public bool Approved; + } + + public struct OwnershipTransferedLog + { + [Index] + public Address PreviousOwner; + + [Index] + public Address NewOwner; + } + + /// + /// Get a value indicacting if the interface is supported. + /// + /// The id of the interface to support. + /// A value indicating if the interface is supported. + private bool GetSupportedInterfaces(uint interfaceId) + { + return this.PersistentState.GetBool($"SupportedInterface:{interfaceId}"); + } + + /// + /// Sets the supported interface value. + /// + /// The interface id. + /// A value indicating if the interface id is supported. + private void SetSupportedInterfaces(uint interfaceId, bool value) => this.PersistentState.SetBool($"SupportedInterface:{interfaceId}", value); + + /// + /// Gets the key to the persistent state for the owner by NFT ID. + /// + /// The NFT ID. + /// The persistent storage key to get or set the NFT owner. + private string GetIdToOwnerKey(ulong id) => $"IdToOwner:{id}"; + + /// + /// Gets the address of the owner of the NFT ID. + /// + /// The ID of the NFT + ///The owner address. + private Address GetIdToOwner(ulong id) => this.PersistentState.GetAddress(GetIdToOwnerKey(id)); + + /// + /// Sets the owner to the NFT ID. + /// + /// The ID of the NFT + /// The address of the owner. + private void SetIdToOwner(ulong id, Address value) => this.PersistentState.SetAddress(GetIdToOwnerKey(id), value); + + /// + /// Gets the key to the persistent state for the approval address by NFT ID. + /// + /// The NFT ID. + /// The persistent storage key to get or set the NFT approval. + private string GetIdToApprovalKey(ulong id) => $"IdToApproval:{id}"; + + /// + /// Getting from NFT ID the approval address. + /// + /// The ID of the NFT + /// Address of the approval. + private Address GetIdToApproval(ulong id) => this.PersistentState.GetAddress(GetIdToApprovalKey(id)); + + /// + /// Setting to NFT ID to approval address. + /// + /// The ID of the NFT + /// The address of the approval. + private void SetIdToApproval(ulong id, Address value) => this.PersistentState.SetAddress(GetIdToApprovalKey(id), value); + + /// + /// Gets the amount of non fungible tokens the owner has. + /// + /// The address of the owner. + /// The amount of non fungible tokens. + private ulong GetOwnerToNFTokenCount(Address address) => this.PersistentState.GetUInt64($"OwnerToNFTokenCount:{address}"); + + /// + /// Sets the owner count of this non fungible tokens. + /// + /// The address of the owner. + /// The amount of tokens. + private void SetOwnerToNFTokenCount(Address address, ulong value) => this.PersistentState.SetUInt64($"OwnerToNFTokenCount:{address}", value); + + /// + /// Gets the permission value of the operator authorization to perform actions on behalf of the owner. + /// + /// The owner address of the NFT. + /// >Address of the authorized operators + /// A value indicating if the operator has permissions to act on behalf of the owner. + private bool GetOwnerToOperator(Address owner, Address operatorAddress) => this.PersistentState.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); + + /// + /// Sets the owner to operator permission. + /// + /// The owner address of the NFT. + /// >Address to add to the set of authorized operators. + /// The permission value. + private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => this.PersistentState.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); + + /// + /// Owner of the contract is responsible to for minting/burning + /// + public Address Owner + { + get => this.PersistentState.GetAddress(nameof(Owner)); + private set => this.PersistentState.SetAddress(nameof(Owner), value); + } + + /// + /// Name for non-fungible token contract + /// + public string Name + { + get => this.PersistentState.GetString(nameof(Name)); + private set => this.PersistentState.SetString(nameof(Name), value); + } + + /// + /// Symbol for non-fungible token contract + /// + public string Symbol + { + get => this.PersistentState.GetString(nameof(Symbol)); + private set => this.PersistentState.SetString(nameof(Symbol), value); + } + + /// + /// The next token index which is going to be minted + /// + private ulong NextTokenId + { + get => this.PersistentState.GetUInt64(nameof(NextTokenId)); + set => this.PersistentState.SetUInt64(nameof(NextTokenId), value); + } + + private string GetTokenByIndexKey(ulong index) => $"TokenByIndex:{index}"; + + private ulong GetTokenByIndex(ulong index) => this.PersistentState.GetUInt64(GetTokenByIndexKey(index)); + + private void SetTokenByIndex(ulong index, ulong token) => this.PersistentState.SetUInt64(GetTokenByIndexKey(index), token); + + private void ClearTokenByIndex(ulong index) => this.PersistentState.Clear(GetTokenByIndexKey(index)); + + private string GetIndexByTokenKey(ulong token) => $"IndexByToken:{token}"; + + private ulong GetIndexByToken(ulong token) => this.PersistentState.GetUInt64(GetIndexByTokenKey(token)); + + private void SetIndexByToken(ulong token, ulong index) => this.PersistentState.SetUInt64(GetIndexByTokenKey(token), index); + + private void ClearIndexByToken(ulong token) => this.PersistentState.Clear(GetIndexByTokenKey(token)); + + private string GetTokenOfOwnerByIndexKey(Address address, ulong index) => $"TokenOfOwnerByIndex:{address}:{index}"; + + private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => this.PersistentState.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); + + private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => this.PersistentState.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); + + private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => this.PersistentState.Clear(GetTokenOfOwnerByIndexKey(owner, index)); + + private string IndexOfOwnerByTokenKey(Address owner, ulong tokenId) => $"IndexOfOwnerByToken:{owner}:{tokenId}"; + private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => this.PersistentState.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); + private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => this.PersistentState.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); + private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => this.PersistentState.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); + public ulong TotalSupply + { + get => this.PersistentState.GetUInt64(nameof(TotalSupply)); + private set => this.PersistentState.SetUInt64(nameof(TotalSupply), value); + } + + /// + /// Constructor. Initializes the supported interfaces. + /// + /// The smart contract state. + public NonFungibleToken(ISmartContractState state, string name, string symbol) : base(state) + { + // todo: discuss callback handling and supported interface numbering with community. + this.SetSupportedInterfaces((uint)0x00000001, true); // (ERC165) - ISupportsInterface + this.SetSupportedInterfaces((uint)0x00000002, true); // (ERC721) - INonFungibleToken, + this.SetSupportedInterfaces((uint)0x00000003, false); // (ERC721) - INonFungibleTokenReceiver + this.SetSupportedInterfaces((uint)0x00000004, true); // (ERC721) - INonFungibleTokenMetadata + this.SetSupportedInterfaces((uint)0x00000005, true); // (ERC721) - IERC721Enumerable + + this.Name = name; + this.Symbol = symbol; + this.Owner = Message.Sender; + this.NextTokenId = 1; + } + + public ulong TokenByIndex(ulong index) + { + Assert(index < TotalSupply, "The index is invalid."); + + return GetTokenByIndex(index); + } + + public ulong TokenOfOwnerByIndex(Address owner, ulong index) + { + Assert(index < GetOwnerToNFTokenCount(owner), "The index is invalid."); + + return GetTokenOfOwnerByIndex(owner, index); + } + + /// + /// Function to check which interfaces are supported by this contract. + /// + /// Id of the interface. + /// True if is supported, false otherwise. + public bool SupportsInterface(uint interfaceID) + { + return GetSupportedInterfaces(interfaceID); + } + + /// + /// Transfers the ownership of an NFT from one address to another address. This function can + /// be changed to payable. + /// + /// Throws unless is the current owner, an authorized operator, or the + /// approved address for this NFT.Throws if 'from' is not the current owner.Throws if 'to' is + /// the zero address.Throws if 'tokenId' is not a valid NFT. When transfer is complete, this + /// function checks if 'to' is a smart contract. If so, it calls + /// 'OnNonFungibleTokenReceived' on 'to' and throws if the return value true. + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + /// Additional data with no specified format, sent in call to 'to'. + public void SafeTransferFrom(Address from, Address to, ulong tokenId, byte[] data) + { + SafeTransferFromInternal(from, to, tokenId, data); + } + + /// + /// Transfers the ownership of an NFT from one address to another address. This function can + /// be changed to payable. + /// + /// This works identically to the other function with an extra data parameter, except this + /// function just sets data to an empty byte array. + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + public void SafeTransferFrom(Address from, Address to, ulong tokenId) + { + SafeTransferFromInternal(from, to, tokenId, new byte[0]); + } + + /// + /// Throws unless is the current owner, an authorized operator, or the approved + /// address for this NFT.Throws if is not the current owner.Throws if is the zero + /// address.Throws if is not a valid NFT. This function can be changed to payable. + /// + /// The caller is responsible to confirm that is capable of receiving NFTs or else + /// they maybe be permanently lost. + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + public void TransferFrom(Address from, Address to, ulong tokenId) + { + CanTransfer(tokenId); + + Address tokenOwner = GetIdToOwner(tokenId); + EnsureAddressIsNotEmpty(tokenOwner); + EnsureAddressIsNotEmpty(to); + Assert(tokenOwner == from); + + TransferInternal(to, tokenId); + } + + /// + /// Set or reaffirm the approved address for an NFT. This function can be changed to payable. + /// + /// + /// The zero address indicates there is no approved address. Throws unless is + /// the current NFT owner, or an authorized operator of the current owner. + /// + /// Address to be approved for the given NFT ID. + /// ID of the token to be approved. + public void Approve(Address approved, ulong tokenId) + { + CanOperate(tokenId); + ValidNFToken(tokenId); + + Address tokenOwner = GetIdToOwner(tokenId); + Assert(approved != tokenOwner); + + SetIdToApproval(tokenId, approved); + LogApproval(tokenOwner, approved, tokenId); + } + + /// + /// Enables or disables approval for a third party ("operator") to manage all of + /// 's assets. It also Logs the ApprovalForAll event. + /// + /// This works even if sender doesn't own any tokens at the time. + /// Address to add to the set of authorized operators. + /// True if the operators is approved, false to revoke approval. + public void SetApprovalForAll(Address operatorAddress, bool approved) + { + SetOwnerToOperator(this.Message.Sender, operatorAddress, approved); + LogApprovalForAll(this.Message.Sender, operatorAddress, approved); + } + + /// + /// Returns the number of NFTs owned by 'owner'. NFTs assigned to the zero address are + /// considered invalid, and this function throws for queries about the zero address. + /// + /// Address for whom to query the balance. + /// Balance of owner. + public ulong BalanceOf(Address owner) + { + EnsureAddressIsNotEmpty(owner); + return GetOwnerToNFTokenCount(owner); + } + + /// + /// Returns the address of the owner of the NFT. NFTs assigned to zero address are considered invalid, and queries about them do throw. + /// + /// The identifier for an NFT. + /// Address of tokenId owner. + public Address OwnerOf(ulong tokenId) + { + Address owner = GetIdToOwner(tokenId); + EnsureAddressIsNotEmpty(owner); + return owner; + } + + /// + /// Get the approved address for a single NFT. + /// + /// Throws if 'tokenId' is not a valid NFT. + /// ID of the NFT to query the approval of. + /// Address that tokenId is approved for. + public Address GetApproved(ulong tokenId) + { + ValidNFToken(tokenId); + + return GetIdToApproval(tokenId); + } + + /// + /// Checks if 'operator' is an approved operator for 'owner'. + /// + /// The address that owns the NFTs. + /// The address that acts on behalf of the owner. + /// True if approved for all, false otherwise. + public bool IsApprovedForAll(Address owner, Address operatorAddress) + { + return GetOwnerToOperator(owner, operatorAddress); + } + + /// + /// Actually preforms the transfer. + /// + /// Does NO checks. + /// Address of a new owner. + /// The NFT that is being transferred. + private void TransferInternal(Address to, ulong tokenId) + { + Address from = GetIdToOwner(tokenId); + ClearApproval(tokenId); + + RemoveNFToken(from, tokenId); + AddNFToken(to, tokenId); + + LogTransfer(from, to, tokenId); + } + + /// + /// Removes a NFT from owner. + /// + /// Use and override this function with caution. Wrong usage can have serious consequences. + /// Address from wich we want to remove the NFT. + /// Which NFT we want to remove. + private void RemoveNFToken(Address from, ulong tokenId) + { + Assert(GetIdToOwner(tokenId) == from); + var tokenCount = GetOwnerToNFTokenCount(from); + SetOwnerToNFTokenCount(from, checked(tokenCount - 1)); + this.PersistentState.Clear(GetIdToOwnerKey(tokenId)); + + ulong index = GetIndexOfOwnerByToken(from, tokenId); + ulong lastIndex = tokenCount - 1; + + if (index != lastIndex) + { + ulong lastToken = GetTokenOfOwnerByIndex(from, lastIndex); + SetIndexOfOwnerByToken(from, lastToken, index); + SetTokenOfOwnerByIndex(from, index, lastToken); + } + + ClearTokenOfOwnerByIndex(from, lastIndex); + ClearIndexOfOwnerByToken(from, tokenId); + } + + /// + /// Assignes a new NFT to owner. + /// + /// Use and override this function with caution. Wrong usage can have serious consequences. + /// Address to which we want to add the NFT. + /// Which NFT we want to add. + private void AddNFToken(Address to, ulong tokenId) + { + Assert(GetIdToOwner(tokenId) == Address.Zero); + + SetIdToOwner(tokenId, to); + ulong currentTokenAmount = GetOwnerToNFTokenCount(to); + SetOwnerToNFTokenCount(to, checked(currentTokenAmount + 1)); + + var index = currentTokenAmount; + SetIndexOfOwnerByToken(to, tokenId, index); + SetTokenOfOwnerByIndex(to, index, tokenId); + } + + /// + /// Actually perform the safeTransferFrom. + /// + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + /// Additional data with no specified format, sent in call to 'to' if it is a contract. + private void SafeTransferFromInternal(Address from, Address to, ulong tokenId, byte[] data) + { + CanTransfer(tokenId); + ValidNFToken(tokenId); + + Address tokenOwner = GetIdToOwner(tokenId); + Assert(tokenOwner == from); + EnsureAddressIsNotEmpty(to); + + TransferInternal(to, tokenId); + + if (this.PersistentState.IsContract(to)) + { + ITransferResult result = this.Call(to, 0, "OnNonFungibleTokenReceived", new object[] { this.Message.Sender, from, tokenId, data }, 0); + Assert((bool)result.ReturnValue); + } + } + + /// + /// Clears the current approval of a given NFT ID. + /// + /// ID of the NFT to be transferred + private void ClearApproval(ulong tokenId) + { + if (GetIdToApproval(tokenId) != Address.Zero) + { + this.PersistentState.Clear(GetIdToApprovalKey(tokenId)); + } + } + + /// + /// This logs when ownership of any NFT changes by any mechanism. This event logs when NFTs are + /// created('from' == 0) and destroyed('to' == 0). Exception: during contract creation, any + /// number of NFTs may be created and assigned without logging Transfer.At the time of any + /// transfer, the approved Address for that NFT (if any) is reset to none. + /// + /// The from address. + /// The to address. + /// The NFT ID. + private void LogTransfer(Address from, Address to, ulong tokenId) + { + Log(new TransferLog() { From = from, To = to, TokenId = tokenId }); + } + + /// + /// This logs when the approved Address for an NFT is changed or reaffirmed. The zero + /// Address indicates there is no approved Address. When a Transfer logs, this also + /// indicates that the approved Address for that NFT (if any) is reset to none. + /// + /// The owner address. + /// The approved address. + /// The NFT ID. + private void LogApproval(Address owner, Address approved, ulong tokenId) + { + Log(new ApprovalLog() { Owner = owner, Approved = approved, TokenId = tokenId }); + } + + /// + /// This logs when an operator is enabled or disabled for an owner. The operator can manage all NFTs of the owner. + /// + /// The owner address + /// The operator address. + /// A boolean indicating if it has been approved. + private void LogApprovalForAll(Address owner, Address operatorAddress, bool approved) + { + Log(new ApprovalForAllLog() { Owner = owner, Operator = operatorAddress, Approved = approved }); + } + + + /// + /// Guarantees that the is an owner or operator of the given NFT. + /// + /// ID of the NFT to validate. + private void CanOperate(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + Assert(tokenOwner == this.Message.Sender || GetOwnerToOperator(tokenOwner, this.Message.Sender)); + } + + /// + /// Guarantees that the msg.sender is allowed to transfer NFT. + /// + /// ID of the NFT to transfer. + private void CanTransfer(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + Assert( + tokenOwner == this.Message.Sender + || GetIdToApproval(tokenId) == Message.Sender + || GetOwnerToOperator(tokenOwner, Message.Sender) + ); + } + + /// + /// Guarantees that tokenId is a valid Token. + /// + /// ID of the NFT to validate. + private void ValidNFToken(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + EnsureAddressIsNotEmpty(tokenOwner); + } + + /// + /// Sets the contract owner who can mint/bur + /// + /// + public void TransferOwnership(Address owner) + { + EnsureOwnerOnly(); + Assert(owner != Address.Zero, $"The {nameof(owner)} parameter can not be default(zero) address."); + + Log(new OwnershipTransferedLog { PreviousOwner = this.Owner, NewOwner = owner }); + + this.Owner = owner; + } + + private void EnsureOwnerOnly() + { + Assert(Message.Sender == Owner, "Only owner of the contract can set new owner."); + } + + /// + /// Mints new tokens + /// + /// The address that will own the minted NFT + /// Number of tokens will be created + public void MintAll(Address address, ulong amount) + { + EnsureOwnerOnly(); + EnsureAddressIsNotEmpty(address); + Assert(amount > 0, "the amount should be higher than zero"); + + var index = TotalSupply; + var lastIndex = checked(index + amount); + var tokenId = NextTokenId; + + while (index < lastIndex) + { + AddNFToken(address, tokenId); + SetTokenByIndex(index, tokenId); + SetIndexByToken(tokenId, index); + + Log(new TransferLog { From = Address.Zero, To = address, TokenId = tokenId }); + + checked + { + index++; + tokenId++; + } + } + + TotalSupply = checked(TotalSupply + amount); + NextTokenId = tokenId; + } + + public void Burn(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + + EnsureAddressIsNotEmpty(tokenOwner); + + Assert(tokenOwner == Message.Sender, "Only token owner can burn the token."); + + ClearApproval(tokenId); + RemoveNFToken(tokenOwner, tokenId); + + //move last token to removed token and delete last token info + var index = GetIndexByToken(tokenId); + var lastTokenIndex = checked(--TotalSupply); + var lastToken = GetTokenByIndex(lastTokenIndex); + + SetTokenByIndex(index, lastToken); + SetIndexByToken(lastToken, index); + + ClearTokenByIndex(lastTokenIndex); + ClearIndexByToken(tokenId); + + Log(new TransferLog { From = tokenOwner, To = Address.Zero, TokenId = tokenId }); + } + + public void EnsureAddressIsNotEmpty(Address address) + { + Assert(address != Address.Zero, "The address can not be zero."); + } +} \ No newline at end of file diff --git a/Mainnet/STOContract/STOContract/STOContract.csproj b/Mainnet/STOContract/STOContract/STOContract.csproj new file mode 100644 index 00000000..6e90aeb4 --- /dev/null +++ b/Mainnet/STOContract/STOContract/STOContract.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.1 + + 8.0 + + + + + + + + Never + + + From a0fc2ef2c74140fcabc5c5a4c82fb1156241cdce Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 27 Jan 2021 02:54:25 +0300 Subject: [PATCH 02/12] STO contract upgraded to new types and tokens --- .../STOContract.Tests/AddressExtensions.cs | 42 ++++ .../STOContract.Tests/InMemoryState.cs | 68 +++--- .../STOContract.Tests.csproj | 2 - .../STOContract.Tests/STOContractTests.cs | 158 ++++++++------ .../STOContract/STOContract/STOContract.cs | 198 ++++++++++-------- .../STOContract/STOContract.csproj | 6 +- 6 files changed, 296 insertions(+), 178 deletions(-) create mode 100644 Mainnet/STOContract/STOContract.Tests/AddressExtensions.cs diff --git a/Mainnet/STOContract/STOContract.Tests/AddressExtensions.cs b/Mainnet/STOContract/STOContract.Tests/AddressExtensions.cs new file mode 100644 index 00000000..4d21961e --- /dev/null +++ b/Mainnet/STOContract/STOContract.Tests/AddressExtensions.cs @@ -0,0 +1,42 @@ +using Stratis.SmartContracts; +using System; +using System.Collections.Generic; +using System.Text; + +namespace STOContractTests +{ + public static class AddressExtensions + { + private static byte[] HexStringToBytes(string val) + { + if (val.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + val = val.Substring(2); + + byte[] ret = new byte[val.Length / 2]; + for (int i = 0; i < val.Length; i = i + 2) + { + string hexChars = val.Substring(i, 2); + ret[i / 2] = byte.Parse(hexChars, System.Globalization.NumberStyles.HexNumber); + } + return ret; + } + + public static Address HexToAddress(this string hexString) + { + // uint160 only parses a big-endian hex string + var result = HexStringToBytes(hexString); + return CreateAddress(result); + } + + private static Address CreateAddress(byte[] bytes) + { + uint pn0 = BitConverter.ToUInt32(bytes, 0); + uint pn1 = BitConverter.ToUInt32(bytes, 4); + uint pn2 = BitConverter.ToUInt32(bytes, 8); + uint pn3 = BitConverter.ToUInt32(bytes, 12); + uint pn4 = BitConverter.ToUInt32(bytes, 16); + + return new Address(pn0, pn1, pn2, pn3, pn4); + } + } +} diff --git a/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs b/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs index b5f4087c..acf89a6e 100644 --- a/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs +++ b/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs @@ -1,5 +1,4 @@ -using NBitcoin; -using Stratis.SmartContracts; +using Stratis.SmartContracts; using System; using System.Collections.Generic; using static STOContract; @@ -9,66 +8,77 @@ namespace STOContractTests public class InMemoryState : IPersistentState { private readonly Dictionary storage = new Dictionary(); - public bool IsContractResult { get; set; } + public void Clear(string key) => storage.Remove(key); - public void Clear(string key) => this.storage.Remove(key); - - public T GetValue(string key) => (T)this.storage.GetValueOrDefault(key, default(T)); + public T GetValue(string key) => (T)storage.GetValueOrDefault(key, default(T)); - public Address GetAddress(string key) => this.GetValue
(key); + public void AddOrReplace(string key, object value) + { + if (!storage.TryAdd(key,value)) + storage[key] = value; + } + public Address GetAddress(string key) => GetValue
(key); - public T[] GetArray(string key) => this.GetValue(key); + public T[] GetArray(string key) => GetValue(key); - public bool GetBool(string key) => this.GetValue(key); + public bool GetBool(string key) => GetValue(key); public byte[] GetBytes(byte[] key) => throw new NotImplementedException(); - public byte[] GetBytes(string key) => this.GetValue(key); + public byte[] GetBytes(string key) => GetValue(key); - public char GetChar(string key) => this.GetValue(key); + public char GetChar(string key) => GetValue(key); - public int GetInt32(string key) => this.GetValue(key); + public int GetInt32(string key) => GetValue(key); - public long GetInt64(string key) => this.GetValue(key); + public long GetInt64(string key) => GetValue(key); - public string GetString(string key) => this.GetValue(key); + public string GetString(string key) => GetValue(key); public T GetStruct(string key) - where T : struct => this.GetValue(key); + where T : struct => GetValue(key); - public uint GetUInt32(string key) => this.GetValue(key); + public uint GetUInt32(string key) => GetValue(key); - public ulong GetUInt64(string key) => this.GetValue(key); + public ulong GetUInt64(string key) => GetValue(key); - public bool IsContract(Address address) => this.IsContractResult; + public UInt128 GetUInt128(string key) => GetValue(key); - public void SetAddress(string key, Address value) => this.storage.AddOrReplace(key, value); + public UInt256 GetUInt256(string key) => GetValue(key); - public void SetArray(string key, Array a) => this.storage.AddOrReplace(key, a); + public bool IsContract(Address address) => IsContractResult; - public void SetBool(string key, bool value) => this.storage.AddOrReplace(key, value); + public void SetAddress(string key, Address value) => AddOrReplace(key, value); + + public void SetArray(string key, Array a) => AddOrReplace(key, a); + + public void SetBool(string key, bool value) => AddOrReplace(key, value); public void SetBytes(byte[] key, byte[] value) { throw new NotImplementedException(); } - public void SetBytes(string key, byte[] value) => this.storage.AddOrReplace(key, value); + public void SetBytes(string key, byte[] value) => AddOrReplace(key, value); - public void SetChar(string key, char value) => this.storage.AddOrReplace(key, value); + public void SetChar(string key, char value) => AddOrReplace(key, value); - public void SetInt32(string key, int value) => this.storage.AddOrReplace(key, value); + public void SetInt32(string key, int value) => AddOrReplace(key, value); - public void SetInt64(string key, long value) => this.storage.AddOrReplace(key, value); + public void SetInt64(string key, long value) => AddOrReplace(key, value); - public void SetString(string key, string value) => this.storage.AddOrReplace(key, value); + public void SetString(string key, string value) => AddOrReplace(key, value); public void SetStruct(string key, T value) - where T : struct => this.storage.AddOrReplace(key, value); + where T : struct => AddOrReplace(key, value); + + public void SetUInt32(string key, uint value) => AddOrReplace(key, value); + + public void SetUInt64(string key, ulong value) => AddOrReplace(key, value); - public void SetUInt32(string key, uint value) => this.storage.AddOrReplace(key, value); + public void SetUInt128(string key, UInt128 value) => AddOrReplace(key, value); - public void SetUInt64(string key, ulong value) => this.storage.AddOrReplace(key, value); + public void SetUInt256(string key, UInt256 value) => AddOrReplace(key, value); } } \ No newline at end of file diff --git a/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj b/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj index f570e31e..5d87b345 100644 --- a/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj +++ b/Mainnet/STOContract/STOContract.Tests/STOContract.Tests.csproj @@ -8,8 +8,6 @@ - - diff --git a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs index 55277fb4..17551b2f 100644 --- a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs +++ b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs @@ -1,10 +1,7 @@ namespace STOContractTests { using Moq; - using NBitcoin; using Stratis.SmartContracts; - using Stratis.SmartContracts.CLR; - using Stratis.SmartContracts.CLR.Serialization; using Xunit; using SalePeriod = STOContract.SalePeriod; using SalePeriodInput = STOContract.SalePeriodInput; @@ -17,8 +14,9 @@ public class STOContractTests private readonly Mock mContractState; private readonly Mock mContractLogger; private readonly Mock mTransactionExecutor; + private readonly Mock mSerializer; private readonly Mock mBlock; - private readonly ICreateResult createSuccess; + private Address sender; private Address owner; private Address investor; @@ -29,11 +27,10 @@ public class STOContractTests private Address mapperContract; private InMemoryState persistentState; + private UInt256 totalSupply; private string name; private string symbol; - private ulong totalSupply; - private Mock network; - private Serializer serializer; + private uint decimals; public STOContractTests() { @@ -41,14 +38,13 @@ public STOContractTests() this.mContractState = new Mock(); this.mTransactionExecutor = new Mock(); this.persistentState = new InMemoryState(); - this.network = new Mock(); this.mBlock = new Mock(); this.mContractState.Setup(s => s.Block).Returns(this.mBlock.Object); this.mContractState.Setup(s => s.PersistentState).Returns(this.persistentState); this.mContractState.Setup(s => s.ContractLogger).Returns(this.mContractLogger.Object); this.mContractState.Setup(s => s.InternalTransactionExecutor).Returns(this.mTransactionExecutor.Object); - this.serializer = new Serializer(new ContractPrimitiveSerializer(this.network.Object)); - this.mContractState.Setup(s => s.Serializer).Returns(this.serializer); + this.mSerializer = new Mock(); + this.mContractState.Setup(s => s.Serializer).Returns(this.mSerializer.Object); this.sender = "0x0000000000000000000000000000000000000001".HexToAddress(); this.owner = "0x0000000000000000000000000000000000000002".HexToAddress(); this.investor = "0x0000000000000000000000000000000000000003".HexToAddress(); @@ -57,13 +53,52 @@ public STOContractTests() this.tokenContract = "0x0000000000000000000000000000000000000006".HexToAddress(); this.kycContract = "0x0000000000000000000000000000000000000007".HexToAddress(); this.mapperContract = "0x0000000000000000000000000000000000000008".HexToAddress(); - this.createSuccess = CreateResult.Succeeded(this.tokenContract); + this.name = "Test Token"; this.symbol = "TST"; this.totalSupply = 100 * Satoshis; + this.decimals = 0; this.persistentState.IsContractResult = true; } + private ICreateResult createSucceed() + { + var mock = new Mock(); + + mock.SetupGet(m => m.Success).Returns(true); + mock.SetupGet(m => m.NewContractAddress).Returns(this.tokenContract); + + return mock.Object; + } + + private ICreateResult createFailed() + { + var mock = new Mock(); + + mock.SetupGet(m => m.Success).Returns(false); + + return mock.Object; + } + + private ITransferResult transferSucceed(object returnValue = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.Success).Returns(true); + mock.SetupGet(m => m.ReturnValue).Returns(returnValue); + + return mock.Object; + } + + private ITransferResult transferFailed() + { + var mock = new Mock(); + + mock.SetupGet(m => m.Success).Returns(false); + + return mock.Object; + } + [Fact] public void Constructor_IsContract_ReturnsFalse_ThrowsAssertException() { @@ -83,16 +118,16 @@ public void Constructor_TokenType_HigherThan2_ThrowsAssertException() [Fact] public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(CreateResult.Failed()); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createFailed()); Assert.Throws(() => this.Create(TokenType.StandardToken)); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny()), Times.Once); + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny()), Times.Once); } [Fact] public void Constructor_TokenTypeIsStandardToken_Success() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, periods) = this.Create(TokenType.StandardToken); Assert.Equal(this.totalSupply, contract.TokenBalance); @@ -100,13 +135,13 @@ public void Constructor_TokenTypeIsStandardToken_Success() Assert.Equal(this.tokenContract, contract.TokenAddress); Assert.Equal(4ul, contract.EndBlock); Assert.Equal(periods, contract.SalePeriods); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, 0), Times.Once); + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, 0), Times.Once); } [Fact] public void Constructor_TokenTypeIsDividendToken_Success() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, periods) = this.Create(TokenType.DividendToken); Assert.Equal(this.totalSupply, contract.TokenBalance); @@ -114,16 +149,16 @@ public void Constructor_TokenTypeIsDividendToken_Success() Assert.Equal(this.tokenContract, contract.TokenAddress); Assert.Equal(4ul, contract.EndBlock); Assert.Equal(periods, contract.SalePeriods); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, 0), Times.Once); + this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, 0), Times.Once); } [Fact] public void Constructor_TokenTypeIsNonFungibleToken_Success() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(createSucceed()); var (contract, periods) = this.Create(TokenType.NonFungibleToken); - Assert.Equal(ulong.MaxValue, contract.TokenBalance); + Assert.Equal((UInt256)ulong.MaxValue, contract.TokenBalance); Assert.Equal(this.owner, contract.Owner); Assert.Equal(this.tokenContract, contract.TokenAddress); Assert.Equal(4ul, contract.EndBlock); @@ -146,7 +181,8 @@ public void Constructor_TokenTypeIsNonFungibleToken_Success() this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, 0)); this.mBlock.Setup(s => s.Number).Returns(1); - var contract = new STOContract(this.mContractState.Object, this.owner, (uint)tokenType, this.totalSupply, this.name, this.symbol, this.kycContract,this.mapperContract, this.serializer.Serialize(periodInputs)); + this.mSerializer.Setup(m => m.ToArray(new byte[0])).Returns(periodInputs); + var contract = new STOContract(this.mContractState.Object, this.owner, (uint)tokenType, this.totalSupply, this.name, this.symbol, this.decimals, this.kycContract, this.mapperContract, new byte[0]); return (contract, periods); } @@ -154,15 +190,15 @@ public void Constructor_TokenTypeIsNonFungibleToken_Success() public void Invest_CalledForStandardToken_Success() { var amount = 15 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, 5ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, (UInt256)5 }, It.IsAny())).Returns(transferSucceed(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); Assert.True(contract.Invest()); @@ -170,7 +206,7 @@ public void Invest_CalledForStandardToken_Success() this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); this.mBlock.Setup(s => s.Number).Returns(4); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, 3ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, (UInt256)3 }, It.IsAny())).Returns(transferSucceed(true)); Assert.True(contract.Invest()); @@ -182,25 +218,25 @@ public void Invest_CalledForStandardToken_Success() public void Invest_CalledForNonFungibleToken_Success() { var amount = 15 * Satoshis; - var totalSupply = ulong.MaxValue; + var totalSupply = (UInt256)ulong.MaxValue; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.NonFungibleToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 5ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 5ul }, It.IsAny())).Returns(transferSucceed(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); Assert.True(contract.Invest()); - Assert.Equal(totalSupply - 5ul, contract.TokenBalance); + Assert.Equal(totalSupply - 5, contract.TokenBalance); this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); this.mBlock.Setup(s => s.Number).Returns(4); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 3ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 3ul }, It.IsAny())).Returns(transferSucceed(true)); Assert.True(contract.Invest()); @@ -213,18 +249,18 @@ public void Invest_Refunds_Oversold_Tokens() { this.totalSupply = 60; var amount = 190 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, this.totalSupply }, It.IsAny())).Returns(TransferResult.Transferred(true)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); - + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, this.totalSupply }, It.IsAny())).Returns(transferSucceed(true)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + this.mTransactionExecutor.Setup(m => m.Transfer(this.mContractState.Object, this.investor, 10 * Satoshis)).Returns(transferSucceed()); Assert.True(contract.Invest()); - Assert.Equal(0ul, contract.TokenBalance); // All tokens are sold + Assert.Equal((UInt256)0, contract.TokenBalance); // All tokens are sold this.mTransactionExecutor.Verify(s => s.Transfer(this.mContractState.Object, this.investor, 10 * Satoshis), Times.Once); } @@ -232,12 +268,12 @@ public void Invest_Refunds_Oversold_Tokens() public void Invest_Fails_If_TokenBalance_Is_Zero() { var amount = 1 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.investor, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.persistentState.SetUInt64(nameof(STOContract.TokenBalance), 0); + this.persistentState.SetUInt256(nameof(STOContract.TokenBalance), 0); Assert.Throws(() => contract.Invest()); } @@ -246,7 +282,7 @@ public void Invest_Fails_If_TokenBalance_Is_Zero() public void Invest_Fails_If_EndBlock_Reached() { var amount = 1 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); @@ -260,7 +296,7 @@ public void Invest_Fails_If_EndBlock_Reached() public void Invest_Fails_If_Investment_Amount_Is_Zero() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); @@ -272,8 +308,8 @@ public void Invest_Fails_If_Investment_Amount_Is_Zero() public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Failed()); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferFailed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); @@ -284,8 +320,8 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(Address.Zero)); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(Address.Zero)); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); @@ -296,9 +332,9 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() public void Invest_Fails_If_GetClaim_Call_Fails() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Failed()); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferFailed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); @@ -309,9 +345,9 @@ public void Invest_Fails_If_GetClaim_Call_Fails() public void Invest_Fails_If_GetClaim_Call_Returns_Null() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(TransferResult.Transferred(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3 /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(null)); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(null)); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); @@ -322,7 +358,7 @@ public void Invest_Fails_If_GetClaim_Call_Returns_Null() public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); @@ -335,7 +371,7 @@ public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() public void WithdrawFunds_Fails_If_Sale_Is_Open() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); @@ -348,7 +384,7 @@ public void WithdrawFunds_Fails_If_Sale_Is_Open() public void WithdrawFunds_Called_By_Owner_Success() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); @@ -361,19 +397,19 @@ public void WithdrawFunds_Called_By_Owner_Success() public void WithdrawTokens_Called_By_Owner_After_Sale_Is_Closed_Success() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol }, It.IsAny())).Returns(this.createSuccess); + this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); var (contract, _) = this.Create(TokenType.StandardToken); this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); this.mBlock.Setup(m => m.Number).Returns(5); - this.persistentState.SetUInt64(nameof(contract.TokenBalance), 100); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, 100ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + this.persistentState.SetUInt256(nameof(contract.TokenBalance), 100); + this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, (UInt256)100 }, It.IsAny())).Returns(transferSucceed(true)); var success = contract.WithdrawTokens(); Assert.True(success); - Assert.Equal(0ul, this.persistentState.GetUInt64(nameof(contract.TokenBalance))); - this.mTransactionExecutor.Verify(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, 100ul }, 0)); + Assert.Equal((UInt256)0, this.persistentState.GetUInt256(nameof(contract.TokenBalance))); + this.mTransactionExecutor.Verify(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, (UInt256)100 }, 0)); } } } diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index c515ab3c..cff3d92b 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -28,10 +28,10 @@ public Address MapperAddress private set => this.PersistentState.SetAddress(nameof(MapperAddress), value); } - public ulong TokenBalance + public UInt256 TokenBalance { - get => this.PersistentState.GetUInt64(nameof(TokenBalance)); - private set => this.PersistentState.SetUInt64(nameof(TokenBalance), value); + get => this.PersistentState.GetUInt256(nameof(TokenBalance)); + private set => this.PersistentState.SetUInt256(nameof(TokenBalance), value); } public bool IsNonFungibleToken @@ -57,22 +57,24 @@ public SalePeriod[] SalePeriods public STOContract(ISmartContractState smartContractState, Address owner, uint tokenType, - ulong totalSupply, + UInt256 totalSupply, string name, string symbol, + uint decimals, Address kycAddress, Address mapperAddress, byte[] salePeriods) : base(smartContractState) { - Assert(tokenType < 3, $"The {nameof(tokenType)} parameter can be between 0 and 2"); + Assert(tokenType < 3, $"The {nameof(tokenType)} parameter can be between 0 and 2."); - Assert(PersistentState.IsContract(kycAddress), "The kycAdress is not a contract adress"); + Assert(PersistentState.IsContract(kycAddress), $"The {nameof(kycAddress)} is not a contract adress."); + Assert(PersistentState.IsContract(mapperAddress), $"The {nameof(mapperAddress)} is not a contract adress."); var periods = Serializer.ToArray(salePeriods); ValidatePeriods(periods); var tokenTypeEnum = (TokenType)tokenType; - var result = CreateTokenContract(tokenTypeEnum, totalSupply, name, symbol); + var result = CreateTokenContract(tokenTypeEnum, totalSupply, name, symbol, decimals); Assert(result.Success, "Creating token contract failed."); @@ -82,19 +84,19 @@ public STOContract(ISmartContractState smartContractState, MapperAddress = mapperAddress; TokenAddress = result.NewContractAddress; IsNonFungibleToken = tokenTypeEnum == TokenType.NonFungibleToken; - TokenBalance = IsNonFungibleToken ? ulong.MaxValue : totalSupply; + TokenBalance = IsNonFungibleToken ? (UInt256)ulong.MaxValue : totalSupply; Owner = owner; SetPeriods(periods); } - private ICreateResult CreateTokenContract(TokenType tokenType, ulong totalSupply, string name, string symbol) + private ICreateResult CreateTokenContract(TokenType tokenType, UInt256 totalSupply, string name, string symbol, uint decimals) { - switch (tokenType) + return tokenType switch { - case TokenType.StandardToken: return Create(parameters: new object[] { totalSupply, name, symbol }); - case TokenType.DividendToken: return Create(parameters: new object[] { totalSupply, name, symbol }); - default: return Create(parameters: new object[] { name, symbol }); - } + TokenType.StandardToken => Create(parameters: new object[] { totalSupply, name, symbol, decimals }), + TokenType.DividendToken => Create(parameters: new object[] { totalSupply, name, symbol, decimals }), + _ => Create(parameters: new object[] { name, symbol }), + }; } public override void Receive() => Invest(); @@ -107,17 +109,21 @@ public bool Invest() var saleInfo = GetSaleInfo(); - var method = IsNonFungibleToken ? nameof(NonFungibleToken.MintAll) : nameof(IStandardToken.TransferTo); - var result = Call(TokenAddress, 0, method, new object[] { Message.Sender, saleInfo.TokenAmount }); + var result = IsNonFungibleToken ? + Call(TokenAddress, 0, nameof(NonFungibleToken.MintAll), new object[] { Message.Sender, (ulong)saleInfo.TokenAmount }) : + Call(TokenAddress, 0, nameof(IStandardToken.TransferTo), new object[] { Message.Sender, saleInfo.TokenAmount }); Assert(result.Success && (bool)result.ReturnValue, "Token transfer failed."); Log(new InvestLog { Sender = Message.Sender, Invested = saleInfo.Invested, TokenAmount = saleInfo.TokenAmount, Refunded = saleInfo.RefundAmount }); - TokenBalance = checked(TokenBalance - saleInfo.TokenAmount); + TokenBalance -= saleInfo.TokenAmount; if (saleInfo.RefundAmount > 0) // refund over sold amount - Transfer(Message.Sender, saleInfo.RefundAmount); + { + result = Transfer(Message.Sender, saleInfo.RefundAmount); + Assert(result.Success, "Refund failed."); + } return true; } @@ -130,7 +136,7 @@ private void EnsureKycVerified() { result = Call(KYCAddress, 0, "GetClaim", new object[] { identityAddress, (uint)3 /*shufti kyc*/ }); - Assert(result.Success && result.ReturnValue != null, "Your KYC is not verified."); + Assert(result.Success && result.ReturnValue is byte[] b && b?.Length > 0, "Your KYC is not verified."); return; } @@ -148,6 +154,7 @@ public bool WithdrawFunds() return result.Success; } + public bool WithdrawTokens() { Assert(!IsNonFungibleToken, $"The {nameof(WithdrawTokens)} method is not supported for Non-Fungible Token."); @@ -223,7 +230,7 @@ private SaleInfo GetSaleInfo() var tokenBalance = TokenBalance; if (tokenAmount > tokenBalance) // refund over sold amount { - var spend = checked(tokenBalance * period.PricePerToken); + var spend = (ulong)tokenBalance * period.PricePerToken; var refund = Message.Value - spend; return new SaleInfo { Invested = spend, RefundAmount = refund, TokenAmount = tokenBalance }; @@ -248,7 +255,7 @@ public struct SaleInfo { public ulong Invested; public ulong RefundAmount; - public ulong TokenAmount; + public UInt256 TokenAmount; } @@ -262,7 +269,7 @@ public struct InvestLog [Index] public Address Sender; public ulong Invested; - public ulong TokenAmount; + public UInt256 TokenAmount; public ulong Refunded; } @@ -295,12 +302,13 @@ public class StandardToken : SmartContract, IStandardToken /// The total token supply. /// The name of the token. /// The symbol used to identify the token. - public StandardToken(ISmartContractState smartContractState, ulong totalSupply, string name, string symbol) + public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply, string name, string symbol, uint decimals) : base(smartContractState) { this.TotalSupply = totalSupply; this.Name = name; this.Symbol = symbol; + this.Decimals = decimals; this.SetBalance(Message.Sender, totalSupply); } @@ -317,25 +325,32 @@ public string Name } /// - public ulong TotalSupply + public uint Decimals { - get => PersistentState.GetUInt64(nameof(this.TotalSupply)); - private set => PersistentState.SetUInt64(nameof(this.TotalSupply), value); + get => PersistentState.GetUInt32(nameof(this.Decimals)); + private set => PersistentState.SetUInt32(nameof(this.Decimals), value); } /// - public ulong GetBalance(Address address) + public UInt256 TotalSupply { - return PersistentState.GetUInt64($"Balance:{address}"); + get => PersistentState.GetUInt256(nameof(this.TotalSupply)); + private set => PersistentState.SetUInt256(nameof(this.TotalSupply), value); } - private void SetBalance(Address address, ulong value) + /// + public UInt256 GetBalance(Address address) { - PersistentState.SetUInt64($"Balance:{address}", value); + return PersistentState.GetUInt256($"Balance:{address}"); + } + + private void SetBalance(Address address, UInt256 value) + { + PersistentState.SetUInt256($"Balance:{address}", value); } /// - public bool TransferTo(Address to, ulong amount) + public bool TransferTo(Address to, UInt256 amount) { if (amount == 0) { @@ -344,7 +359,7 @@ public bool TransferTo(Address to, ulong amount) return true; } - ulong senderBalance = GetBalance(Message.Sender); + UInt256 senderBalance = GetBalance(Message.Sender); if (senderBalance < amount) { @@ -361,7 +376,7 @@ public bool TransferTo(Address to, ulong amount) } /// - public bool TransferFrom(Address from, Address to, ulong amount) + public bool TransferFrom(Address from, Address to, UInt256 amount) { if (amount == 0) { @@ -370,8 +385,8 @@ public bool TransferFrom(Address from, Address to, ulong amount) return true; } - ulong senderAllowance = Allowance(from, Message.Sender); - ulong fromBalance = GetBalance(from); + UInt256 senderAllowance = Allowance(from, Message.Sender); + UInt256 fromBalance = GetBalance(from); if (senderAllowance < amount || fromBalance < amount) { @@ -390,7 +405,7 @@ public bool TransferFrom(Address from, Address to, ulong amount) } /// - public bool Approve(Address spender, ulong currentAmount, ulong amount) + public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) { if (Allowance(Message.Sender, spender) != currentAmount) { @@ -404,15 +419,15 @@ public bool Approve(Address spender, ulong currentAmount, ulong amount) return true; } - private void SetApproval(Address owner, Address spender, ulong value) + private void SetApproval(Address owner, Address spender, UInt256 value) { - PersistentState.SetUInt64($"Allowance:{owner}:{spender}", value); + PersistentState.SetUInt256($"Allowance:{owner}:{spender}", value); } /// - public ulong Allowance(Address owner, Address spender) + public UInt256 Allowance(Address owner, Address spender) { - return PersistentState.GetUInt64($"Allowance:{owner}:{spender}"); + return PersistentState.GetUInt256($"Allowance:{owner}:{spender}"); } public struct TransferLog @@ -423,7 +438,7 @@ public struct TransferLog [Index] public Address To; - public ulong Amount; + public UInt256 Amount; } public struct ApprovalLog @@ -434,14 +449,15 @@ public struct ApprovalLog [Index] public Address Spender; - public ulong OldAmount; + public UInt256 OldAmount; - public ulong Amount; + public UInt256 Amount; } } public class DividendToken : SmartContract, IStandardToken { + public ulong Dividends { get => PersistentState.GetUInt64(nameof(this.Dividends)); @@ -453,25 +469,32 @@ public ulong Dividends private void SetAccount(Address address, Account account) => PersistentState.SetStruct($"Account:{address}", account); - public DividendToken(ISmartContractState state, ulong totalSupply, string name, string symbol) + public DividendToken(ISmartContractState state, UInt256 totalSupply, string name, string symbol, uint decimals) : base(state) { this.TotalSupply = totalSupply; this.Name = name; this.Symbol = symbol; + this.Decimals = decimals; this.SetBalance(Message.Sender, totalSupply); } + + public override void Receive() + { + DistributeDividends(); + } + /// /// It is advised that deposit amount should to be evenly divided by total supply, /// otherwise small amount of satoshi may lost(burn) /// - public override void Receive() + public void DistributeDividends() { Dividends += Message.Value; } - public bool TransferTo(Address to, ulong amount) + public bool TransferTo(Address to, UInt256 amount) { UpdateAccount(Message.Sender); UpdateAccount(to); @@ -479,7 +502,7 @@ public bool TransferTo(Address to, ulong amount) return TransferTokensTo(to, amount); } - public bool TransferFrom(Address from, Address to, ulong amount) + public bool TransferFrom(Address from, Address to, UInt256 amount) { UpdateAccount(from); UpdateAccount(to); @@ -487,14 +510,14 @@ public bool TransferFrom(Address from, Address to, ulong amount) return TransferTokensFrom(from, to, amount); } - Account UpdateAccount(Address address) + private Account UpdateAccount(Address address) { var account = GetAccount(address); - var newDividends = GetWithdrawableDividends(address, account); + var newDividends = GetNewDividends(address, account); if (newDividends > 0) { - account.DividendBalance = checked(account.DividendBalance + newDividends); + account.DividendBalance += newDividends; account.CreditedDividends = Dividends; SetAccount(address, account); } @@ -502,12 +525,15 @@ Account UpdateAccount(Address address) return account; } - private ulong GetWithdrawableDividends(Address address, Account account) + private UInt256 GetWithdrawableDividends(Address address, Account account) { - var newDividends = Dividends - account.CreditedDividends; - var notCreditedDividends = checked(GetBalance(address) * newDividends); + return account.DividendBalance + GetNewDividends(address, account); //Delay divide by TotalSupply to final stage for avoid decimal value loss. + } - return checked(account.DividendBalance + notCreditedDividends); //Delay divide by TotalSupply to final stage for avoid decimal value loss. + private UInt256 GetNewDividends(Address address, Account account) + { + var notCreditedDividends = checked(Dividends - account.CreditedDividends); + return GetBalance(address) * notCreditedDividends; } /// @@ -542,7 +568,8 @@ public ulong GetDividends(Address address) public ulong GetTotalDividends(Address address) { var account = GetAccount(address); - return checked(GetWithdrawableDividends(address, account) + account.WithdrawnDividends) / TotalSupply; + var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; + return withdrawable + account.WithdrawnDividends; } /// @@ -552,12 +579,12 @@ public void Withdraw() { var account = UpdateAccount(Message.Sender); var balance = account.DividendBalance / TotalSupply; - var remainder = account.DividendBalance % TotalSupply; Assert(balance > 0, "The account has no dividends."); - account.WithdrawnDividends = checked(account.WithdrawnDividends + account.DividendBalance - remainder); - account.DividendBalance = remainder; + account.WithdrawnDividends += balance; + + account.DividendBalance %= TotalSupply; SetAccount(Message.Sender, account); @@ -571,11 +598,7 @@ public struct Account /// /// Withdrawable Dividend Balance. Exact value should to divided by /// - public ulong DividendBalance; - - /// - /// - /// + public UInt256 DividendBalance; public ulong WithdrawnDividends; @@ -588,6 +611,7 @@ public struct Account #region StandardToken code is inlined + public string Symbol { get => PersistentState.GetString(nameof(this.Symbol)); @@ -601,24 +625,32 @@ public string Name } /// - public ulong TotalSupply + public UInt256 TotalSupply { - get => PersistentState.GetUInt64(nameof(this.TotalSupply)); - private set => PersistentState.SetUInt64(nameof(this.TotalSupply), value); + get => PersistentState.GetUInt256(nameof(this.TotalSupply)); + private set => PersistentState.SetUInt256(nameof(this.TotalSupply), value); } + + public uint Decimals + { + get => PersistentState.GetUInt32(nameof(Decimals)); + private set => PersistentState.SetUInt32(nameof(Decimals), value); + } + + /// - public ulong GetBalance(Address address) + public UInt256 GetBalance(Address address) { - return PersistentState.GetUInt64($"Balance:{address}"); + return PersistentState.GetUInt256($"Balance:{address}"); } - private void SetBalance(Address address, ulong value) + private void SetBalance(Address address, UInt256 value) { - PersistentState.SetUInt64($"Balance:{address}", value); + PersistentState.SetUInt256($"Balance:{address}", value); } /// - private bool TransferTokensTo(Address to, ulong amount) + private bool TransferTokensTo(Address to, UInt256 amount) { if (amount == 0) { @@ -627,7 +659,7 @@ private bool TransferTokensTo(Address to, ulong amount) return true; } - ulong senderBalance = GetBalance(Message.Sender); + UInt256 senderBalance = GetBalance(Message.Sender); if (senderBalance < amount) { @@ -644,7 +676,7 @@ private bool TransferTokensTo(Address to, ulong amount) } /// - private bool TransferTokensFrom(Address from, Address to, ulong amount) + private bool TransferTokensFrom(Address from, Address to, UInt256 amount) { if (amount == 0) { @@ -653,8 +685,8 @@ private bool TransferTokensFrom(Address from, Address to, ulong amount) return true; } - ulong senderAllowance = Allowance(from, Message.Sender); - ulong fromBalance = GetBalance(from); + UInt256 senderAllowance = Allowance(from, Message.Sender); + UInt256 fromBalance = GetBalance(from); if (senderAllowance < amount || fromBalance < amount) { @@ -673,7 +705,7 @@ private bool TransferTokensFrom(Address from, Address to, ulong amount) } /// - public bool Approve(Address spender, ulong currentAmount, ulong amount) + public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) { if (Allowance(Message.Sender, spender) != currentAmount) { @@ -687,15 +719,15 @@ public bool Approve(Address spender, ulong currentAmount, ulong amount) return true; } - private void SetApproval(Address owner, Address spender, ulong value) + private void SetApproval(Address owner, Address spender, UInt256 value) { - PersistentState.SetUInt64($"Allowance:{owner}:{spender}", value); + PersistentState.SetUInt256($"Allowance:{owner}:{spender}", value); } /// - public ulong Allowance(Address owner, Address spender) + public UInt256 Allowance(Address owner, Address spender) { - return PersistentState.GetUInt64($"Allowance:{owner}:{spender}"); + return PersistentState.GetUInt256($"Allowance:{owner}:{spender}"); } public struct TransferLog @@ -706,7 +738,7 @@ public struct TransferLog [Index] public Address To; - public ulong Amount; + public UInt256 Amount; } public struct ApprovalLog @@ -717,9 +749,9 @@ public struct ApprovalLog [Index] public Address Spender; - public ulong OldAmount; + public UInt256 OldAmount; - public ulong Amount; + public UInt256 Amount; } #endregion } diff --git a/Mainnet/STOContract/STOContract/STOContract.csproj b/Mainnet/STOContract/STOContract/STOContract.csproj index 6e90aeb4..86174c2e 100644 --- a/Mainnet/STOContract/STOContract/STOContract.csproj +++ b/Mainnet/STOContract/STOContract/STOContract.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 @@ -6,8 +6,8 @@ 8.0 - - + + From 8c3ce1f444c238a5777dd0672078797456535f7d Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 27 Jan 2021 03:14:18 +0300 Subject: [PATCH 03/12] Code cleanup --- .../STOContract/STOContract/STOContract.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index cff3d92b..351dfb42 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -6,46 +6,46 @@ public class STOContract : SmartContract { public ulong EndBlock { - get => this.PersistentState.GetUInt64(nameof(EndBlock)); - private set => this.PersistentState.SetUInt64(nameof(EndBlock), value); + get => PersistentState.GetUInt64(nameof(EndBlock)); + private set => PersistentState.SetUInt64(nameof(EndBlock), value); } public Address TokenAddress { - get => this.PersistentState.GetAddress(nameof(TokenAddress)); - private set => this.PersistentState.SetAddress(nameof(TokenAddress), value); + get => PersistentState.GetAddress(nameof(TokenAddress)); + private set => PersistentState.SetAddress(nameof(TokenAddress), value); } public Address KYCAddress { - get => this.PersistentState.GetAddress(nameof(KYCAddress)); - private set => this.PersistentState.SetAddress(nameof(KYCAddress), value); + get => PersistentState.GetAddress(nameof(KYCAddress)); + private set => PersistentState.SetAddress(nameof(KYCAddress), value); } public Address MapperAddress { - get => this.PersistentState.GetAddress(nameof(MapperAddress)); - private set => this.PersistentState.SetAddress(nameof(MapperAddress), value); + get => PersistentState.GetAddress(nameof(MapperAddress)); + private set => PersistentState.SetAddress(nameof(MapperAddress), value); } public UInt256 TokenBalance { - get => this.PersistentState.GetUInt256(nameof(TokenBalance)); - private set => this.PersistentState.SetUInt256(nameof(TokenBalance), value); + get => PersistentState.GetUInt256(nameof(TokenBalance)); + private set => PersistentState.SetUInt256(nameof(TokenBalance), value); } public bool IsNonFungibleToken { - get => this.PersistentState.GetBool(nameof(IsNonFungibleToken)); - private set => this.PersistentState.SetBool(nameof(IsNonFungibleToken), value); + get => PersistentState.GetBool(nameof(IsNonFungibleToken)); + private set => PersistentState.SetBool(nameof(IsNonFungibleToken), value); } - public bool SaleOpen => EndBlock >= this.Block.Number && TokenBalance > 0; + public bool SaleOpen => EndBlock >= Block.Number && TokenBalance > 0; public Address Owner { - get => this.PersistentState.GetAddress(nameof(Owner)); - private set => this.PersistentState.SetAddress(nameof(Owner), value); + get => PersistentState.GetAddress(nameof(Owner)); + private set => PersistentState.SetAddress(nameof(Owner), value); } public SalePeriod[] SalePeriods @@ -149,7 +149,7 @@ public bool WithdrawFunds() Assert(Message.Sender == Owner, "Only contract owner can transfer funds."); Assert(!SaleOpen, "STO is not ended yet."); - var result = Transfer(this.Owner, Balance); + var result = Transfer(Owner, Balance); return result.Success; } From 3e0c7a2c28040c47a77a2f9746bef899cdaca3e3 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 27 Jan 2021 03:23:56 +0300 Subject: [PATCH 04/12] Code clean up --- .../STOContract.Tests/STOContractTests.cs | 294 +++++++++--------- 1 file changed, 147 insertions(+), 147 deletions(-) diff --git a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs index 17551b2f..9451167e 100644 --- a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs +++ b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs @@ -17,48 +17,48 @@ public class STOContractTests private readonly Mock mSerializer; private readonly Mock mBlock; - private Address sender; - private Address owner; - private Address investor; - private Address identity; - private Address contract; - private Address tokenContract; - private Address kycContract; - private Address mapperContract; - - private InMemoryState persistentState; + private readonly Address sender; + private readonly Address owner; + private readonly Address investor; + private readonly Address identity; + private readonly Address currentContract; + private readonly Address tokenContract; + private readonly Address kycContract; + private readonly Address mapperContract; + + private readonly InMemoryState persistentState; private UInt256 totalSupply; - private string name; - private string symbol; - private uint decimals; + private readonly string name; + private readonly string symbol; + private readonly uint decimals; public STOContractTests() { - this.mContractLogger = new Mock(); - this.mContractState = new Mock(); - this.mTransactionExecutor = new Mock(); - this.persistentState = new InMemoryState(); - this.mBlock = new Mock(); - this.mContractState.Setup(s => s.Block).Returns(this.mBlock.Object); - this.mContractState.Setup(s => s.PersistentState).Returns(this.persistentState); - this.mContractState.Setup(s => s.ContractLogger).Returns(this.mContractLogger.Object); - this.mContractState.Setup(s => s.InternalTransactionExecutor).Returns(this.mTransactionExecutor.Object); - this.mSerializer = new Mock(); - this.mContractState.Setup(s => s.Serializer).Returns(this.mSerializer.Object); - this.sender = "0x0000000000000000000000000000000000000001".HexToAddress(); - this.owner = "0x0000000000000000000000000000000000000002".HexToAddress(); - this.investor = "0x0000000000000000000000000000000000000003".HexToAddress(); - this.identity = "0x0000000000000000000000000000000000000004".HexToAddress(); - this.contract = "0x0000000000000000000000000000000000000005".HexToAddress(); - this.tokenContract = "0x0000000000000000000000000000000000000006".HexToAddress(); - this.kycContract = "0x0000000000000000000000000000000000000007".HexToAddress(); - this.mapperContract = "0x0000000000000000000000000000000000000008".HexToAddress(); - - this.name = "Test Token"; - this.symbol = "TST"; - this.totalSupply = 100 * Satoshis; - this.decimals = 0; - this.persistentState.IsContractResult = true; + mContractLogger = new Mock(); + mContractState = new Mock(); + mTransactionExecutor = new Mock(); + persistentState = new InMemoryState(); + mBlock = new Mock(); + mContractState.Setup(s => s.Block).Returns(mBlock.Object); + mContractState.Setup(s => s.PersistentState).Returns(persistentState); + mContractState.Setup(s => s.ContractLogger).Returns(mContractLogger.Object); + mContractState.Setup(s => s.InternalTransactionExecutor).Returns(mTransactionExecutor.Object); + mSerializer = new Mock(); + mContractState.Setup(s => s.Serializer).Returns(mSerializer.Object); + sender = "0x0000000000000000000000000000000000000001".HexToAddress(); + owner = "0x0000000000000000000000000000000000000002".HexToAddress(); + investor = "0x0000000000000000000000000000000000000003".HexToAddress(); + identity = "0x0000000000000000000000000000000000000004".HexToAddress(); + currentContract = "0x0000000000000000000000000000000000000005".HexToAddress(); + tokenContract = "0x0000000000000000000000000000000000000006".HexToAddress(); + kycContract = "0x0000000000000000000000000000000000000007".HexToAddress(); + mapperContract = "0x0000000000000000000000000000000000000008".HexToAddress(); + + name = "Test Token"; + symbol = "TST"; + totalSupply = 100 * Satoshis; + decimals = 0; + persistentState.IsContractResult = true; } private ICreateResult createSucceed() @@ -66,7 +66,7 @@ private ICreateResult createSucceed() var mock = new Mock(); mock.SetupGet(m => m.Success).Returns(true); - mock.SetupGet(m => m.NewContractAddress).Returns(this.tokenContract); + mock.SetupGet(m => m.NewContractAddress).Returns(tokenContract); return mock.Object; } @@ -102,9 +102,9 @@ private ITransferResult transferFailed() [Fact] public void Constructor_IsContract_ReturnsFalse_ThrowsAssertException() { - this.persistentState.IsContractResult = false; + persistentState.IsContractResult = false; - Assert.Throws(() => this.Create(TokenType.StandardToken)); + Assert.Throws(() => Create(TokenType.StandardToken)); } [Fact] @@ -112,58 +112,58 @@ public void Constructor_TokenType_HigherThan2_ThrowsAssertException() { var tokenType = (TokenType)3; - Assert.Throws(() => this.Create(tokenType)); + Assert.Throws(() => Create(tokenType)); } [Fact] public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createFailed()); - Assert.Throws(() => this.Create(TokenType.StandardToken)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createFailed()); + Assert.Throws(() => Create(TokenType.StandardToken)); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny()), Times.Once); + mTransactionExecutor.Verify(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny()), Times.Once); } [Fact] public void Constructor_TokenTypeIsStandardToken_Success() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, periods) = this.Create(TokenType.StandardToken); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + var (contract, periods) = Create(TokenType.StandardToken); - Assert.Equal(this.totalSupply, contract.TokenBalance); - Assert.Equal(this.owner, contract.Owner); - Assert.Equal(this.tokenContract, contract.TokenAddress); + Assert.Equal(totalSupply, contract.TokenBalance); + Assert.Equal(owner, contract.Owner); + Assert.Equal(tokenContract, contract.TokenAddress); Assert.Equal(4ul, contract.EndBlock); Assert.Equal(periods, contract.SalePeriods); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, 0), Times.Once); + mTransactionExecutor.Verify(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, 0), Times.Once); } [Fact] public void Constructor_TokenTypeIsDividendToken_Success() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, periods) = this.Create(TokenType.DividendToken); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + var (contract, periods) = Create(TokenType.DividendToken); - Assert.Equal(this.totalSupply, contract.TokenBalance); - Assert.Equal(this.owner, contract.Owner); - Assert.Equal(this.tokenContract, contract.TokenAddress); + Assert.Equal(totalSupply, contract.TokenBalance); + Assert.Equal(owner, contract.Owner); + Assert.Equal(tokenContract, contract.TokenAddress); Assert.Equal(4ul, contract.EndBlock); Assert.Equal(periods, contract.SalePeriods); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, 0), Times.Once); + mTransactionExecutor.Verify(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, 0), Times.Once); } [Fact] public void Constructor_TokenTypeIsNonFungibleToken_Success() { - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(createSucceed()); - var (contract, periods) = this.Create(TokenType.NonFungibleToken); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(createSucceed()); + var (contract, periods) = Create(TokenType.NonFungibleToken); Assert.Equal((UInt256)ulong.MaxValue, contract.TokenBalance); - Assert.Equal(this.owner, contract.Owner); - Assert.Equal(this.tokenContract, contract.TokenAddress); + Assert.Equal(owner, contract.Owner); + Assert.Equal(tokenContract, contract.TokenAddress); Assert.Equal(4ul, contract.EndBlock); Assert.Equal(periods, contract.SalePeriods); - this.mTransactionExecutor.Verify(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, 0), Times.Once); + mTransactionExecutor.Verify(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, 0), Times.Once); } public (STOContract contract, SalePeriod[] periods) Create(TokenType tokenType) @@ -179,10 +179,10 @@ public void Constructor_TokenTypeIsNonFungibleToken_Success() new SalePeriod { PricePerToken = 5 * Satoshis, EndBlock = 4 } }; - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, 0)); - this.mBlock.Setup(s => s.Number).Returns(1); - this.mSerializer.Setup(m => m.ToArray(new byte[0])).Returns(periodInputs); - var contract = new STOContract(this.mContractState.Object, this.owner, (uint)tokenType, this.totalSupply, this.name, this.symbol, this.decimals, this.kycContract, this.mapperContract, new byte[0]); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, 0)); + mBlock.Setup(s => s.Number).Returns(1); + mSerializer.Setup(m => m.ToArray(new byte[0])).Returns(periodInputs); + var contract = new STOContract(mContractState.Object, owner, (uint)tokenType, totalSupply, name, symbol, decimals, kycContract, mapperContract, new byte[0]); return (contract, periods); } @@ -190,28 +190,28 @@ public void Constructor_TokenTypeIsNonFungibleToken_Success() public void Invest_CalledForStandardToken_Success() { var amount = 15 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); + var (contract, _) = Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, (UInt256)5 }, It.IsAny())).Returns(transferSucceed(true)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)5 }, It.IsAny())).Returns(transferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); Assert.True(contract.Invest()); - Assert.Equal(this.totalSupply - 5ul, contract.TokenBalance); - this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + Assert.Equal(totalSupply - 5ul, contract.TokenBalance); + mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); - this.mBlock.Setup(s => s.Number).Returns(4); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, (UInt256)3 }, It.IsAny())).Returns(transferSucceed(true)); + mBlock.Setup(s => s.Number).Returns(4); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)3 }, It.IsAny())).Returns(transferSucceed(true)); Assert.True(contract.Invest()); - Assert.Equal(this.totalSupply - 8ul, contract.TokenBalance); - this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + Assert.Equal(totalSupply - 8ul, contract.TokenBalance); + mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); } [Fact] @@ -220,60 +220,60 @@ public void Invest_CalledForNonFungibleToken_Success() var amount = 15 * Satoshis; var totalSupply = (UInt256)ulong.MaxValue; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.name, this.symbol }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.NonFungibleToken); + var (contract, _) = Create(TokenType.NonFungibleToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 5ul }, It.IsAny())).Returns(transferSucceed(true)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 5ul }, It.IsAny())).Returns(transferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); Assert.True(contract.Invest()); Assert.Equal(totalSupply - 5, contract.TokenBalance); - this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); - this.mBlock.Setup(s => s.Number).Returns(4); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { this.investor, 3ul }, It.IsAny())).Returns(transferSucceed(true)); + mBlock.Setup(s => s.Number).Returns(4); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 3ul }, It.IsAny())).Returns(transferSucceed(true)); Assert.True(contract.Invest()); Assert.Equal(totalSupply - 8ul, contract.TokenBalance); - this.mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); + mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); } [Fact] public void Invest_Refunds_Oversold_Tokens() { - this.totalSupply = 60; + totalSupply = 60; var amount = 190 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); + var (contract, _) = Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.investor, this.totalSupply }, It.IsAny())).Returns(transferSucceed(true)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); - this.mTransactionExecutor.Setup(m => m.Transfer(this.mContractState.Object, this.investor, 10 * Satoshis)).Returns(transferSucceed()); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, totalSupply }, It.IsAny())).Returns(transferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Transfer(mContractState.Object, investor, 10 * Satoshis)).Returns(transferSucceed()); Assert.True(contract.Invest()); Assert.Equal((UInt256)0, contract.TokenBalance); // All tokens are sold - this.mTransactionExecutor.Verify(s => s.Transfer(this.mContractState.Object, this.investor, 10 * Satoshis), Times.Once); + mTransactionExecutor.Verify(s => s.Transfer(mContractState.Object, investor, 10 * Satoshis), Times.Once); } [Fact] public void Invest_Fails_If_TokenBalance_Is_Zero() { var amount = 1 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.persistentState.SetUInt256(nameof(STOContract.TokenBalance), 0); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); + persistentState.SetUInt256(nameof(STOContract.TokenBalance), 0); Assert.Throws(() => contract.Invest()); } @@ -282,12 +282,12 @@ public void Invest_Fails_If_TokenBalance_Is_Zero() public void Invest_Fails_If_EndBlock_Reached() { var amount = 1 * Satoshis; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); + var (contract, _) = Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.mBlock.Setup(s => s.Number).Returns(5); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); + mBlock.Setup(s => s.Number).Returns(5); Assert.Throws(() => contract.Invest()); } @@ -296,10 +296,10 @@ public void Invest_Fails_If_EndBlock_Reached() public void Invest_Fails_If_Investment_Amount_Is_Zero() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); Assert.Throws(() => contract.Invest()); } @@ -308,10 +308,10 @@ public void Invest_Fails_If_Investment_Amount_Is_Zero() public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferFailed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferFailed()); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); Assert.Throws(() => contract.Invest()); } @@ -320,10 +320,10 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(Address.Zero)); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(Address.Zero)); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); Assert.Throws(() => contract.Invest()); } @@ -332,11 +332,11 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() public void Invest_Fails_If_GetClaim_Call_Fails() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferFailed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferFailed()); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); Assert.Throws(() => contract.Invest()); } @@ -345,11 +345,11 @@ public void Invest_Fails_If_GetClaim_Call_Fails() public void Invest_Fails_If_GetClaim_Call_Returns_Null() { var amount = 10ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.mapperContract, 0, "GetSecondaryAddress", new object[] { this.investor }, It.IsAny())).Returns(transferSucceed(this.identity)); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.kycContract, 0, "GetClaim", new object[] { this.identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(null)); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(null)); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); Assert.Throws(() => contract.Invest()); } @@ -358,11 +358,11 @@ public void Invest_Fails_If_GetClaim_Call_Returns_Null() public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.investor, amount)); - this.mBlock.Setup(m => m.Number).Returns(5); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); + mBlock.Setup(m => m.Number).Returns(5); Assert.Throws(() => contract.WithdrawFunds()); } @@ -371,11 +371,11 @@ public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() public void WithdrawFunds_Fails_If_Sale_Is_Open() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); - this.mBlock.Setup(m => m.Number).Returns(4); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); + mBlock.Setup(m => m.Number).Returns(4); Assert.Throws(() => contract.WithdrawFunds()); } @@ -384,11 +384,11 @@ public void WithdrawFunds_Fails_If_Sale_Is_Open() public void WithdrawFunds_Called_By_Owner_Success() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); - this.mBlock.Setup(m => m.Number).Returns(4); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); + mBlock.Setup(m => m.Number).Returns(4); Assert.Throws(() => contract.WithdrawFunds()); } @@ -397,19 +397,19 @@ public void WithdrawFunds_Called_By_Owner_Success() public void WithdrawTokens_Called_By_Owner_After_Sale_Is_Closed_Success() { var amount = 0ul; - this.mTransactionExecutor.Setup(m => m.Create(this.mContractState.Object, 0, new object[] { this.totalSupply, this.name, this.symbol, this.decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - var (contract, _) = this.Create(TokenType.StandardToken); - this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, amount)); - this.mBlock.Setup(m => m.Number).Returns(5); - this.persistentState.SetUInt256(nameof(contract.TokenBalance), 100); - this.mTransactionExecutor.Setup(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, (UInt256)100 }, It.IsAny())).Returns(transferSucceed(true)); + var (contract, _) = Create(TokenType.StandardToken); + mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); + mBlock.Setup(m => m.Number).Returns(5); + persistentState.SetUInt256(nameof(contract.TokenBalance), 100); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, It.IsAny())).Returns(transferSucceed(true)); var success = contract.WithdrawTokens(); Assert.True(success); - Assert.Equal((UInt256)0, this.persistentState.GetUInt256(nameof(contract.TokenBalance))); - this.mTransactionExecutor.Verify(m => m.Call(this.mContractState.Object, this.tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { this.owner, (UInt256)100 }, 0)); + Assert.Equal((UInt256)0, persistentState.GetUInt256(nameof(contract.TokenBalance))); + mTransactionExecutor.Verify(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, 0)); } } } From eef68b1e1fb376bdf5fda5aa1f2ffa16d8f5b747 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 27 Jan 2021 04:09:58 +0300 Subject: [PATCH 05/12] Fix name convention --- .../STOContract.Tests/STOContractTests.cs | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs index 9451167e..abfaaeb0 100644 --- a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs +++ b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs @@ -61,7 +61,7 @@ public STOContractTests() persistentState.IsContractResult = true; } - private ICreateResult createSucceed() + private ICreateResult CreateSucceed() { var mock = new Mock(); @@ -71,7 +71,7 @@ private ICreateResult createSucceed() return mock.Object; } - private ICreateResult createFailed() + private ICreateResult CreateFailed() { var mock = new Mock(); @@ -80,7 +80,7 @@ private ICreateResult createFailed() return mock.Object; } - private ITransferResult transferSucceed(object returnValue = null) + private ITransferResult TransferSucceed(object returnValue = null) { var mock = new Mock(); @@ -90,7 +90,7 @@ private ITransferResult transferSucceed(object returnValue = null) return mock.Object; } - private ITransferResult transferFailed() + private ITransferResult TransferFailed() { var mock = new Mock(); @@ -118,7 +118,7 @@ public void Constructor_TokenType_HigherThan2_ThrowsAssertException() [Fact] public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createFailed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateFailed()); Assert.Throws(() => Create(TokenType.StandardToken)); mTransactionExecutor.Verify(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny()), Times.Once); @@ -127,7 +127,7 @@ public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() [Fact] public void Constructor_TokenTypeIsStandardToken_Success() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, periods) = Create(TokenType.StandardToken); Assert.Equal(totalSupply, contract.TokenBalance); @@ -141,7 +141,7 @@ public void Constructor_TokenTypeIsStandardToken_Success() [Fact] public void Constructor_TokenTypeIsDividendToken_Success() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, periods) = Create(TokenType.DividendToken); Assert.Equal(totalSupply, contract.TokenBalance); @@ -155,7 +155,7 @@ public void Constructor_TokenTypeIsDividendToken_Success() [Fact] public void Constructor_TokenTypeIsNonFungibleToken_Success() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(CreateSucceed()); var (contract, periods) = Create(TokenType.NonFungibleToken); Assert.Equal((UInt256)ulong.MaxValue, contract.TokenBalance); @@ -190,15 +190,15 @@ public void Constructor_TokenTypeIsNonFungibleToken_Success() public void Invest_CalledForStandardToken_Success() { var amount = 15 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)5 }, It.IsAny())).Returns(transferSucceed(true)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)5 }, It.IsAny())).Returns(TransferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); Assert.True(contract.Invest()); @@ -206,7 +206,7 @@ public void Invest_CalledForStandardToken_Success() mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); mBlock.Setup(s => s.Number).Returns(4); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)3 }, It.IsAny())).Returns(transferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)3 }, It.IsAny())).Returns(TransferSucceed(true)); Assert.True(contract.Invest()); @@ -220,15 +220,15 @@ public void Invest_CalledForNonFungibleToken_Success() var amount = 15 * Satoshis; var totalSupply = (UInt256)ulong.MaxValue; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.NonFungibleToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 5ul }, It.IsAny())).Returns(transferSucceed(true)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 5ul }, It.IsAny())).Returns(TransferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); Assert.True(contract.Invest()); @@ -236,7 +236,7 @@ public void Invest_CalledForNonFungibleToken_Success() mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); mBlock.Setup(s => s.Number).Returns(4); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 3ul }, It.IsAny())).Returns(transferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 3ul }, It.IsAny())).Returns(TransferSucceed(true)); Assert.True(contract.Invest()); @@ -249,15 +249,15 @@ public void Invest_Refunds_Oversold_Tokens() { totalSupply = 60; var amount = 190 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, totalSupply }, It.IsAny())).Returns(transferSucceed(true)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); - mTransactionExecutor.Setup(m => m.Transfer(mContractState.Object, investor, 10 * Satoshis)).Returns(transferSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, totalSupply }, It.IsAny())).Returns(TransferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Transfer(mContractState.Object, investor, 10 * Satoshis)).Returns(TransferSucceed()); Assert.True(contract.Invest()); Assert.Equal((UInt256)0, contract.TokenBalance); // All tokens are sold @@ -268,8 +268,8 @@ public void Invest_Refunds_Oversold_Tokens() public void Invest_Fails_If_TokenBalance_Is_Zero() { var amount = 1 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -282,7 +282,7 @@ public void Invest_Fails_If_TokenBalance_Is_Zero() public void Invest_Fails_If_EndBlock_Reached() { var amount = 1 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); @@ -296,7 +296,7 @@ public void Invest_Fails_If_EndBlock_Reached() public void Invest_Fails_If_Investment_Amount_Is_Zero() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -308,8 +308,8 @@ public void Invest_Fails_If_Investment_Amount_Is_Zero() public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferFailed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferFailed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -320,8 +320,8 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(Address.Zero)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(Address.Zero)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -332,9 +332,9 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() public void Invest_Fails_If_GetClaim_Call_Fails() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferFailed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferFailed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -345,9 +345,9 @@ public void Invest_Fails_If_GetClaim_Call_Fails() public void Invest_Fails_If_GetClaim_Call_Returns_Null() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(transferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(transferSucceed(null)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(null)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -358,7 +358,7 @@ public void Invest_Fails_If_GetClaim_Call_Returns_Null() public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -371,7 +371,7 @@ public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() public void WithdrawFunds_Fails_If_Sale_Is_Open() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); @@ -384,7 +384,7 @@ public void WithdrawFunds_Fails_If_Sale_Is_Open() public void WithdrawFunds_Called_By_Owner_Success() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); @@ -397,13 +397,13 @@ public void WithdrawFunds_Called_By_Owner_Success() public void WithdrawTokens_Called_By_Owner_After_Sale_Is_Closed_Success() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(createSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); mBlock.Setup(m => m.Number).Returns(5); persistentState.SetUInt256(nameof(contract.TokenBalance), 100); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, It.IsAny())).Returns(transferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, It.IsAny())).Returns(TransferSucceed(true)); var success = contract.WithdrawTokens(); From 4347f3ea0e7a4717c07b147276ae98343076cfb1 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Thu, 28 Jan 2021 02:54:38 +0300 Subject: [PATCH 06/12] Return early if token balance is already withdrawn --- Mainnet/STOContract/STOContract/STOContract.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index 351dfb42..3c0a5e18 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -161,6 +161,9 @@ public bool WithdrawTokens() Assert(Message.Sender == Owner, "Only contract owner can transfer tokens."); Assert(!SaleOpen, "STO is not ended yet."); + if (TokenBalance == 0) + return true; + var result = Call(TokenAddress, 0, nameof(StandardToken.TransferTo), new object[] { Message.Sender, TokenBalance }); TokenBalance = 0; From 4726bafd9a4b7128154f0bd991903769bad4a802 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Fri, 29 Jan 2021 05:49:22 +0300 Subject: [PATCH 07/12] Updated NFT contract --- .../STOContract/STOContract/STOContract.cs | 113 ++++++++---------- 1 file changed, 53 insertions(+), 60 deletions(-) diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index 3c0a5e18..a6594850 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -759,6 +759,9 @@ public struct ApprovalLog #endregion } +/// +/// A non fungible token contract. +/// public class NonFungibleToken : SmartContract { public struct TransferLog @@ -801,13 +804,13 @@ public struct OwnershipTransferedLog } /// - /// Get a value indicacting if the interface is supported. + /// Function to check which interfaces are supported by this contract. /// - /// The id of the interface to support. - /// A value indicating if the interface is supported. - private bool GetSupportedInterfaces(uint interfaceId) + /// Id of the interface. + /// True if is supported, false otherwise. + public bool SupportsInterface(uint interfaceID) { - return this.PersistentState.GetBool($"SupportedInterface:{interfaceId}"); + return PersistentState.GetBool($"SupportedInterface:{interfaceID}"); } /// @@ -815,7 +818,7 @@ private bool GetSupportedInterfaces(uint interfaceId) /// /// The interface id. /// A value indicating if the interface id is supported. - private void SetSupportedInterfaces(uint interfaceId, bool value) => this.PersistentState.SetBool($"SupportedInterface:{interfaceId}", value); + private void SetSupportedInterfaces(uint interfaceId, bool value) => PersistentState.SetBool($"SupportedInterface:{interfaceId}", value); /// /// Gets the key to the persistent state for the owner by NFT ID. @@ -829,14 +832,14 @@ private bool GetSupportedInterfaces(uint interfaceId) /// /// The ID of the NFT ///The owner address. - private Address GetIdToOwner(ulong id) => this.PersistentState.GetAddress(GetIdToOwnerKey(id)); + private Address GetIdToOwner(ulong id) => PersistentState.GetAddress(GetIdToOwnerKey(id)); /// /// Sets the owner to the NFT ID. /// /// The ID of the NFT /// The address of the owner. - private void SetIdToOwner(ulong id, Address value) => this.PersistentState.SetAddress(GetIdToOwnerKey(id), value); + private void SetIdToOwner(ulong id, Address value) => PersistentState.SetAddress(GetIdToOwnerKey(id), value); /// /// Gets the key to the persistent state for the approval address by NFT ID. @@ -850,28 +853,28 @@ private bool GetSupportedInterfaces(uint interfaceId) /// /// The ID of the NFT /// Address of the approval. - private Address GetIdToApproval(ulong id) => this.PersistentState.GetAddress(GetIdToApprovalKey(id)); + private Address GetIdToApproval(ulong id) => PersistentState.GetAddress(GetIdToApprovalKey(id)); /// /// Setting to NFT ID to approval address. /// /// The ID of the NFT /// The address of the approval. - private void SetIdToApproval(ulong id, Address value) => this.PersistentState.SetAddress(GetIdToApprovalKey(id), value); + private void SetIdToApproval(ulong id, Address value) => PersistentState.SetAddress(GetIdToApprovalKey(id), value); /// /// Gets the amount of non fungible tokens the owner has. /// /// The address of the owner. /// The amount of non fungible tokens. - private ulong GetOwnerToNFTokenCount(Address address) => this.PersistentState.GetUInt64($"OwnerToNFTokenCount:{address}"); + private ulong GetOwnerToNFTokenCount(Address address) => PersistentState.GetUInt64($"OwnerToNFTokenCount:{address}"); /// /// Sets the owner count of this non fungible tokens. /// /// The address of the owner. /// The amount of tokens. - private void SetOwnerToNFTokenCount(Address address, ulong value) => this.PersistentState.SetUInt64($"OwnerToNFTokenCount:{address}", value); + private void SetOwnerToNFTokenCount(Address address, ulong value) => PersistentState.SetUInt64($"OwnerToNFTokenCount:{address}", value); /// /// Gets the permission value of the operator authorization to perform actions on behalf of the owner. @@ -879,7 +882,7 @@ private bool GetSupportedInterfaces(uint interfaceId) /// The owner address of the NFT. /// >Address of the authorized operators /// A value indicating if the operator has permissions to act on behalf of the owner. - private bool GetOwnerToOperator(Address owner, Address operatorAddress) => this.PersistentState.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); + private bool GetOwnerToOperator(Address owner, Address operatorAddress) => PersistentState.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); /// /// Sets the owner to operator permission. @@ -887,15 +890,15 @@ private bool GetSupportedInterfaces(uint interfaceId) /// The owner address of the NFT. /// >Address to add to the set of authorized operators. /// The permission value. - private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => this.PersistentState.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); + private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => PersistentState.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); /// /// Owner of the contract is responsible to for minting/burning /// public Address Owner { - get => this.PersistentState.GetAddress(nameof(Owner)); - private set => this.PersistentState.SetAddress(nameof(Owner), value); + get => PersistentState.GetAddress(nameof(Owner)); + private set => PersistentState.SetAddress(nameof(Owner), value); } /// @@ -903,8 +906,8 @@ public Address Owner /// public string Name { - get => this.PersistentState.GetString(nameof(Name)); - private set => this.PersistentState.SetString(nameof(Name), value); + get => PersistentState.GetString(nameof(Name)); + private set => PersistentState.SetString(nameof(Name), value); } /// @@ -912,8 +915,8 @@ public string Name /// public string Symbol { - get => this.PersistentState.GetString(nameof(Symbol)); - private set => this.PersistentState.SetString(nameof(Symbol), value); + get => PersistentState.GetString(nameof(Symbol)); + private set => PersistentState.SetString(nameof(Symbol), value); } /// @@ -921,42 +924,42 @@ public string Symbol /// private ulong NextTokenId { - get => this.PersistentState.GetUInt64(nameof(NextTokenId)); - set => this.PersistentState.SetUInt64(nameof(NextTokenId), value); + get => PersistentState.GetUInt64(nameof(NextTokenId)); + set => PersistentState.SetUInt64(nameof(NextTokenId), value); } private string GetTokenByIndexKey(ulong index) => $"TokenByIndex:{index}"; - private ulong GetTokenByIndex(ulong index) => this.PersistentState.GetUInt64(GetTokenByIndexKey(index)); + private ulong GetTokenByIndex(ulong index) => PersistentState.GetUInt64(GetTokenByIndexKey(index)); - private void SetTokenByIndex(ulong index, ulong token) => this.PersistentState.SetUInt64(GetTokenByIndexKey(index), token); + private void SetTokenByIndex(ulong index, ulong token) => PersistentState.SetUInt64(GetTokenByIndexKey(index), token); - private void ClearTokenByIndex(ulong index) => this.PersistentState.Clear(GetTokenByIndexKey(index)); + private void ClearTokenByIndex(ulong index) => PersistentState.Clear(GetTokenByIndexKey(index)); private string GetIndexByTokenKey(ulong token) => $"IndexByToken:{token}"; - private ulong GetIndexByToken(ulong token) => this.PersistentState.GetUInt64(GetIndexByTokenKey(token)); + private ulong GetIndexByToken(ulong token) => PersistentState.GetUInt64(GetIndexByTokenKey(token)); - private void SetIndexByToken(ulong token, ulong index) => this.PersistentState.SetUInt64(GetIndexByTokenKey(token), index); + private void SetIndexByToken(ulong token, ulong index) => PersistentState.SetUInt64(GetIndexByTokenKey(token), index); - private void ClearIndexByToken(ulong token) => this.PersistentState.Clear(GetIndexByTokenKey(token)); + private void ClearIndexByToken(ulong token) => PersistentState.Clear(GetIndexByTokenKey(token)); private string GetTokenOfOwnerByIndexKey(Address address, ulong index) => $"TokenOfOwnerByIndex:{address}:{index}"; - private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => this.PersistentState.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); + private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => PersistentState.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); - private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => this.PersistentState.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); + private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => PersistentState.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); - private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => this.PersistentState.Clear(GetTokenOfOwnerByIndexKey(owner, index)); + private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => PersistentState.Clear(GetTokenOfOwnerByIndexKey(owner, index)); private string IndexOfOwnerByTokenKey(Address owner, ulong tokenId) => $"IndexOfOwnerByToken:{owner}:{tokenId}"; - private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => this.PersistentState.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); - private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => this.PersistentState.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); - private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => this.PersistentState.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); + private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => PersistentState.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); + private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => PersistentState.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); + private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => PersistentState.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); public ulong TotalSupply { - get => this.PersistentState.GetUInt64(nameof(TotalSupply)); - private set => this.PersistentState.SetUInt64(nameof(TotalSupply), value); + get => PersistentState.GetUInt64(nameof(TotalSupply)); + private set => PersistentState.SetUInt64(nameof(TotalSupply), value); } /// @@ -992,15 +995,7 @@ public ulong TokenOfOwnerByIndex(Address owner, ulong index) return GetTokenOfOwnerByIndex(owner, index); } - /// - /// Function to check which interfaces are supported by this contract. - /// - /// Id of the interface. - /// True if is supported, false otherwise. - public bool SupportsInterface(uint interfaceID) - { - return GetSupportedInterfaces(interfaceID); - } + /// /// Transfers the ownership of an NFT from one address to another address. This function can @@ -1086,8 +1081,8 @@ public void Approve(Address approved, ulong tokenId) /// True if the operators is approved, false to revoke approval. public void SetApprovalForAll(Address operatorAddress, bool approved) { - SetOwnerToOperator(this.Message.Sender, operatorAddress, approved); - LogApprovalForAll(this.Message.Sender, operatorAddress, approved); + SetOwnerToOperator(Message.Sender, operatorAddress, approved); + LogApprovalForAll(Message.Sender, operatorAddress, approved); } /// @@ -1166,7 +1161,7 @@ private void RemoveNFToken(Address from, ulong tokenId) Assert(GetIdToOwner(tokenId) == from); var tokenCount = GetOwnerToNFTokenCount(from); SetOwnerToNFTokenCount(from, checked(tokenCount - 1)); - this.PersistentState.Clear(GetIdToOwnerKey(tokenId)); + PersistentState.Clear(GetIdToOwnerKey(tokenId)); ulong index = GetIndexOfOwnerByToken(from, tokenId); ulong lastIndex = tokenCount - 1; @@ -1219,9 +1214,9 @@ private void SafeTransferFromInternal(Address from, Address to, ulong tokenId, b TransferInternal(to, tokenId); - if (this.PersistentState.IsContract(to)) + if (PersistentState.IsContract(to)) { - ITransferResult result = this.Call(to, 0, "OnNonFungibleTokenReceived", new object[] { this.Message.Sender, from, tokenId, data }, 0); + ITransferResult result = Call(to, 0, "OnNonFungibleTokenReceived", new object[] { Message.Sender, from, tokenId, data }, 0); Assert((bool)result.ReturnValue); } } @@ -1234,7 +1229,7 @@ private void ClearApproval(ulong tokenId) { if (GetIdToApproval(tokenId) != Address.Zero) { - this.PersistentState.Clear(GetIdToApprovalKey(tokenId)); + PersistentState.Clear(GetIdToApprovalKey(tokenId)); } } @@ -1284,7 +1279,7 @@ private void LogApprovalForAll(Address owner, Address operatorAddress, bool appr private void CanOperate(ulong tokenId) { Address tokenOwner = GetIdToOwner(tokenId); - Assert(tokenOwner == this.Message.Sender || GetOwnerToOperator(tokenOwner, this.Message.Sender)); + Assert(tokenOwner == Message.Sender || GetOwnerToOperator(tokenOwner, Message.Sender)); } /// @@ -1295,7 +1290,7 @@ private void CanTransfer(ulong tokenId) { Address tokenOwner = GetIdToOwner(tokenId); Assert( - tokenOwner == this.Message.Sender + tokenOwner == Message.Sender || GetIdToApproval(tokenId) == Message.Sender || GetOwnerToOperator(tokenOwner, Message.Sender) ); @@ -1318,11 +1313,11 @@ private void ValidNFToken(ulong tokenId) public void TransferOwnership(Address owner) { EnsureOwnerOnly(); - Assert(owner != Address.Zero, $"The {nameof(owner)} parameter can not be default(zero) address."); + EnsureAddressIsNotEmpty(owner); - Log(new OwnershipTransferedLog { PreviousOwner = this.Owner, NewOwner = owner }); + Log(new OwnershipTransferedLog { PreviousOwner = Owner, NewOwner = owner }); - this.Owner = owner; + Owner = owner; } private void EnsureOwnerOnly() @@ -1351,7 +1346,7 @@ public void MintAll(Address address, ulong amount) SetTokenByIndex(index, tokenId); SetIndexByToken(tokenId, index); - Log(new TransferLog { From = Address.Zero, To = address, TokenId = tokenId }); + LogTransfer(Address.Zero, address, tokenId); checked { @@ -1368,8 +1363,6 @@ public void Burn(ulong tokenId) { Address tokenOwner = GetIdToOwner(tokenId); - EnsureAddressIsNotEmpty(tokenOwner); - Assert(tokenOwner == Message.Sender, "Only token owner can burn the token."); ClearApproval(tokenId); @@ -1386,7 +1379,7 @@ public void Burn(ulong tokenId) ClearTokenByIndex(lastTokenIndex); ClearIndexByToken(tokenId); - Log(new TransferLog { From = tokenOwner, To = Address.Zero, TokenId = tokenId }); + LogTransfer(tokenOwner, Address.Zero, tokenId); } public void EnsureAddressIsNotEmpty(Address address) From a4822ca8b641540424ba56c14aa374f96dda940d Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 3 Feb 2021 07:25:59 +0300 Subject: [PATCH 08/12] package updates --- .../STOContract.Tests/CreateResult.cs | 16 +++ .../STOContract.Tests/InMemoryState.cs | 1 - .../STOContract.Tests/STOContractTests.cs | 114 ++++++------------ .../STOContract.Tests/TransferResult.cs | 16 +++ .../STOContract/STOContract/STOContract.cs | 36 +++--- .../STOContract/STOContract.csproj | 4 +- 6 files changed, 90 insertions(+), 97 deletions(-) create mode 100644 Mainnet/STOContract/STOContract.Tests/CreateResult.cs create mode 100644 Mainnet/STOContract/STOContract.Tests/TransferResult.cs diff --git a/Mainnet/STOContract/STOContract.Tests/CreateResult.cs b/Mainnet/STOContract/STOContract.Tests/CreateResult.cs new file mode 100644 index 00000000..850e2fb8 --- /dev/null +++ b/Mainnet/STOContract/STOContract.Tests/CreateResult.cs @@ -0,0 +1,16 @@ +using Stratis.SmartContracts; + +namespace STOContractTests +{ + public class CreateResult : ICreateResult + { + public Address NewContractAddress { get; private set; } + + public bool Success { get; private set; } + + public static ICreateResult Failed() => new CreateResult { Success = false }; + + + public static ICreateResult Succeed(Address address) => new CreateResult { Success = true, NewContractAddress = address }; + } +} diff --git a/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs b/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs index acf89a6e..2e230334 100644 --- a/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs +++ b/Mainnet/STOContract/STOContract.Tests/InMemoryState.cs @@ -1,7 +1,6 @@ using Stratis.SmartContracts; using System; using System.Collections.Generic; -using static STOContract; namespace STOContractTests { diff --git a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs index abfaaeb0..7ae5ac81 100644 --- a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs +++ b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs @@ -61,44 +61,6 @@ public STOContractTests() persistentState.IsContractResult = true; } - private ICreateResult CreateSucceed() - { - var mock = new Mock(); - - mock.SetupGet(m => m.Success).Returns(true); - mock.SetupGet(m => m.NewContractAddress).Returns(tokenContract); - - return mock.Object; - } - - private ICreateResult CreateFailed() - { - var mock = new Mock(); - - mock.SetupGet(m => m.Success).Returns(false); - - return mock.Object; - } - - private ITransferResult TransferSucceed(object returnValue = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.Success).Returns(true); - mock.SetupGet(m => m.ReturnValue).Returns(returnValue); - - return mock.Object; - } - - private ITransferResult TransferFailed() - { - var mock = new Mock(); - - mock.SetupGet(m => m.Success).Returns(false); - - return mock.Object; - } - [Fact] public void Constructor_IsContract_ReturnsFalse_ThrowsAssertException() { @@ -118,7 +80,7 @@ public void Constructor_TokenType_HigherThan2_ThrowsAssertException() [Fact] public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateFailed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Failed()); Assert.Throws(() => Create(TokenType.StandardToken)); mTransactionExecutor.Verify(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny()), Times.Once); @@ -127,7 +89,7 @@ public void Constructor_CreateReturnsFailedResult_ThrowsAssertException() [Fact] public void Constructor_TokenTypeIsStandardToken_Success() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, periods) = Create(TokenType.StandardToken); Assert.Equal(totalSupply, contract.TokenBalance); @@ -141,7 +103,7 @@ public void Constructor_TokenTypeIsStandardToken_Success() [Fact] public void Constructor_TokenTypeIsDividendToken_Success() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, periods) = Create(TokenType.DividendToken); Assert.Equal(totalSupply, contract.TokenBalance); @@ -155,7 +117,7 @@ public void Constructor_TokenTypeIsDividendToken_Success() [Fact] public void Constructor_TokenTypeIsNonFungibleToken_Success() { - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, periods) = Create(TokenType.NonFungibleToken); Assert.Equal((UInt256)ulong.MaxValue, contract.TokenBalance); @@ -190,15 +152,15 @@ public void Constructor_TokenTypeIsNonFungibleToken_Success() public void Invest_CalledForStandardToken_Success() { var amount = 15 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)5 }, It.IsAny())).Returns(TransferSucceed(true)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)5 }, It.IsAny())).Returns(TransferResult.Transferred(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Transferred(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(new byte[] { 1 })); Assert.True(contract.Invest()); @@ -206,7 +168,7 @@ public void Invest_CalledForStandardToken_Success() mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); mBlock.Setup(s => s.Number).Returns(4); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)3 }, It.IsAny())).Returns(TransferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, (UInt256)3 }, It.IsAny())).Returns(TransferResult.Transferred(true)); Assert.True(contract.Invest()); @@ -220,15 +182,15 @@ public void Invest_CalledForNonFungibleToken_Success() var amount = 15 * Satoshis; var totalSupply = (UInt256)ulong.MaxValue; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { name, symbol }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.NonFungibleToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 5ul }, It.IsAny())).Returns(TransferSucceed(true)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 5ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Transferred(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(new byte[] { 1 })); Assert.True(contract.Invest()); @@ -236,7 +198,7 @@ public void Invest_CalledForNonFungibleToken_Success() mTransactionExecutor.Verify(s => s.Transfer(It.IsAny(), It.IsAny
(), It.IsAny()), Times.Never); mBlock.Setup(s => s.Number).Returns(4); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 3ul }, It.IsAny())).Returns(TransferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(NonFungibleToken.MintAll), new object[] { investor, 3ul }, It.IsAny())).Returns(TransferResult.Transferred(true)); Assert.True(contract.Invest()); @@ -249,15 +211,15 @@ public void Invest_Refunds_Oversold_Tokens() { totalSupply = 60; var amount = 190 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, totalSupply }, It.IsAny())).Returns(TransferSucceed(true)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); - mTransactionExecutor.Setup(m => m.Transfer(mContractState.Object, investor, 10 * Satoshis)).Returns(TransferSucceed()); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { investor, totalSupply }, It.IsAny())).Returns(TransferResult.Transferred(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Transferred(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Transfer(mContractState.Object, investor, 10 * Satoshis)).Returns(TransferResult.Transferred(null)); Assert.True(contract.Invest()); Assert.Equal((UInt256)0, contract.TokenBalance); // All tokens are sold @@ -268,8 +230,8 @@ public void Invest_Refunds_Oversold_Tokens() public void Invest_Fails_If_TokenBalance_Is_Zero() { var amount = 1 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(new byte[] { 1 })); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { investor, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(new byte[] { 1 })); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -282,7 +244,7 @@ public void Invest_Fails_If_TokenBalance_Is_Zero() public void Invest_Fails_If_EndBlock_Reached() { var amount = 1 * Satoshis; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); @@ -296,7 +258,7 @@ public void Invest_Fails_If_EndBlock_Reached() public void Invest_Fails_If_Investment_Amount_Is_Zero() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -308,8 +270,8 @@ public void Invest_Fails_If_Investment_Amount_Is_Zero() public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferFailed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Failed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -320,8 +282,8 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Fails() public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(Address.Zero)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Transferred(Address.Zero)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -332,9 +294,9 @@ public void Invest_Fails_If_GetSecondaryAddress_Call_Returns_Zero_Address() public void Invest_Fails_If_GetClaim_Call_Fails() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferFailed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Transferred(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Failed()); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -345,9 +307,9 @@ public void Invest_Fails_If_GetClaim_Call_Fails() public void Invest_Fails_If_GetClaim_Call_Returns_Null() { var amount = 10ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferSucceed(identity)); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferSucceed(null)); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, mapperContract, 0, "GetSecondaryAddress", new object[] { investor }, It.IsAny())).Returns(TransferResult.Transferred(identity)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, kycContract, 0, "GetClaim", new object[] { identity, 3U /*shufti kyc*/ }, It.IsAny())).Returns(TransferResult.Transferred(null)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -358,7 +320,7 @@ public void Invest_Fails_If_GetClaim_Call_Returns_Null() public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); @@ -371,7 +333,7 @@ public void WithdrawFunds_Fails_If_Caller_Is_Not_Owner() public void WithdrawFunds_Fails_If_Sale_Is_Open() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); @@ -384,7 +346,7 @@ public void WithdrawFunds_Fails_If_Sale_Is_Open() public void WithdrawFunds_Called_By_Owner_Success() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); @@ -397,13 +359,13 @@ public void WithdrawFunds_Called_By_Owner_Success() public void WithdrawTokens_Called_By_Owner_After_Sale_Is_Closed_Success() { var amount = 0ul; - mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateSucceed()); + mTransactionExecutor.Setup(m => m.Create(mContractState.Object, 0, new object[] { totalSupply, name, symbol, decimals }, It.IsAny())).Returns(CreateResult.Succeed(tokenContract)); var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); mBlock.Setup(m => m.Number).Returns(5); persistentState.SetUInt256(nameof(contract.TokenBalance), 100); - mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, It.IsAny())).Returns(TransferSucceed(true)); + mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, It.IsAny())).Returns(TransferResult.Transferred(true)); var success = contract.WithdrawTokens(); diff --git a/Mainnet/STOContract/STOContract.Tests/TransferResult.cs b/Mainnet/STOContract/STOContract.Tests/TransferResult.cs new file mode 100644 index 00000000..1817264b --- /dev/null +++ b/Mainnet/STOContract/STOContract.Tests/TransferResult.cs @@ -0,0 +1,16 @@ +using Stratis.SmartContracts; + +namespace STOContractTests +{ + public class TransferResult : ITransferResult + { + public object ReturnValue { get; private set; } + + public bool Success { get; private set; } + + public static ITransferResult Failed() => new TransferResult { Success = false }; + + + public static ITransferResult Transferred(object returnValue) => new TransferResult { Success = true, ReturnValue = returnValue }; + } +} diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index a6594850..8d3b0ae2 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -6,52 +6,52 @@ public class STOContract : SmartContract { public ulong EndBlock { - get => PersistentState.GetUInt64(nameof(EndBlock)); - private set => PersistentState.SetUInt64(nameof(EndBlock), value); + get => State.GetUInt64(nameof(EndBlock)); + private set => State.SetUInt64(nameof(EndBlock), value); } public Address TokenAddress { - get => PersistentState.GetAddress(nameof(TokenAddress)); - private set => PersistentState.SetAddress(nameof(TokenAddress), value); + get => State.GetAddress(nameof(TokenAddress)); + private set => State.SetAddress(nameof(TokenAddress), value); } public Address KYCAddress { - get => PersistentState.GetAddress(nameof(KYCAddress)); - private set => PersistentState.SetAddress(nameof(KYCAddress), value); + get => State.GetAddress(nameof(KYCAddress)); + private set => State.SetAddress(nameof(KYCAddress), value); } public Address MapperAddress { - get => PersistentState.GetAddress(nameof(MapperAddress)); - private set => PersistentState.SetAddress(nameof(MapperAddress), value); + get => State.GetAddress(nameof(MapperAddress)); + private set => State.SetAddress(nameof(MapperAddress), value); } public UInt256 TokenBalance { - get => PersistentState.GetUInt256(nameof(TokenBalance)); - private set => PersistentState.SetUInt256(nameof(TokenBalance), value); + get => State.GetUInt256(nameof(TokenBalance)); + private set => State.SetUInt256(nameof(TokenBalance), value); } public bool IsNonFungibleToken { - get => PersistentState.GetBool(nameof(IsNonFungibleToken)); - private set => PersistentState.SetBool(nameof(IsNonFungibleToken), value); + get => State.GetBool(nameof(IsNonFungibleToken)); + private set => State.SetBool(nameof(IsNonFungibleToken), value); } public bool SaleOpen => EndBlock >= Block.Number && TokenBalance > 0; public Address Owner { - get => PersistentState.GetAddress(nameof(Owner)); - private set => PersistentState.SetAddress(nameof(Owner), value); + get => State.GetAddress(nameof(Owner)); + private set => State.SetAddress(nameof(Owner), value); } public SalePeriod[] SalePeriods { - get => PersistentState.GetArray(nameof(SalePeriods)); - private set => PersistentState.SetArray(nameof(SalePeriods), value); + get => State.GetArray(nameof(SalePeriods)); + private set => State.SetArray(nameof(SalePeriods), value); } public STOContract(ISmartContractState smartContractState, @@ -67,8 +67,8 @@ public STOContract(ISmartContractState smartContractState, { Assert(tokenType < 3, $"The {nameof(tokenType)} parameter can be between 0 and 2."); - Assert(PersistentState.IsContract(kycAddress), $"The {nameof(kycAddress)} is not a contract adress."); - Assert(PersistentState.IsContract(mapperAddress), $"The {nameof(mapperAddress)} is not a contract adress."); + Assert(State.IsContract(kycAddress), $"The {nameof(kycAddress)} is not a contract adress."); + Assert(State.IsContract(mapperAddress), $"The {nameof(mapperAddress)} is not a contract adress."); var periods = Serializer.ToArray(salePeriods); diff --git a/Mainnet/STOContract/STOContract/STOContract.csproj b/Mainnet/STOContract/STOContract/STOContract.csproj index 86174c2e..235402b0 100644 --- a/Mainnet/STOContract/STOContract/STOContract.csproj +++ b/Mainnet/STOContract/STOContract/STOContract.csproj @@ -6,8 +6,8 @@ 8.0 - - + + From 5abb811012cbe90a9ac6fe27cd6f282a500e072c Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 3 Feb 2021 07:52:40 +0300 Subject: [PATCH 09/12] Referenced contracts are updated --- .../STOContract.Tests/STOContractTests.cs | 16 +- .../STOContract/STOContract/STOContract.cs | 147 +++++++++--------- 2 files changed, 82 insertions(+), 81 deletions(-) diff --git a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs index 7ae5ac81..4bb16d8c 100644 --- a/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs +++ b/Mainnet/STOContract/STOContract.Tests/STOContractTests.cs @@ -26,7 +26,7 @@ public class STOContractTests private readonly Address kycContract; private readonly Address mapperContract; - private readonly InMemoryState persistentState; + private readonly InMemoryState state; private UInt256 totalSupply; private readonly string name; private readonly string symbol; @@ -37,10 +37,10 @@ public STOContractTests() mContractLogger = new Mock(); mContractState = new Mock(); mTransactionExecutor = new Mock(); - persistentState = new InMemoryState(); + state = new InMemoryState(); mBlock = new Mock(); mContractState.Setup(s => s.Block).Returns(mBlock.Object); - mContractState.Setup(s => s.PersistentState).Returns(persistentState); + mContractState.Setup(s => s.PersistentState).Returns(state); mContractState.Setup(s => s.ContractLogger).Returns(mContractLogger.Object); mContractState.Setup(s => s.InternalTransactionExecutor).Returns(mTransactionExecutor.Object); mSerializer = new Mock(); @@ -58,13 +58,13 @@ public STOContractTests() symbol = "TST"; totalSupply = 100 * Satoshis; decimals = 0; - persistentState.IsContractResult = true; + state.IsContractResult = true; } [Fact] public void Constructor_IsContract_ReturnsFalse_ThrowsAssertException() { - persistentState.IsContractResult = false; + state.IsContractResult = false; Assert.Throws(() => Create(TokenType.StandardToken)); } @@ -235,7 +235,7 @@ public void Invest_Fails_If_TokenBalance_Is_Zero() var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, investor, amount)); - persistentState.SetUInt256(nameof(STOContract.TokenBalance), 0); + state.SetUInt256(nameof(STOContract.TokenBalance), 0); Assert.Throws(() => contract.Invest()); } @@ -364,13 +364,13 @@ public void WithdrawTokens_Called_By_Owner_After_Sale_Is_Closed_Success() var (contract, _) = Create(TokenType.StandardToken); mContractState.Setup(m => m.Message).Returns(new Message(currentContract, owner, amount)); mBlock.Setup(m => m.Number).Returns(5); - persistentState.SetUInt256(nameof(contract.TokenBalance), 100); + state.SetUInt256(nameof(contract.TokenBalance), 100); mTransactionExecutor.Setup(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, It.IsAny())).Returns(TransferResult.Transferred(true)); var success = contract.WithdrawTokens(); Assert.True(success); - Assert.Equal((UInt256)0, persistentState.GetUInt256(nameof(contract.TokenBalance))); + Assert.Equal((UInt256)0, state.GetUInt256(nameof(contract.TokenBalance))); mTransactionExecutor.Verify(m => m.Call(mContractState.Object, tokenContract, 0, nameof(StandardToken.TransferTo), new object[] { owner, (UInt256)100 }, 0)); } } diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index 8d3b0ae2..542395ee 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -296,7 +296,7 @@ public enum TokenType : uint /// /// Implementation of a standard token contract for the Stratis Platform. /// -public class StandardToken : SmartContract, IStandardToken +public class StandardToken : SmartContract, IStandardToken256 { /// /// Constructor used to create a new instance of the token. Assigns the total token supply to the creator of the contract. @@ -305,7 +305,7 @@ public class StandardToken : SmartContract, IStandardToken /// The total token supply. /// The name of the token. /// The symbol used to identify the token. - public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply, string name, string symbol, uint decimals) + public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply, string name, string symbol, byte decimals) : base(smartContractState) { this.TotalSupply = totalSupply; @@ -317,39 +317,39 @@ public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply public string Symbol { - get => PersistentState.GetString(nameof(this.Symbol)); - private set => PersistentState.SetString(nameof(this.Symbol), value); + get => State.GetString(nameof(this.Symbol)); + private set => State.SetString(nameof(this.Symbol), value); } public string Name { - get => PersistentState.GetString(nameof(this.Name)); - private set => PersistentState.SetString(nameof(this.Name), value); + get => State.GetString(nameof(this.Name)); + private set => State.SetString(nameof(this.Name), value); } /// - public uint Decimals + public byte Decimals { - get => PersistentState.GetUInt32(nameof(this.Decimals)); - private set => PersistentState.SetUInt32(nameof(this.Decimals), value); + get => State.GetBytes(nameof(this.Decimals))[0]; + private set => State.SetBytes(nameof(this.Decimals), new[] { value }); } /// public UInt256 TotalSupply { - get => PersistentState.GetUInt256(nameof(this.TotalSupply)); - private set => PersistentState.SetUInt256(nameof(this.TotalSupply), value); + get => State.GetUInt256(nameof(this.TotalSupply)); + private set => State.SetUInt256(nameof(this.TotalSupply), value); } /// public UInt256 GetBalance(Address address) { - return PersistentState.GetUInt256($"Balance:{address}"); + return State.GetUInt256($"Balance:{address}"); } private void SetBalance(Address address, UInt256 value) { - PersistentState.SetUInt256($"Balance:{address}", value); + State.SetUInt256($"Balance:{address}", value); } /// @@ -424,13 +424,13 @@ public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) private void SetApproval(Address owner, Address spender, UInt256 value) { - PersistentState.SetUInt256($"Allowance:{owner}:{spender}", value); + State.SetUInt256($"Allowance:{owner}:{spender}", value); } /// public UInt256 Allowance(Address owner, Address spender) { - return PersistentState.GetUInt256($"Allowance:{owner}:{spender}"); + return State.GetUInt256($"Allowance:{owner}:{spender}"); } public struct TransferLog @@ -458,21 +458,20 @@ public struct ApprovalLog } } -public class DividendToken : SmartContract, IStandardToken +public class DividendToken : SmartContract, IStandardToken256 { - public ulong Dividends { - get => PersistentState.GetUInt64(nameof(this.Dividends)); - private set => PersistentState.SetUInt64(nameof(this.Dividends), value); + get => State.GetUInt64(nameof(this.Dividends)); + private set => State.SetUInt64(nameof(this.Dividends), value); } - private Account GetAccount(Address address) => PersistentState.GetStruct($"Account:{address}"); + private Account GetAccount(Address address) => State.GetStruct($"Account:{address}"); - private void SetAccount(Address address, Account account) => PersistentState.SetStruct($"Account:{address}", account); + private void SetAccount(Address address, Account account) => State.SetStruct($"Account:{address}", account); - public DividendToken(ISmartContractState state, UInt256 totalSupply, string name, string symbol, uint decimals) + public DividendToken(ISmartContractState state, UInt256 totalSupply, string name, string symbol, byte decimals) : base(state) { this.TotalSupply = totalSupply; @@ -554,7 +553,9 @@ public ulong GetDividends(Address address) { var account = GetAccount(address); - return GetWithdrawableDividends(address, account) / TotalSupply; + var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; + + return (ulong)withdrawable; } /// @@ -572,7 +573,7 @@ public ulong GetTotalDividends(Address address) { var account = GetAccount(address); var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; - return withdrawable + account.WithdrawnDividends; + return (ulong)withdrawable + account.WithdrawnDividends; } /// @@ -581,7 +582,7 @@ public ulong GetTotalDividends(Address address) public void Withdraw() { var account = UpdateAccount(Message.Sender); - var balance = account.DividendBalance / TotalSupply; + var balance = (ulong)(account.DividendBalance / TotalSupply); Assert(balance > 0, "The account has no dividends."); @@ -617,39 +618,39 @@ public struct Account public string Symbol { - get => PersistentState.GetString(nameof(this.Symbol)); - private set => PersistentState.SetString(nameof(this.Symbol), value); + get => State.GetString(nameof(this.Symbol)); + private set => State.SetString(nameof(this.Symbol), value); } public string Name { - get => PersistentState.GetString(nameof(this.Name)); - private set => PersistentState.SetString(nameof(this.Name), value); + get => State.GetString(nameof(this.Name)); + private set => State.SetString(nameof(this.Name), value); } /// public UInt256 TotalSupply { - get => PersistentState.GetUInt256(nameof(this.TotalSupply)); - private set => PersistentState.SetUInt256(nameof(this.TotalSupply), value); + get => State.GetUInt256(nameof(this.TotalSupply)); + private set => State.SetUInt256(nameof(this.TotalSupply), value); } - public uint Decimals + public byte Decimals { - get => PersistentState.GetUInt32(nameof(Decimals)); - private set => PersistentState.SetUInt32(nameof(Decimals), value); + get => State.GetBytes(nameof(Decimals))[0]; + private set => State.SetBytes(nameof(Decimals), new[] { value }); } /// public UInt256 GetBalance(Address address) { - return PersistentState.GetUInt256($"Balance:{address}"); + return State.GetUInt256($"Balance:{address}"); } private void SetBalance(Address address, UInt256 value) { - PersistentState.SetUInt256($"Balance:{address}", value); + State.SetUInt256($"Balance:{address}", value); } /// @@ -724,13 +725,13 @@ public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) private void SetApproval(Address owner, Address spender, UInt256 value) { - PersistentState.SetUInt256($"Allowance:{owner}:{spender}", value); + State.SetUInt256($"Allowance:{owner}:{spender}", value); } /// public UInt256 Allowance(Address owner, Address spender) { - return PersistentState.GetUInt256($"Allowance:{owner}:{spender}"); + return State.GetUInt256($"Allowance:{owner}:{spender}"); } public struct TransferLog @@ -810,7 +811,7 @@ public struct OwnershipTransferedLog /// True if is supported, false otherwise. public bool SupportsInterface(uint interfaceID) { - return PersistentState.GetBool($"SupportedInterface:{interfaceID}"); + return State.GetBool($"SupportedInterface:{interfaceID}"); } /// @@ -818,7 +819,7 @@ public bool SupportsInterface(uint interfaceID) /// /// The interface id. /// A value indicating if the interface id is supported. - private void SetSupportedInterfaces(uint interfaceId, bool value) => PersistentState.SetBool($"SupportedInterface:{interfaceId}", value); + private void SetSupportedInterfaces(uint interfaceId, bool value) => State.SetBool($"SupportedInterface:{interfaceId}", value); /// /// Gets the key to the persistent state for the owner by NFT ID. @@ -832,14 +833,14 @@ public bool SupportsInterface(uint interfaceID) /// /// The ID of the NFT ///The owner address. - private Address GetIdToOwner(ulong id) => PersistentState.GetAddress(GetIdToOwnerKey(id)); + private Address GetIdToOwner(ulong id) => State.GetAddress(GetIdToOwnerKey(id)); /// /// Sets the owner to the NFT ID. /// /// The ID of the NFT /// The address of the owner. - private void SetIdToOwner(ulong id, Address value) => PersistentState.SetAddress(GetIdToOwnerKey(id), value); + private void SetIdToOwner(ulong id, Address value) => State.SetAddress(GetIdToOwnerKey(id), value); /// /// Gets the key to the persistent state for the approval address by NFT ID. @@ -853,28 +854,28 @@ public bool SupportsInterface(uint interfaceID) /// /// The ID of the NFT /// Address of the approval. - private Address GetIdToApproval(ulong id) => PersistentState.GetAddress(GetIdToApprovalKey(id)); + private Address GetIdToApproval(ulong id) => State.GetAddress(GetIdToApprovalKey(id)); /// /// Setting to NFT ID to approval address. /// /// The ID of the NFT /// The address of the approval. - private void SetIdToApproval(ulong id, Address value) => PersistentState.SetAddress(GetIdToApprovalKey(id), value); + private void SetIdToApproval(ulong id, Address value) => State.SetAddress(GetIdToApprovalKey(id), value); /// /// Gets the amount of non fungible tokens the owner has. /// /// The address of the owner. /// The amount of non fungible tokens. - private ulong GetOwnerToNFTokenCount(Address address) => PersistentState.GetUInt64($"OwnerToNFTokenCount:{address}"); + private ulong GetOwnerToNFTokenCount(Address address) => State.GetUInt64($"OwnerToNFTokenCount:{address}"); /// /// Sets the owner count of this non fungible tokens. /// /// The address of the owner. /// The amount of tokens. - private void SetOwnerToNFTokenCount(Address address, ulong value) => PersistentState.SetUInt64($"OwnerToNFTokenCount:{address}", value); + private void SetOwnerToNFTokenCount(Address address, ulong value) => State.SetUInt64($"OwnerToNFTokenCount:{address}", value); /// /// Gets the permission value of the operator authorization to perform actions on behalf of the owner. @@ -882,7 +883,7 @@ public bool SupportsInterface(uint interfaceID) /// The owner address of the NFT. /// >Address of the authorized operators /// A value indicating if the operator has permissions to act on behalf of the owner. - private bool GetOwnerToOperator(Address owner, Address operatorAddress) => PersistentState.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); + private bool GetOwnerToOperator(Address owner, Address operatorAddress) => State.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); /// /// Sets the owner to operator permission. @@ -890,15 +891,15 @@ public bool SupportsInterface(uint interfaceID) /// The owner address of the NFT. /// >Address to add to the set of authorized operators. /// The permission value. - private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => PersistentState.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); + private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => State.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); /// /// Owner of the contract is responsible to for minting/burning /// public Address Owner { - get => PersistentState.GetAddress(nameof(Owner)); - private set => PersistentState.SetAddress(nameof(Owner), value); + get => State.GetAddress(nameof(Owner)); + private set => State.SetAddress(nameof(Owner), value); } /// @@ -906,8 +907,8 @@ public Address Owner /// public string Name { - get => PersistentState.GetString(nameof(Name)); - private set => PersistentState.SetString(nameof(Name), value); + get => State.GetString(nameof(Name)); + private set => State.SetString(nameof(Name), value); } /// @@ -915,8 +916,8 @@ public string Name /// public string Symbol { - get => PersistentState.GetString(nameof(Symbol)); - private set => PersistentState.SetString(nameof(Symbol), value); + get => State.GetString(nameof(Symbol)); + private set => State.SetString(nameof(Symbol), value); } /// @@ -924,42 +925,42 @@ public string Symbol /// private ulong NextTokenId { - get => PersistentState.GetUInt64(nameof(NextTokenId)); - set => PersistentState.SetUInt64(nameof(NextTokenId), value); + get => State.GetUInt64(nameof(NextTokenId)); + set => State.SetUInt64(nameof(NextTokenId), value); } private string GetTokenByIndexKey(ulong index) => $"TokenByIndex:{index}"; - private ulong GetTokenByIndex(ulong index) => PersistentState.GetUInt64(GetTokenByIndexKey(index)); + private ulong GetTokenByIndex(ulong index) => State.GetUInt64(GetTokenByIndexKey(index)); - private void SetTokenByIndex(ulong index, ulong token) => PersistentState.SetUInt64(GetTokenByIndexKey(index), token); + private void SetTokenByIndex(ulong index, ulong token) => State.SetUInt64(GetTokenByIndexKey(index), token); - private void ClearTokenByIndex(ulong index) => PersistentState.Clear(GetTokenByIndexKey(index)); + private void ClearTokenByIndex(ulong index) => State.Clear(GetTokenByIndexKey(index)); private string GetIndexByTokenKey(ulong token) => $"IndexByToken:{token}"; - private ulong GetIndexByToken(ulong token) => PersistentState.GetUInt64(GetIndexByTokenKey(token)); + private ulong GetIndexByToken(ulong token) => State.GetUInt64(GetIndexByTokenKey(token)); - private void SetIndexByToken(ulong token, ulong index) => PersistentState.SetUInt64(GetIndexByTokenKey(token), index); + private void SetIndexByToken(ulong token, ulong index) => State.SetUInt64(GetIndexByTokenKey(token), index); - private void ClearIndexByToken(ulong token) => PersistentState.Clear(GetIndexByTokenKey(token)); + private void ClearIndexByToken(ulong token) => State.Clear(GetIndexByTokenKey(token)); private string GetTokenOfOwnerByIndexKey(Address address, ulong index) => $"TokenOfOwnerByIndex:{address}:{index}"; - private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => PersistentState.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); + private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => State.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); - private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => PersistentState.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); + private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => State.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); - private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => PersistentState.Clear(GetTokenOfOwnerByIndexKey(owner, index)); + private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => State.Clear(GetTokenOfOwnerByIndexKey(owner, index)); private string IndexOfOwnerByTokenKey(Address owner, ulong tokenId) => $"IndexOfOwnerByToken:{owner}:{tokenId}"; - private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => PersistentState.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); - private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => PersistentState.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); - private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => PersistentState.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); + private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => State.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); + private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => State.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); + private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => State.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); public ulong TotalSupply { - get => PersistentState.GetUInt64(nameof(TotalSupply)); - private set => PersistentState.SetUInt64(nameof(TotalSupply), value); + get => State.GetUInt64(nameof(TotalSupply)); + private set => State.SetUInt64(nameof(TotalSupply), value); } /// @@ -1161,7 +1162,7 @@ private void RemoveNFToken(Address from, ulong tokenId) Assert(GetIdToOwner(tokenId) == from); var tokenCount = GetOwnerToNFTokenCount(from); SetOwnerToNFTokenCount(from, checked(tokenCount - 1)); - PersistentState.Clear(GetIdToOwnerKey(tokenId)); + State.Clear(GetIdToOwnerKey(tokenId)); ulong index = GetIndexOfOwnerByToken(from, tokenId); ulong lastIndex = tokenCount - 1; @@ -1214,7 +1215,7 @@ private void SafeTransferFromInternal(Address from, Address to, ulong tokenId, b TransferInternal(to, tokenId); - if (PersistentState.IsContract(to)) + if (State.IsContract(to)) { ITransferResult result = Call(to, 0, "OnNonFungibleTokenReceived", new object[] { Message.Sender, from, tokenId, data }, 0); Assert((bool)result.ReturnValue); @@ -1229,7 +1230,7 @@ private void ClearApproval(ulong tokenId) { if (GetIdToApproval(tokenId) != Address.Zero) { - PersistentState.Clear(GetIdToApprovalKey(tokenId)); + State.Clear(GetIdToApprovalKey(tokenId)); } } From 7b5d900eaa8232c310ef771279cc3102e81de940 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Wed, 17 Feb 2021 17:12:37 +0300 Subject: [PATCH 10/12] Fixed switch statement --- Mainnet/STOContract/STOContract/STOContract.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index 542395ee..41a3e673 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -91,12 +91,12 @@ public STOContract(ISmartContractState smartContractState, private ICreateResult CreateTokenContract(TokenType tokenType, UInt256 totalSupply, string name, string symbol, uint decimals) { - return tokenType switch + switch (tokenType) { - TokenType.StandardToken => Create(parameters: new object[] { totalSupply, name, symbol, decimals }), - TokenType.DividendToken => Create(parameters: new object[] { totalSupply, name, symbol, decimals }), - _ => Create(parameters: new object[] { name, symbol }), - }; + case TokenType.StandardToken: return Create(parameters: new object[] { totalSupply, name, symbol, decimals }); + case TokenType.DividendToken: return Create(parameters: new object[] { totalSupply, name, symbol, decimals }); + default: return Create(parameters: new object[] { name, symbol }); + } } public override void Receive() => Invest(); From 2ea1615a4d8d0488a3793f2915808821f47a33b9 Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Thu, 18 Feb 2021 00:03:48 +0300 Subject: [PATCH 11/12] Readme update --- Mainnet/STOContract/README.md | 8 ++++++-- Mainnet/STOContract/STOContract/STOContract.cs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Mainnet/STOContract/README.md b/Mainnet/STOContract/README.md index dda80e07..dbede22e 100644 --- a/Mainnet/STOContract/README.md +++ b/Mainnet/STOContract/README.md @@ -1,11 +1,15 @@ # STO Smart Contract +**Compiler Version** +``` +v2.0.0 +``` **Contract Hash** ``` -0afb2de46b2c3800e65e367171991671f85ce644efd5fe6e9e3e27a8767e55d6 +2b1db3edf5cc6fc4a5fcb9cec0b31350da20367c1cf635bcabf0012933c81b68 ``` **Contract Byte Code** ``` -4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0102008862EED20000000000000000E00022200B013000004A000000020000000000002E68000000200000008000000000001000200000000200000400000000000000040000000000000000A000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000DC6700004F000000000000000000000000000000000000000000000000000000008000000C000000C06700001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E746578740000003448000000200000004A000000020000000000000000000000000000200000602E72656C6F6300000C0000000080000000020000004C000000000000000000000000000040000042000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010680000000000004800000002000500803A0000402D000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004602280600000A72010000706F0700000A2A4A02280600000A7201000070036F0800000A2A4602280600000A72130000706F0900000A2A4A02280600000A7213000070036F0A00000A2A4602280600000A722D0000706F0900000A2A4A02280600000A722D000070036F0A00000A2A4602280600000A72430000706F0900000A2A4A02280600000A7243000070036F0A00000A2A4602280600000A725F0000706F0700000A2A4A02280600000A725F000070036F0800000A2A4602280600000A72790000706F0B00000A2A4A02280600000A7279000070036F0C00000A2A8202280100000602280D00000A6F0E00000A370B022809000006166AFE032A162A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A72AB0000706F0100002B2A4A02280600000A72AB000070036F1000000A2A00000013300500D3000000010000110203281100000A020519FE0572C30000707215010070281200000A281300000A0202280600000A0E076F1400000A7229010070281300000A02281500000A0E096F0200002B0A0206281C000006050B02070E040E050E0628130000060C02086F1700000A7277010070281300000A021203FE15090000021203086F1800000A7D0800000409280300002B020E072806000006020E08280800000602086F1800000A2804000006020718FE01280C0000060202280B0000062D040E042B02156A280A0000060204280F0000060206281A0000062A00133006006900000000000000032C0603172E252B4602166A198D0B0000012516048C15000001A2251705A225180E04A2166A280400002B2A02166A198D0B0000012516048C15000001A2251705A225180E04A2166A280500002B2A02166A188D0B000001251605A225170E04A2166A280600002B2A22022815000006262A00001330080026010000020000110202280D00000672B7010070281300000A0202281B00000A6F1C00000A166AFE0372E3010070281300000A02281600000602281D0000060A02280B0000062D07722F0200702B0572450200700B02022803000006166A07188D0B000001251602281B00000A6F1D00000A8C07000001A22517067B070000048C15000001A2166A281E00000A0C02086F1F00000A2C0D086F2000000AA5170000012B01167255020070281300000A021203FE150A000002120302281B00000A6F1D00000A7D090000041203067B050000047D0A0000041203067B070000047D0B0000041203067B060000047D0C00000409280700002B02022809000006067B07000004DB280A000006067B06000004166A36180202281B00000A6F1D00000A067B06000004282100000A26172A000013300800C90000000300001102022807000006166A7283020070178D0B000001251602281B00000A6F1D00000A8C07000001A2166A281E00000A0A066F1F00000A3982000000066F2000000A0C08750700000114FE03252D0B1203FE1507000001092B0608A5070000010B2C5B077E2200000A282300000A2C4E02022805000006166A72AB020070188D0B0000012516078C07000001A22517198C18000001A2166A281E00000A0A02066F1F00000A2C0B066F2000000A14FE032B011672BD020070281300000A2A021672F1020070281300000A2A000000133003004D000000000000000202281B00000A6F1D00000A02280E000006282400000A7229030070281300000A0202280D00000616FE017279030070281300000A0202280E00000602282500000A282100000A6F1F00000A2A00000013300800BB000000040000110202280B00000616FE0172A50300707215040070281200000A281300000A0202281B00000A6F1D00000A02280E000006282400000A7233040070281300000A0202280D00000616FE017279030070281300000A02022803000006166A722F020070188D0B000001251602281B00000A6F1D00000A8C07000001A225170228090000068C15000001A2166A281E00000A0A02166A280A00000602066F1F00000A2C0D066F2000000AA5170000012B01167255020070281300000A172A00133002003E000000050000111200FE15070000020228100000060B160C2B230708A3070000020D097B0300000402280D00000A6F0E00000A3704090A2B0A0817580C08078E6932D7062A00001330040026000000060000110203281B0000060A020628110000060206068E6917DA8F070000027B0300000428020000062A0000133004005C00000007000011038E698D070000020A02280D00000A6F0E00000A0B160C2B3B0308A3060000020D07097B01000004D70B06081204FE15070000021204077D030000041204097B020000047D040000041104A4070000020817D60C08038E6932BF062A133003003E0000000800001102038E16FE037285040070281300000A030A160B2B210607A3060000020C02087B01000004166AFE0372D1040070281300000A0717580B07068E6932D92A00001330020086000000090000110228190000060A02281B00000A6F1C00000A067B040000045C0B0228090000060C0708363C08067B04000004D90D02281B00000A6F1C00000A09DB13041205FE15080000021205097D05000004120511047D060000041205087D0700000411052A1205FE1508000002120502281B00000A6F1C00000A7D050000041205077D0700000411052AC20203281100000A0204282400000602052822000006020E0428200000060202281B00000A6F1D00000A0428260000062A4602280600000A721F0500706F2600000A2A4A02280600000A721F050070036F2700000A2A4602280600000A722D0500706F2600000A2A4A02280600000A722D050070036F2700000A2A4602280600000A72370500706F0700000A2A4A02280600000A7237050070036F0800000A2A7202280600000A724F050070038C07000001281200000A6F0700000A2A7602280600000A724F050070038C07000001281200000A046F0800000A2A00000013300400A60000000A000011042D34021201FE150D000002120102281B00000A6F1D00000A7D140000041201037D150000041201166A7D1600000407280800002B172A0202281B00000A6F1D00000A28250000060A06043402162A0202281B00000A6F1D00000A0604DB282600000602030203282500000604D72826000006021201FE150D000002120102281B00000A6F1D00000A7D140000041201037D150000041201047D1600000407280800002B172A000013300500AA0000000B000011052D2A021202FE150D0000021202037D140000041202047D150000041202166A7D1600000408280800002B172A020302281B00000A6F1D00000A282B0000060A020328250000060B0605370407053402162A020302281B00000A6F1D00000A0605DB282A00000602030705DB282600000602040204282500000605D72826000006021202FE150D0000021202037D140000041202047D150000041202057D1600000408280800002B172A000013300400650000000C0000110202281B00000A6F1D00000A03282B000006042E02162A0202281B00000A6F1D00000A0305282A000006021200FE150E000002120002281B00000A6F1D00000A7D170000041200037D180000041200057D1A0000041200047D1900000406280900002B172A8E02280600000A7267050070038C07000001048C07000001282800000A056F0800000A2A8A02280600000A7267050070038C07000001048C07000001282800000A6F0700000A2A4602280600000A728B0500706F0700000A2A4A02280600000A728B050070036F0800000A2A7202280600000A729F050070038C07000001281200000A6F0A00002B2A7602280600000A729F050070038C07000001281200000A046F0B00002B2AC20203281100000A020428400000060205283E000006020E04283C0000060202281B00000A6F1D00000A0428420000062A660202282C00000602281B00000A6F1C00000AD7282D0000062A8E0202281B00000A6F1D00000A283400000626020328340000062602030428430000062A6A020328340000062602042834000006260203040528440000062A0000133003003C0000000D0000110203282E0000060A02030628350000060B07166A36241200067B1B00000407D77D1B000004120002282C0000067D1D000004020306282F000006062A13300200210000000E00001102282C000006047B1D000004DB0A0203284100000606D90B047B1B00000407D72A4A0202281B00000A6F1D00000A28370000062A13300300180000000F0000110203282E0000060A020306283500000602283F0000065C2A4A0202281B00000A6F1D00000A28390000062A00133003001F0000000F0000110203282E0000060A0203062835000006067B1C000004D702283F0000065C2A001330030093000000100000110202281B00000A6F1D00000A28340000060A067B1B00000402283F0000065C0B067B1B00000402283F0000065E0C0207166AFE0372B7050070281300000A1200067B1C000004067B1B000004D708DB7D1C0000041200087D1B0000040202281B00000A6F1D00000A06282F0000060202281B00000A6F1D00000A07282100000A0D02096F1F00000A72F3050070281300000A2A4602280600000A721F0500706F2600000A2A4A02280600000A721F050070036F2700000A2A4602280600000A722D0500706F2600000A2A4A02280600000A722D050070036F2700000A2A4602280600000A72370500706F0700000A2A4A02280600000A7237050070036F0800000A2A7202280600000A724F050070038C07000001281200000A6F0700000A2A7602280600000A724F050070038C07000001281200000A046F0800000A2A00000013300400A600000011000011042D34021201FE1510000002120102281B00000A6F1D00000A7D1E0000041201037D1F0000041201166A7D2000000407280C00002B172A0202281B00000A6F1D00000A28410000060A06043402162A0202281B00000A6F1D00000A0604DB284200000602030203284100000604D72842000006021201FE1510000002120102281B00000A6F1D00000A7D1E0000041201037D1F0000041201047D2000000407280C00002B172A000013300500AA00000012000011052D2A021202FE15100000021202037D1E0000041202047D1F0000041202166A7D2000000408280C00002B172A020302281B00000A6F1D00000A28470000060A020328410000060B0605370407053402162A020302281B00000A6F1D00000A0605DB284600000602030705DB284200000602040204284100000605D72842000006021202FE15100000021202037D1E0000041202047D1F0000041202057D2000000408280C00002B172A00001330040065000000130000110202281B00000A6F1D00000A032847000006042E02162A0202281B00000A6F1D00000A03052846000006021200FE1511000002120002281B00000A6F1D00000A7D210000041200037D220000041200057D240000041200047D2300000406280D00002B172A8E02280600000A7267050070038C07000001048C07000001282800000A056F0800000A2A8A02280600000A7267050070038C07000001048C07000001282800000A6F0700000A2A7202280600000A7215060070038C18000001281200000A6F0B00000A2A7602280600000A7215060070038C18000001281200000A046F0C00000A2A467243060070038C15000001281200000A2A4E02280600000A0203284A0000066F0900000A2A5202280600000A0203284A000006046F0A00000A2A46725F060070038C15000001281200000A2A4E02280600000A0203284D0000066F0900000A2A5202280600000A0203284D000006046F0A00000A2A7202280600000A7281060070038C07000001281200000A6F0700000A2A7602280600000A7281060070038C07000001281200000A046F0800000A2A8A02280600000A72B1060070038C07000001048C07000001282800000A6F0B00000A2A8E02280600000A72B1060070038C07000001048C07000001282800000A056F0C00000A2A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A722D0500706F2600000A2A4A02280600000A722D050070036F2700000A2A4602280600000A721F0500706F2600000A2A4A02280600000A721F050070036F2700000A2A4602280600000A72E10600706F0700000A2A4A02280600000A72E1060070036F0800000A2A4672F9060070038C15000001281200000A2A4E02280600000A0203285C0000066F0700000A2A5202280600000A0203285C000006046F0800000A2A4E02280600000A0203285C0000066F2B00000A2A46721B070070038C15000001281200000A2A4E02280600000A020328600000066F0700000A2A5202280600000A02032860000006046F0800000A2A4E02280600000A020328600000066F2B00000A2A5E723D070070038C07000001048C15000001282800000A2A5202280600000A02030428640000066F0700000A2A5602280600000A0203042864000006056F0800000A2A5202280600000A02030428640000066F2B00000A2A5E7275070070038C07000001048C15000001282800000A2A5202280600000A02030428680000066F0700000A2A5602280600000A0203042868000006056F0800000A2A5202280600000A02030428680000066F2B00000A2A4602280600000A72370500706F0700000A2A4A02280600000A7237050070036F0800000A2A00001330030057000000000000000203281100000A021717284900000602181728490000060219162849000006021A172849000006021B17284900000602042857000006020528590000060202281B00000A6F1D00000A285500000602176A285B0000062A72020302286C000006FE0572AD070070281300000A0203285D0000062A7A020402032850000006FE0572AD070070281300000A02030428650000062A22020328480000062A32020304050E04287E0000062A4202030405168D19000001287E0000062A0000133003003800000014000011020528840000060205284B0000060A0206288A0000060204288A000006020603282400000A72D9070070281300000A020405287B0000062A133004003A0000001400001102042883000006020428850000060204284B0000060A020306282300000A72D9070070281300000A020403284F0000060206030428810000062A9E0202281B00000A6F1D00000A030428530000060202281B00000A6F1D00000A030428820000062A3E0203288A000006020328500000062A00001330020011000000140000110203284B0000060A0206288A000006062A3E020328850000060203284E0000062A2602030428520000062A001330040029000000140000110204284B0000060A0204287F000006020604287C000006020304287D0000060206030428800000062A000000133004007B00000015000011020204284B00000603282400000A72D9070070281300000A020328500000060A020306176ADB285100000602280600000A0204284A0000066F2B00000A02030428690000060B06176ADB0C07082E1B02030828650000060D02030907286A0000060203070928660000060203082867000006020304286B0000062A00133004004C0000000E000011020204284B0000067E2200000A282400000A72D9070070281300000A020403284C000006020328500000060A020306176AD72851000006060B02030407286A0000060203070428660000062A133008009D0000001600001102052884000006020528850000060205284B0000060A020603282400000A72D9070070281300000A0204288A000006020405287B00000602280600000A046F1400000A2C570204166A72F70700701A8D0B000001251602281B00000A6F1D00000A8C07000001A22517038C07000001A22518058C15000001A225190E04A2166A281E00000A0B02076F2000000AA51700000172D9070070281300000A2A9A0203284E0000067E2200000A282300000A2C1202280600000A0203284D0000066F2B00000A2A133003002800000017000011021200FE15120000021200037D250000041200047D260000041200057D2700000406280E00002B2A133003002800000018000011021200FE15130000021200037D280000041200047D290000041200057D2A00000406280F00002B2A133003002800000019000011021200FE15140000021200037D2B0000041200047D2C0000041200057D2D00000406281000002B2A133004003C000000140000110203284B0000060A020602281B00000A6F1D00000A282400000A2D14020602281B00000A6F1D00000A28520000062B011772D9070070281300000A2A1330040055000000140000110203284B0000060A020602281B00000A6F1D00000A282400000A2D2D0203284E00000602281B00000A6F1D00000A282400000A2D14020602281B00000A6F1D00000A28520000062B011772D9070070281300000A2A0000001330020010000000140000110203284B0000060A0206288A0000062A13300400520000001A00001102288700000602037E2200000A282300000A722D0800707295080070281200000A281300000A021200FE151500000212000228540000067D2E0000041200037D2F00000406281100002B020328550000062A8A0202281B00000A6F1D00000A022854000006282400000A72A1080070281300000A2A00000013300300980000001B0000110228870000060203288A0000060204166AFE0372FD080070281300000A02286C0000060A0604D70B02285A0000060C2B4D020308287D000006020608285E0000060208062862000006021203FE151200000212037E2200000A7D250000041203037D260000041203087D2700000409280E00002B06176AD70A08176AD70C060737AF0202286C00000604D7286D0000060208285B0000062A13300300AB0000001C0000110203284B0000060A0206288A000006020602281B00000A6F1D00000A282400000A7249090070281300000A0203287F000006020603287C000006020328610000060B02286C000006176ADB1304021104286D00000611040C0208285D0000060D020709285E00000602090728620000060208285F00000602032863000006021205FE15120000021205067D2500000412057E2200000A7D260000041205037D270000041105280E00002B2A5E02037E2200000A282300000A7293090070281300000A2A0042534A4201000100000000000C00000076342E302E33303331390000000005006C0000007C130000237E0000E8130000F40B000023537472696E677300000000DC1F0000D009000023555300AC290000100000002347554944000000BC2900008403000023426C6F620000000000000002000001571FA201090A000000FA0133001600000100000019000000150000002F0000008A000000BE000000020000002B00000003000000170000001C000000040000001500000029000000010000000300000010000000110000000000090401000000000006001D02F70706005C02F70706000902BF060F00170800000A004C0212090A00730912090A00020912090A00B70112090A009C0912090A00AA0912090600810948040600870148040A003D02120906007E0448040E00BF04A8070A00DE0112090A002C0312090600060B48040600CE0248040A00780612090600150048040A005B01120906008304480406000100480406007A024804000000001C000000000001000100010010004809000019000100010001001000C0040000190001001E0001001000B1040000190001002C0001001000EB0400001900010048000A0110005A0A0000310001008B000A011000CF000000310003008B000A011000C6050000310005008B000A0110000A030000310008008B000A01100022030000310009008B000A0110004F04000031000D008B000201000091010000390010008B000A01100016030000310014008B000A011000EC020000310017008B000A011000040A000031001B008B000A0110001603000031001E008B000A011000EC020000310021008B000A01100016030000310025008B000A011000EC020000310028008B000A011000F802000031002B008B000A011000D502000031002E008B000600260814020600FC04140206004E0314020600FC0414020600AE00140206001E0A140206002B0A140206008E08F0000600FE05F0000600AE00140206002B0A14020600890014020600820B17020600960517020600A4001A02060631001D025680C00420025680B10420025680EB04200206007404F0000600C005F00006003E0A140206006C06F00006000506F0000600140A140206003E0A140206000901140206002B0714020600EE06140206007404F0000600C005F00006003E0A140206006C06F00006000506F0000600140A140206003E0A140206007404F0000600C005F00006007900140206006C06F0000600BA00F00006007900140206006C06F0000600B006F0000600BA001A0206005B06F00006006906F00050200000000086083D033D00010062200000000081084A0324020100752000000000860879089000020087200000000081088A08290202009A20000000008608500890000300AC200000000081085F0829020300BF200000000086089B0890000400D120000000008108AD0829020400E42000000000860819013D000500F6200000000081082A01240205000921000000008608CE048C0006001B21000000008108E5042F0206002E2100000000860889058C0007004F210000000086082D069000070061210000000081083706290207007421000000008608590734020800862100000000810869073A0208009C21000000008618B906410209007C22000000008100540953021200F12200000000C6009F0206001600FC22000000008600530A8C001600302400000000810092000600160008250000000086004B078C001600642500000000860035088C0016002C26000000008100DA005D0216007826000000008100950762021600AC2600000000810029096902170014270000000081008507620218006027000000008100C30572021900F227000000008618B9067702190023280000000086081B0480021D0035280000000081082604A9011D004828000000008608700180021E005A280000000081087901A9011E006D2800000000E609960B3D001F007F28000000008108A60B24021F00922800000000E6013B0184022000AF2800000000810046018A022100D02800000000E601A70591022300842900000000E6015904980225003C2A00000000E601A702A1022800AD2A000000008100BB03A9022B00D12A00000000E6015101B2022E00F42A000000008608D2063D003000062B000000008108E00624023000192B000000008100F609BA023100362B000000008100010AC1023200542B000000008618B90677023400852B00000000C6009F02060038009F2B00000000E601A70591023800C32B00000000E601590498023A00E02B000000008100E809BA023D00282C0000000081000007C9023E00552C0000000086003E073D004000682C0000000086003E07840240008C2C00000000860019073D004100A02C000000008600190784024100CC2C0000000086006A0A060042006B2D0000000086081B04800242007D2D0000000081082604A9014200902D000000008608700180024300A22D0000000081087901A9014300B52D00000000E609960B3D004400C72D000000008108A60B24024400DA2D00000000E6013B0184024500F72D00000000810046018A024600182E000000008100B20591024800CC2E000000008100660498024A00842F00000000E601A702A1024D00F52F000000008100BB03A9025000193000000000E6015101B20253003C30000000008100C907D10255005930000000008100E007D60256007730000000008100490BDC02580089300000000081004106E10259009D300000000081004E06E7025A00B2300000000081000C0BDC025C00C4300000000081008D03E1025D00D8300000000081009D03E7025E00ED30000000008100BA09840260000A31000000008100D1098A02610028310000000081009306EE0263004B31000000008100A606F60265006F310000000086082D069000680081310000000081083706290268009431000000008608700180026900A6310000000081087901A9016900B9310000000086081B0480026A00CB310000000081082604A9016A00DE3100000000810861003D006B00F031000000008108710024026B000332000000008100590BDC026C001532000000008100850AFF026D002932000000008100950A04036E003E32000000008100730A240270005232000000008100360BDC02710064320000000081006305FF02720078320000000081007305040373008D32000000008100510524027500A1320000000081006C0B0A037600B932000000008100BE0A11037800CE32000000008100D50A18037A00E432000000008100A50A8A027D00F9320000000081001F0B0A037F00113300000000810023051103810026330000000081003A05180383003C330000000081000A058A0286005133000000008608960B3D0088006333000000008108A60B240288007833000000008618B90620038900DB33000000008600980AFF028C00F833000000008600D80A11038D001734000000008600EB00D1028F0020340000000086005504280390002D340000000086005504A902940040340000000086005904A90297008434000000008600A7028A029A00CA34000000008600EA0333039C00F234000000008600AF0284029E000435000000008600B902E1029F002135000000008600B700E102A0003135000000008600C703EE02A1003C3500000000810070038A02A3007435000000008100A3048A02A500FC350000000081008B048A02A700543600000000810057032803A900FD36000000008100AD032402AD0024370000000081001506A902AE0058370000000081008103A902B1008C37000000008100D803F602B400C037000000008100AC012402B700083800000000810021062402B8006C3800000000810096042402B9008838000000008600D7052902BA00E638000000008100860B0600BB000C39000000008600FC038A02BB00B039000000008600A2052402BD00673A000000008600DC0B2902BE0000000100990200000100990200000100990200000100990200000100990200000100990200000100990200000100990200000100CB01000002007206000003009B0100000400B60B000005008201000006003104000007006E0800000800BF08000009007907000001009B0100000200B60B00000300820100000400310400000100A00700000100A00700000100A00700000100CB0100000200B60B000003008201000004003104000001009902000001009902000001009902000001000A09000001000A0900000200990200000100D40500000200450A00000100790400000200D40500000300450A000001000D0600000200370A00000300450A000001007206000002000D06000003009902000001007206000002000D06000001009902000001000A09000001000A09000002000C0A00000100030200000200B60B00000300820100000400310400000100D40500000200450A00000100790400000200D40500000300450A000001000A09000001000A09000002000C0A000001000A09000001000A09000001009902000001009902000001009902000001000A09000001000A0900000200990200000100D40500000200450A00000100790400000200D40500000300450A000001000D0600000200370A00000300450A000001007206000002000D06000003009902000001007206000002000D0600000100550000000100550000000200990200000100CC0000000100CC0000000100CC0000000200990200000100CC0000000100CC0000000100CC00000002009902000001000A09000001000A0900000200990200000100720600000200CD0800000100720600000200CD0800000300990200000100990200000100990200000100990200000100990200000100EC0A00000100EC0A00000100EC0A00000200830500000100EC0A00000100830500000100830500000100830500000200EC0A000001008305000001000A0900000200EC0A000001000A0900000200EC0A00000100720600000200EC0A00000300810000000100720600000200EC0A00000100720600000200810000000100720600000200810000000100720600000200810000000300EC0A00000100720600000200810000000100990200000100030200000200820100000300310400000100EC0A00000100720600000200EC0A00000100250000000100790400000200D40500000300810000000400390000000100790400000200D40500000300810000000100790400000200D40500000300810000000100C30000000200810000000100CD0800000200C30000000100720600000100810000000100810000000100720600000200CD0800000100D40500000200810000000100790400000200810000000100D40500000200810000000100790400000200D40500000300810000000400390000000100810000000100790400000200D40500000300810000000100720600000200C30000000300810000000100720600000200CD0800000300C300000001008100000001008100000001008100000001007206000001000A0900000200450A000001008100000001000A0903003D0004003D000900B90601001100B90606001900B9060A002900B90606006900B90606003100EF011000810008001500810012001A008100F40820008100FF082600810038042D008100400432003100330338008900EF053D008100FA0A41008100030B4E003100B906610099004109670031004C0A6D00810068097300310084067900A100F20A7E00490044088C004900DD0890003100280395003100A501A10031006401C400B1007F023D00B100FA05900031000404C900510044088C0051008902D50031002406DE003900CF05F0003900CE0BF4003900C20BF4003100FD003D008100C10232018100CB023701990041095901810088096001810092096C018100E905A901090044000502090048000A0209004C000F022E000B0051032E0013005A032E001B007903430023000A0221012B000A0281022B000A02A1022B000A02E1022B000A0201032B000A02C1032B000A02E1032B000A0221042B000A0241042B000A02A1042B000A02C1042B000A02E1042B000A0201052B000A0221052B000A0241052B000A0261052B000A0281052B000A02C1052B000A02E1052B000A025500BA00E600FC0001010C0112011E0127013D0148014F0174017A017F0184018D0198019F01AE01B301BA01C101CB01D501DF01E901F1010200010003000A0004000D000500110000004E033A0300008E083E03000063083E030000B1083E0300002E013A030000E904430300008D05430300006C063E0300006D07470300002A044D0300007D014D030000AA0B3A03000041073A0300002A044D0300007D014D030000AA0B3A0300006C063E0300007D014D0300002A044D03000075003A030000AA0B3A03020001000300010002000300020003000500010004000500020005000700010006000700020007000900010008000900020009000B0001000A000B0002000B000D0001000C000D0002000D000F0002000E00110001000F00110002001000130001001100130002001F00150001002000150002002100170001002200170002002300190001002400190002002C001B0001002D001B0002003B001D0001003C001D0002003D001F0001003E001F0002003F00210001004000210002005400230001005500230002005600250001005700250002005800270001005900270002005A00290001005B00290002006C002B0001006D002B0004800000000000000000000000000000000073090000040000000000000000000000FC013E000000000001000200010000000000000000001209000000000100000000000000000000000000A80700000000060002000700020008000200090002000A0002000B0002000C0002000D0003000E0003000F0004001000040011000400120005001300050014000500150005001F0049002D00870033009C003500AB003500B0003500B5003300D90033004301330054015300670155006701330093013300A4013300C6013300D0013300DA013300E401000000000055496E7433320047657455496E7436340053657455496E743634003C4D6F64756C653E00696E7465726661636549440076616C75655F5F00646174610053797374656D2E507269766174652E436F72654C696200696E746572666163654964006765745F4E657874546F6B656E4964007365745F4E657874546F6B656E496400746F6B656E496400526566756E64656400456E737572654B796356657269666965640049735265766F6B656400496E76657374656400476574417070726F76656400617070726F7665640069640053616C65506572696F640047657443757272656E74506572696F6400537570706F727473496E74657266616365006765745F42616C616E6365004469766964656E6442616C616E6365006765745F546F6B656E42616C616E6365007365745F546F6B656E42616C616E63650047657442616C616E63650053657442616C616E636500416C6C6F77616E636500494D657373616765006765745F4D657373616765006765745F4E616D65007365745F4E616D65006E616D650056616C75655479706500546F6B656E5479706500746F6B656E54797065004372656174650043616E4F7065726174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E745374617465006765745F50657273697374656E7453746174650073746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E646578417474726962757465004465706C6F794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650042797465006765745F56616C7565006765745F52657475726E56616C75650076616C7565005265636569766500417070726F76650042616C616E63654F66004F776E65724F6600476574537472696E6700536574537472696E67004F776E6572736869705472616E7366657265644C6F6700417070726F76616C4C6F6700417070726F76616C466F72416C6C4C6F670053544F53657475704C6F67005472616E736665724C6F6700496E766573744C6F670049426C6F636B006765745F426C6F636B006765745F456E64426C6F636B007365745F456E64426C6F636B00536166655472616E7366657246726F6D496E7465726E616C005472616E73666572496E7465726E616C004C6F67417070726F76616C004765744964546F417070726F76616C005365744964546F417070726F76616C00436C656172417070726F76616C00536574417070726F76616C004973417070726F766564466F72416C6C004C6F67417070726F76616C466F72416C6C00536574417070726F76616C466F72416C6C004D696E74416C6C0043616C6C00536D617274436F6E74726163742E646C6C006765745F53796D626F6C007365745F53796D626F6C0073796D626F6C00476574426F6F6C00536574426F6F6C0053797374656D00436C61696D00536166655472616E7366657246726F6D005472616E73666572546F6B656E7346726F6D0066726F6D00456E756D00426F6F6C65616E004164644E46546F6B656E0056616C69644E46546F6B656E0052656D6F76654E46546F6B656E004469766964656E64546F6B656E00495374616E64617264546F6B656E006765745F49734E6F6E46756E6769626C65546F6B656E007365745F49734E6F6E46756E6769626C65546F6B656E005072696365506572546F6B656E00436C656172496E6465784F664F776E65724279546F6B656E00476574496E6465784F664F776E65724279546F6B656E00536574496E6465784F664F776E65724279546F6B656E00436C656172496E6465784279546F6B656E00476574496E6465784279546F6B656E00536574496E6465784279546F6B656E00746F6B656E006765745F53616C654F70656E004465736372697074696F6E004275726E005472616E73666572546F005472616E73666572546F6B656E73546F0047657453616C65496E666F005A65726F00746F005472616E736665724F776E65727368697000436C656172006765745F4E756D626572006765745F53656E646572005370656E646572007370656E646572004C6F675472616E736665720043616E5472616E73666572006765745F4F776E6572007365745F4F776E6572004765744964546F4F776E6572005365744964546F4F776E65720050726576696F75734F776E6572004E65774F776E6572006F776E6572004953657269616C697A6572006765745F53657269616C697A6572004765744F776E6572546F4F70657261746F72005365744F776E6572546F4F70657261746F72002E63746F720053797374656D2E446961676E6F7374696373006765745F4469766964656E6473007365745F4469766964656E64730043726564697465644469766964656E647300476574576974686472617761626C654469766964656E647300476574546F74616C4469766964656E64730057697468647261776E4469766964656E6473004765744469766964656E647300576974686472617746756E6473006765745F53616C65506572696F6473007365745F53616C65506572696F64730073616C65506572696F64730056616C6964617465506572696F647300536574506572696F647300706572696F647300537472617469732E536D617274436F6E7472616374732E5374616E646172647300476574537570706F72746564496E746572666163657300536574537570706F72746564496E74657266616365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004475726174696F6E426C6F636B73005769746864726177546F6B656E73006765745F53756363657373006765745F4B594341646472657373007365745F4B594341646472657373006B796341646472657373006765745F546F6B656E41646472657373007365745F546F6B656E41646472657373006765745F4D617070657241646472657373007365745F4D617070657241646472657373006D617070657241646472657373006F70657261746F7241646472657373006765745F4E6577436F6E74726163744164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300436F6E7665727453616C65506572696F64496E7075747300466F726D61740053544F436F6E747261637400437265617465546F6B656E436F6E7472616374004973436F6E747261637400536D617274436F6E7472616374004F626A65637400476574537472756374005365745374727563740049437265617465526573756C7400495472616E73666572526573756C74004765744F776E6572546F4E46546F6B656E436F756E74005365744F776E6572546F4E46546F6B656E436F756E74005570646174654163636F756E74004765744163636F756E74005365744163636F756E74006163636F756E74004F6C64416D6F756E7400526566756E64416D6F756E7400546F6B656E416D6F756E740063757272656E74416D6F756E7400616D6F756E740041737365727400496E766573740053616C65506572696F64496E70757400576974686472617700436C656172546F6B656E4279496E64657800476574546F6B656E4279496E64657800536574546F6B656E4279496E64657800436C656172546F6B656E4F664F776E65724279496E64657800476574546F6B656E4F664F776E65724279496E64657800536574546F6B656E4F664F776E65724279496E64657800696E64657800546F4172726179004765744172726179005365744172726179004765744964546F417070726F76616C4B657900496E6465784F664F776E65724279546F6B656E4B657900476574496E6465784279546F6B656E4B6579004765744964546F4F776E65724B657900476574546F6B656E4279496E6465784B657900476574546F6B656E4F664F776E65724279496E6465784B657900456E737572654F776E65724F6E6C79006765745F546F74616C537570706C79007365745F546F74616C537570706C7900746F74616C537570706C79006F705F457175616C697479006F705F496E657175616C69747900456E737572654164647265737349734E6F74456D70747900001145006E00640042006C006F0063006B00001954006F006B0065006E00410064006400720065007300730000154B00590043004100640064007200650073007300001B4D00610070007000650072004100640064007200650073007300001954006F006B0065006E00420061006C0061006E00630065000025490073004E006F006E00460075006E006700690062006C00650054006F006B0065006E00000B4F0077006E00650072000017530061006C00650050006500720069006F0064007300005154006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006200650020006200650074007700650065006E0020003000200061006E00640020003200001374006F006B0065006E005400790070006500004D54006800650020006B007900630041006400720065007300730020006900730020006E006F00740020006100200063006F006E00740072006100630074002000610064007200650073007300003F4300720065006100740069006E006700200074006F006B0065006E00200063006F006E007400720061006300740020006600610069006C00650064002E00002B5400680065002000530054004F00200069007300200063006F006D0070006C0065007400650064002E00004B540068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000155400720061006E00730066006500720054006F00000F4D0069006E00740041006C006C00002D54006F006B0065006E0020007400720061006E00730066006500720020006600610069006C00650064002E0000274700650074005300650063006F006E0064006100720079004100640064007200650073007300001147006500740043006C00610069006D00003359006F007500720020004B005900430020006900730020006E006F0074002000760065007200690066006900650064002E00003754006800650020006100640064007200650073007300200068006100730020006E006F0020006D0061007000700069006E0067002E00004F4F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E0073006600650072002000660075006E00640073002E00002B530054004F0020006900730020006E006F007400200065006E0064006500640020007900650074002E00006F54006800650020007B0030007D0020006D006500740068006F00640020006900730020006E006F007400200073007500700070006F007200740065006400200066006F00720020004E006F006E002D00460075006E006700690062006C006500200054006F006B0065006E002E00011D5700690074006800640072006100770054006F006B0065006E00730000514F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E007300660065007200200074006F006B0065006E0073002E00004B50006C0065006100730065002000700072006F00760069006400650020006100740020006C006500610073007400200031002000730061006C006500200070006500720069006F006400004D4400750072006100740069006F006E0042006C006F0063006B0073002000730068006F0075006C006400200068006900670068006500720020007400680061006E0020007A00650072006F00000D530079006D0062006F006C0000094E0061006D006500001754006F00740061006C0053007500700070006C0079000017420061006C0061006E00630065003A007B0030007D00002341006C006C006F00770061006E00630065003A007B0030007D003A007B0031007D0000134400690076006900640065006E006400730000174100630063006F0075006E0074003A007B0030007D00003B54006800650020006100630063006F0075006E007400200068006100730020006E006F0020006400690076006900640065006E00640073002E0000215400720061006E00730066006500720020006600610069006C00650064002E00002D53007500700070006F00720074006500640049006E0074006500720066006100630065003A007B0030007D00001B4900640054006F004F0077006E00650072003A007B0030007D0000214900640054006F0041007000700072006F00760061006C003A007B0030007D00002F4F0077006E006500720054006F004E00460054006F006B0065006E0043006F0075006E0074003A007B0030007D00002F4F0077006E006500720054006F004F00700065007200610074006F0072003A007B0030007D003A007B0031007D0000174E0065007800740054006F006B0065006E0049006400002154006F006B0065006E004200790049006E006400650078003A007B0030007D00002149006E006400650078004200790054006F006B0065006E003A007B0030007D00003754006F006B0065006E004F0066004F0077006E00650072004200790049006E006400650078003A007B0030007D003A007B0031007D00003749006E006400650078004F0066004F0077006E00650072004200790054006F006B0065006E003A007B0030007D003A007B0031007D00002B540068006500200069006E00640065007800200069007300200069006E00760061006C00690064002E00001D41007300730065007200740020006600610069006C00650064002E0000354F006E004E006F006E00460075006E006700690062006C00650054006F006B0065006E0052006500630065006900760065006400006754006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006E006F0074002000620065002000640065006600610075006C00740028007A00650072006F002900200061006400640072006500730073002E00000B6F0077006E0065007200005B4F006E006C00790020006F0077006E006500720020006F0066002000740068006500200063006F006E00740072006100630074002000630061006E00200073006500740020006E006500770020006F0077006E00650072002E00004B740068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000494F006E006C007900200074006F006B0065006E0020006F0077006E00650072002000630061006E0020006200750072006E002000740068006500200074006F006B0065006E002E000039540068006500200061006400640072006500730073002000630061006E0020006E006F00740020006200650020007A00650072006F002E0000000000FD646BC443B245489ABE08ABE6FFCD940004200101080320000105200101111104200012410420010B0E052002010E0B052001111D0E062002010E111D042001020E052002010E0204200012450320000B073001011D1E000E040A01111C062002010E12490B07041D11181130122511240520010112210500020E0E1C05200201020E05200102111D0420001251083001011D1E001D05040A01111803200002042000111D06300101011E00040A0111240930010312250B1D1C0B040A01120C040A011210040A01121409070411200E1229112804200012590B20051229111D0B0E1D1C0B0320001C040A0111280720021229111D0B0907041229111D1C111D0306111D07000202111D111D04070112290A0704111C1D111C08111C0507011D111C0B07051D111C0B081118111C0807031D11180811180A0706111C0B0B0B0B11200420010E0E052002010E0E0507020B1134040A0111340607030B0B11340407011138040A0111380600030E0E1C1C063001011E000E040A01113C07300102010E1E00050702113C0B0407020B0B040701113C080704113C0B0B12290507020B1140040A0111400607030B0B11400407011144040A011144042001010E040701111D0607040B0B0B0B060702111D12290407011148040A011148040701114C040A01114C0407011150040A0111500407011154040A0111540707040B0B0B11480A0706111D0B0B0B0B1148087CEC85D7BEA7798E04000000000401000000040200000002060B02060E02060202060903061130042001010B05200101111D04200101020520001D111C062001011D111C112009011221111D090B0E0E111D111D1D05092004122511300B0E0E042000111C062001011D11180820011D111C1D111804200011200820040112210B0E0E0320000E0520010B111D06200201111D0B06200202111D0B08200302111D111D0B07200302111D0B0B08200301111D111D0B0720020B111D111D062001113C111D07200201111D113C0720020B111D113C04200102090520020109020420010E0B052001111D0B062002010B111D07200202111D111D08200301111D111D020420010B0B052002010B0B0620020E111D0B0620020B111D0B07200301111D0B0B0720030112210E0E0A200401111D111D0B1D0506200201111D020328000B042800111D032800020528001D111C0328000E0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000200000000000000000000000000000000000000100000000000000000000000000000000468000000000000000000001E68000000200000000000000000000000000000000000000000000010680000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000C000000303800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010200F94775F00000000000000000E00022200B013000004E00000002000000000000226C000000200000008000000000001000200000000200000400000000000000040000000000000000A000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000D06B00004F000000000000000000000000000000000000000000000000000000008000000C000000B46B00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000284C000000200000004E000000020000000000000000000000000000200000602E72656C6F6300000C000000008000000002000000500000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046C0000000000004800000002000500343C0000802F000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004602280600000A72010000706F0700000A2A4A02280600000A7201000070036F0800000A2A4602280600000A72130000706F0900000A2A4A02280600000A7213000070036F0A00000A2A4602280600000A722D0000706F0900000A2A4A02280600000A722D000070036F0A00000A2A4602280600000A72430000706F0900000A2A4A02280600000A7243000070036F0A00000A2A4602280600000A725F0000706F0B00000A2A4A02280600000A725F000070036F0C00000A2A4602280600000A72790000706F0D00000A2A4A02280600000A7279000070036F0E00000A2A9E02280100000602280F00000A6F1000000A371202280900000616281100000A281200000A2A162A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A72AB0000706F0100002B2A4A02280600000A72AB000070036F1400000A2A1330060006010000010000110203281500000A020519FE0572C30000707217010070281600000A281700000A0202280600000A0E086F1800000A722B010070726F010070281600000A281700000A0202280600000A0E096F1800000A722B0100707285010070281600000A281700000A02281900000A0E0A6F0200002B0A0206281C000006050B02070E040E050E060E0728130000060C02086F1B00000A72A1010070281700000A021203FE15090000021203086F1C00000A7D0800000409280300002B020E082806000006020E09280800000602086F1C00000A2804000006020718FE01280C0000060202280B0000062D040E042B07156A281E00000A280A0000060204280F0000060206281A0000062A0000133006007D00000000000000032C0603172E2F2B5A02166A1A8D0C0000012516048C08000001A2251705A225180E04A225190E058C16000001A2166A280400002B2A02166A1A8D0C0000012516048C08000001A2251705A225180E04A225190E058C16000001A2166A280500002B2A02166A188D0C000001251605A225170E04A2166A280600002B2A22022815000006262A00001330080075010000020000110202280D00000672E1010070281700000A0202282000000A6F2100000A166AFE03720D020070281700000A02281600000602281D0000060A02280B0000062D3E02022803000006166A7259020070188D0C000001251602282000000A6F2200000A8C07000001A22517067B070000048C08000001A2166A282300000A2B4102022803000006166A726F020070188D0C000001251602282000000A6F2200000A8C07000001A22517067B07000004282400000A8C18000001A2166A282300000A0B02076F2500000A2C0D076F2600000AA5190000012B0116727F020070281700000A021202FE150A000002120202282000000A6F2200000A7D090000041202067B050000047D0A0000041202067B070000047D0B0000041202067B060000047D0C00000408280700002B02022809000006067B07000004282700000A280A000006067B06000004166A36290202282000000A6F2200000A067B06000004282800000A0B02076F2500000A72AD020070281700000A172A00000013300800DD0000000300001102022807000006166A72CB020070178D0C000001251602282000000A6F2200000A8C07000001A2166A282300000A0A066F2500000A3996000000066F2600000A0C08750700000114FE03252D0B1203FE1507000001092B0608A5070000010B2C6F077E2900000A282A00000A2C6202022805000006166A72F3020070188D0C0000012516078C07000001A22517198C16000001A2166A282300000A0A02066F2500000A2C1F066F2600000A750100001B2513042C0F11042D03162B0911048E16FE032B01167205030070281700000A2A02167239030070281700000A2A000000133003004D000000000000000202282000000A6F2200000A02280E000006282B00000A7271030070281700000A0202280D00000616FE0172C1030070281700000A0202280E00000602282C00000A282800000A6F2500000A2A00000013300800D4000000040000110202280B00000616FE0172ED030070725D040070281600000A281700000A0202282000000A6F2200000A02280E000006282B00000A727B040070281700000A0202280D00000616FE0172C1030070281700000A02280900000616281100000A282D00000A2C02172A02022803000006166A7259020070188D0C000001251602282000000A6F2200000A8C07000001A225170228090000068C08000001A2166A282300000A0A0216281100000A280A00000602066F2500000A2C0D066F2600000AA5190000012B0116727F020070281700000A172A133002003E000000050000111200FE15070000020228100000060B160C2B230708A3070000020D097B0300000402280F00000A6F1000000A3704090A2B0A0817580C08078E6932D7062A00001330040026000000060000110203281B0000060A020628110000060206068E6917DA8F070000027B0300000428020000062A0000133004005C00000007000011038E698D070000020A02280F00000A6F1000000A0B160C2B3B0308A3060000020D07097B01000004D70B06081204FE15070000021204077D030000041204097B020000047D040000041104A4070000020817D60C08038E6932BF062A133003003E0000000800001102038E16FE0372CD040070281700000A030A160B2B210607A3060000020C02087B01000004166AFE037219050070281700000A0717580B07068E6932D92A0000133002009A000000090000110228190000060A02282000000A6F2100000A067B040000045C0B0228090000060C07281E00000A08281200000A2C4108282400000A067B04000004D90D02282000000A6F2100000A09DB13041205FE15080000021205097D05000004120511047D060000041205087D0700000411052A1205FE1508000002120502282000000A6F2100000A7D05000004120507281E00000A7D0700000411052AE20203281500000A0204282600000602052822000006020E042820000006020E0528240000060202282000000A6F2200000A0428280000062A4602280600000A72670500706F2E00000A2A4A02280600000A7267050070036F2F00000A2A4602280600000A72750500706F2E00000A2A4A02280600000A7275050070036F2F00000A2A4E02280600000A727F0500706F3000000A16912A6E02280600000A727F050070178D1A0000012516039C6F3100000A2A4602280600000A72910500706F0B00000A2A4A02280600000A7291050070036F0C00000A2A7202280600000A72A9050070038C07000001281600000A6F0B00000A2A7602280600000A72A9050070038C07000001281600000A046F0C00000A2A00000013300400C20000000A0000110416281100000A282D00000A2C38021201FE150D000002120102282000000A6F2200000A7D140000041201037D15000004120116281100000A7D1600000407280800002B172A0202282000000A6F2200000A28270000060A0604283200000A2C02162A0202282000000A6F2200000A0604282700000A282800000602030203282700000604283300000A2828000006021201FE150D000002120102282000000A6F2200000A7D140000041201037D150000041201047D1600000407280800002B172A000013300500CF0000000B0000110516281100000A282D00000A2C2E021202FE150D0000021202037D140000041202047D15000004120216281100000A7D1600000408280800002B172A020302282000000A6F2200000A282D0000060A020328270000060B0605283200000A2D090705283200000A2C02162A020302282000000A6F2200000A0605282700000A282C00000602030705282700000A282800000602040204282700000605283300000A2828000006021202FE150D0000021202037D140000041202047D150000041202057D1600000408280800002B172A00133004006A0000000C0000110202282000000A6F2200000A03282D00000604283400000A2C02162A0202282000000A6F2200000A0305282C000006021200FE150E000002120002282000000A6F2200000A7D170000041200037D180000041200057D1A0000041200047D1900000406280900002B172A8E02280600000A72C1050070038C07000001048C07000001283500000A056F0C00000A2A8A02280600000A72C1050070038C07000001048C07000001283500000A6F0B00000A2A4602280600000A72E50500706F0700000A2A4A02280600000A72E5050070036F0800000A2A7202280600000A72F9050070038C07000001281600000A6F0A00002B2A7602280600000A72F9050070038C07000001281600000A046F0B00002B2AE20203281500000A0204284400000602052842000006020E042840000006020E0528460000060202282000000A6F2200000A0428480000062A1E0228340000062A660202282E00000602282000000A6F2100000AD7282F0000062A8E0202282000000A6F2200000A283700000626020328370000062602030428490000062A6A0203283700000626020428370000062602030405284A0000062A00133003004E0000000D000011020328300000060A02030628390000060B0716281100000A281200000A2C2D12007C1B00000425710800000107283300000A8108000001120002282E0000067D1D0000040203062831000006062A52047B1B0000040203042839000006283300000A2A0013300200210000000E00001102282E000006047B1D000004DB0A0203284700000606281E00000A283800000A2A4A0202282000000A6F2200000A283B0000062A13300300210000000F000011020328300000060A0203062838000006022843000006283900000A282400000A2A4A0202282000000A6F2200000A283D0000062A13300300280000000F000011020328300000060A0203062838000006022843000006283900000A282400000A067B1C000004D72A1330030099000000100000110202282000000A6F2200000A28370000060A067B1B000004022843000006283900000A282400000A0B0207166AFE037211060070281700000A12007C1C000004254C07D75512007C1B000004257108000001022843000006283A00000A81080000010202282000000A6F2200000A0628310000060202282000000A6F2200000A07282800000A0C02086F2500000A724D060070281700000A2A4602280600000A72670500706F2E00000A2A4A02280600000A7267050070036F2F00000A2A4602280600000A72750500706F2E00000A2A4A02280600000A7275050070036F2F00000A2A4602280600000A72910500706F0B00000A2A4A02280600000A7291050070036F0C00000A2A4E02280600000A727F0500706F3000000A16912A6E02280600000A727F050070178D1A0000012516039C6F3100000A2A7202280600000A72A9050070038C07000001281600000A6F0B00000A2A7602280600000A72A9050070038C07000001281600000A046F0C00000A2A0013300400C2000000110000110416281100000A282D00000A2C38021201FE1510000002120102282000000A6F2200000A7D1E0000041201037D1F000004120116281100000A7D2000000407280C00002B172A0202282000000A6F2200000A28470000060A0604283200000A2C02162A0202282000000A6F2200000A0604282700000A284800000602030203284700000604283300000A2848000006021201FE1510000002120102282000000A6F2200000A7D1E0000041201037D1F0000041201047D2000000407280C00002B172A000013300500CF000000120000110516281100000A282D00000A2C2E021202FE15100000021202037D1E0000041202047D1F000004120216281100000A7D2000000408280C00002B172A020302282000000A6F2200000A284D0000060A020328470000060B0605283200000A2D090705283200000A2C02162A020302282000000A6F2200000A0605282700000A284C00000602030705282700000A284800000602040204284700000605283300000A2848000006021202FE15100000021202037D1E0000041202047D1F0000041202057D2000000408280C00002B172A00133004006A000000130000110202282000000A6F2200000A03284D00000604283400000A2C02162A0202282000000A6F2200000A0305284C000006021200FE1511000002120002282000000A6F2200000A7D210000041200037D220000041200057D240000041200047D2300000406280D00002B172A8E02280600000A72C1050070038C07000001048C07000001283500000A056F0C00000A2A8A02280600000A72C1050070038C07000001048C07000001283500000A6F0B00000A2A7202280600000A726F060070038C16000001281600000A6F0D00000A2A7602280600000A726F060070038C16000001281600000A046F0E00000A2A46729D060070038C18000001281600000A2A4E02280600000A020328500000066F0900000A2A5202280600000A02032850000006046F0A00000A2A4672B9060070038C18000001281600000A2A4E02280600000A020328530000066F0900000A2A5202280600000A02032853000006046F0A00000A2A7202280600000A72DB060070038C07000001281600000A6F0700000A2A7602280600000A72DB060070038C07000001281600000A046F0800000A2A8A02280600000A720B070070038C07000001048C07000001283500000A6F0D00000A2A8E02280600000A720B070070038C07000001048C07000001283500000A056F0E00000A2A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A72750500706F2E00000A2A4A02280600000A7275050070036F2F00000A2A4602280600000A72670500706F2E00000A2A4A02280600000A7267050070036F2F00000A2A4602280600000A723B0700706F0700000A2A4A02280600000A723B070070036F0800000A2A467253070070038C18000001281600000A2A4E02280600000A020328620000066F0700000A2A5202280600000A02032862000006046F0800000A2A4E02280600000A020328620000066F3B00000A2A467275070070038C18000001281600000A2A4E02280600000A020328660000066F0700000A2A5202280600000A02032866000006046F0800000A2A4E02280600000A020328660000066F3B00000A2A5E7297070070038C07000001048C18000001283500000A2A5202280600000A020304286A0000066F0700000A2A5602280600000A020304286A000006056F0800000A2A5202280600000A020304286A0000066F3B00000A2A5E72CF070070038C07000001048C18000001283500000A2A5202280600000A020304286E0000066F0700000A2A5602280600000A020304286E000006056F0800000A2A5202280600000A020304286E0000066F3B00000A2A4602280600000A72910500706F0700000A2A4A02280600000A7291050070036F0800000A2A001330030057000000000000000203281500000A021717284F000006021817284F000006021916284F000006021A17284F000006021B17284F0000060204285D0000060205285F0000060202282000000A6F2200000A285B00000602176A28610000062A720203022872000006FE057207080070281700000A020328630000062A7A020402032856000006FE057207080070281700000A020304286B0000062A32020304050E0428830000062A4202030405168D1A00000128830000062A00000013300300380000001400001102052889000006020528510000060A0206288F0000060204288F000006020603282B00000A7233080070281700000A02040528800000062A133004003A00000014000011020428880000060204288A000006020428510000060A020306282A00000A7233080070281700000A02040328550000060206030428860000062A9E0202282000000A6F2200000A030428590000060202282000000A6F2200000A030428870000062A3E0203288F000006020328560000062A0000133002001100000014000011020328510000060A0206288F000006062A3E0203288A000006020328540000062A2602030428580000062A00133004002900000014000011020428510000060A02042884000006020604288100000602030428820000060206030428850000062A000000133004007B00000015000011020204285100000603282B00000A7233080070281700000A020328560000060A020306176ADB285700000602280600000A020428500000066F3B00000A020304286F0000060B06176ADB0C07082E1B020308286B0000060D02030907287000000602030709286C000006020308286D00000602030428710000062A00133004004C0000001600001102020428510000067E2900000A282B00000A7233080070281700000A0204032852000006020328560000060A020306176AD72857000006060B02030407287000000602030704286C0000062A133008009D00000017000011020528890000060205288A000006020528510000060A020603282B00000A7233080070281700000A0204288F000006020405288000000602280600000A046F1800000A2C570204166A72510800701A8D0C000001251602282000000A6F2200000A8C07000001A22517038C07000001A22518058C18000001A225190E04A2166A282300000A0B02076F2600000AA5190000017233080070281700000A2A9A020328540000067E2900000A282A00000A2C1202280600000A020328530000066F3B00000A2A133003002800000018000011021200FE15120000021200037D250000041200047D260000041200057D2700000406280E00002B2A133003002800000019000011021200FE15130000021200037D280000041200047D290000041200057D2A00000406280F00002B2A13300300280000001A000011021200FE15140000021200037D2B0000041200047D2C0000041200057D2D00000406281000002B2A133004003C00000014000011020328510000060A020602282000000A6F2200000A282B00000A2D14020602282000000A6F2200000A28580000062B01177233080070281700000A2A133004005500000014000011020328510000060A020602282000000A6F2200000A282B00000A2D2D0203285400000602282000000A6F2200000A282B00000A2D14020602282000000A6F2200000A28580000062B01177233080070281700000A2A000000133002001000000014000011020328510000060A0206288F0000062A13300300390000001B00001102288C0000060203288F000006021200FE1515000002120002285A0000067D2E0000041200037D2F00000406281100002B0203285B0000062A8A0202282000000A6F2200000A02285A000006282B00000A7287080070281700000A2A133004007A0000001C00001102288C0000060203288F0000060204166AFE0372E3080070281700000A0228720000060A0604D70B0228600000060C2B2F020308288200000602060828640000060208062868000006027E2900000A0308288500000606176AD70A08176AD70C060737CD0202287200000604D72873000006020828610000062A000013300400850000001D000011020328510000060A020602282000000A6F2200000A282B00000A722F090070281700000A020328840000060206032881000006020328670000060B022872000006176ADB1304021104287300000611040C020828630000060D02070928640000060209072868000006020828650000060203286900000602067E2900000A0328850000062A5E02037E2900000A282A00000A7279090070281700000A2A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C0000007C140000237E0000E8140000C40C000023537472696E677300000000AC210000B409000023555300602B0000100000002347554944000000702B00001004000023426C6F620000000000000002000001571FA209090A000000FA013300160000010000001A000000150000002F0000008F000000C5000000020000003B00000003000000170000001D00000004000000170000002D00000001000000010000000300000010000000110000000000270401000000000006003B02630806007A026308060027021E070F00830800000A006A02B3090A001F0AB3090A00A309B3090A003C00B3090A00E901B3090A00600AB3090A006E0AB30906002D0A66040600AF0166040A005B02B30906009C0466040E001C002B080A001002B3090A004A03B3090600CA0B66040600EC0266040A00D706B3090600010066040A008301B3090600150066040600A1046604060098026604000000004400000000000100010001001000F409000019000100010001001000F8040000190001001E0001001000EA040000190001002E000100100023050000190001004E000A0110001E0B00003500010090000A011000F70000003500030090000A011000250600003500050090000A011000280300003500080090000A011000400300003500090090000A0110006D04000035000D00900002010000B90100003D00100090000A011000340300003500140090000A0110000A0300003500170090000A011000C80A000035001B0090000A0110003403000035001E0090000A0110000A0300003500210090000A011000340300003500250090000A0110000A0300003500280090000A0110001603000035002B0090000A011000F302000035002E0090000600A4085C02060034055C0206006C035C02060034055C020600D6005C020600E20A5C020600EF0A5F0206002F09210106005D0621010600D6005C020600EF0A5F020600B1005C020600460C63020600F50563020600CC0066020606590069025680F8046C025680EA046C02568023056C0206009204210106001F0621010600020B5F020600CB0621010600640621010600D80A5F020600020B5F02060031015F0206009E075C0206004D075C0206009204210106001F0621010600020B5F020600CB0621010600640621010600D80A5F020600020B5F0206009204210106001F0621010600A1005C020600CB0621010600E20021010600A1005C020600CB06210106000F0721010600E20066020600BA0621010600C806210150200000000086085B034A000100622000000000810868037002010075200000000086081A09AB00020087200000000081082B09750202009A20000000008608F108AB000300AC20000000008108000975020300BF200000000086083C09AB000400D1200000000081084E0975020400E42000000000860841017B020500F62000000000810852018002050009210000000086080605A70006001B210000000081081D05860206002E21000000008608C105A700070056210000000086088C06AB00070068210000000081089606750207007B21000000008608DC078B0208008D21000000008108EC0791020800A021000000008618180798020900B422000000008100000AAC0213003D2300000000C600BD02060018004823000000008600170BA7001800CC24000000008100BA0006001800B825000000008600CE07A70018001426000000008600D608A7001800F4260000000081000201B802180040270000000081001808BD0218007427000000008100CA09C4021900DC270000000081000808BD021A0028280000000081002206CD021B00CE280000000086181807D2021B0007290000000086083904DD02200019290000000081084404F00120002C290000000086089801DD0221003E29000000008108A101F0012100512900000000E609B308E10222006529000000008108C008E5022200812900000000E609660C7B0223009329000000008108760C80022300A62900000000E6016301EA022400C3290000000081006E01F1022500E42900000000E6010606F9022700B42A00000000E601770401032900902B00000000E601C5020B032C00062C000000008100D90315032F002A2C00000000E60179011F0332004D2C00000000860831074A0034005F2C0000000081083F0770023400722C000000008100BA0A280335008F2C000000008100C50A2F033600AD2C0000000086181807D2023800E62C00000000C600BD0206003D00EE2C000000008600780706003D00082D00000000E6010606F9023D002C2D00000000E601770401033F00482D000000008100AC0A28034200A22D0000000081005F0737034300B82D000000008100BE0737034500E52D000000008600B1074A004700F82D000000008600B10740034700252E0000000086008C074A004800382E0000000086008C07400348006C2E0000000086002E0B06004900112F0000000086083904DD024900232F0000000081084404F0014900362F0000000086089801DD024A00482F000000008108A101F0014A005B2F00000000E609660C7B024B006D2F000000008108760C80024B00802F00000000E609B308E1024C00942F000000008108C008E5024C00B02F00000000E6016301EA024D00CD2F0000000081006E01F1024E00EC2F0000000081001106F9025000BC30000000008100840401035200983100000000E601C5020B0355000E32000000008100D90315035800323200000000E60179011F035B005532000000008600130146035D0072320000000081004C084B035E0090320000000081000D0C51036000A232000000008100A00656036100B632000000008100AD065C036200CB32000000008100D00B51036400DD32000000008100AB0356036500F132000000008100BB035C03660006330000000081007E0A400368002333000000008100950A630369004133000000008100F2066A036B006433000000008100050772036D0088330000000086088C06AB0070009A33000000008108960675027000AD330000000086089801DD027100BF33000000008108A101F0017100D2330000000086083904DD027200E4330000000081084404F0017200F73300000000810889004A00730009340000000081089900700273001C340000000081001D0C510374002E34000000008100490B7B0375004234000000008100590B800376005734000000008100370B700278006B34000000008100FA0B510379007D340000000081009B057B037A009134000000008100AB0580037B00A634000000008100890570027D00BA34000000008100300C86037E00D234000000008100820B8D038000E734000000008100990B94038200FD34000000008100690B630385001235000000008100E30B860387002A350000000081005B058D0389003F35000000008100720594038B005535000000008100420563038E006A35000000008608660C4A0090007C35000000008108760C70029000903500000000861818079C039100F3350000000086005C0B7B03940010360000000086009C0B8D0395002F360000000086007304A40397003C360000000086007304AF039B0050360000000086007704AF039E009436000000008600C5026303A100DA360000000086000804B803A3000237000000008600CD024003A5001437000000008600D7025603A6003137000000008600DF005603A7004137000000008600E5036A03A8004C370000000081008E036303AA008437000000008100DC046303AC000C38000000008100C4046303AE0064380000000081007503A403B0000D39000000008100CB037002B40034390000000081007406AF03B50068390000000081009F03AF03B8009C39000000008100F6037203BB00D039000000008100D4017002BE00183A00000000810080067002BF007C3A000000008100CF047002C000983A00000000860036067502C100DD3A0000000081004A0C0600C200003B0000000086001A046303C200883B00000000860001067002C400193C000000008600AC0C7502C50000000100B70200000100B70200000100B70200000100B70200000100B70200000100B70200000100B70200000100B70200000100FD0100000200D10600000300C30100000400860C00000500AA01000006004F0400000700CD08000008000F0900000900600900000A00FC0700000100C30100000200860C00000300AA01000004004F0400000500CD0800000100230800000100230800000100230800000100FD0100000200860C00000300AA01000004004F0400000500CD0800000100B70200000100B70200000100B70200000100B70200000100AB0900000100AB0900000200B70200000100330600000200090B00000100970400000200330600000300090B000001006C0600000200FB0A00000300090B00000100D106000002006C0600000300B70200000100D106000002006C0600000100B70200000100AB0900000100AB0900000200D00A00000100210200000200860C00000300AA01000004004F0400000500CD0800000100330600000200090B00000100970400000200330600000300090B00000100AB0900000100AB0900000200D00A00000100AB0900000200D00A00000100AB0900000100AB0900000100B70200000100B70200000100B70200000100B70200000100AB0900000100AB0900000200B70200000100330600000200090B00000100970400000200330600000300090B000001006C0600000200FB0A00000300090B00000100D106000002006C0600000300B70200000100D106000002006C06000001004D00000001007D0000000200B70200000100F40000000100F40000000100F40000000200B70200000100F40000000100F40000000100F40000000200B70200000100AB0900000100AB0900000200B70200000100D106000002006E0900000100D106000002006E0900000300B70200000100B70200000100B70200000100B70200000100B70200000100B00B00000100B00B00000100B00B00000200BB0500000100B00B00000100BB0500000100BB0500000100BB0500000200B00B00000100BB0500000100AB0900000200B00B00000100AB0900000200B00B00000100D10600000200B00B00000300A90000000100D10600000200B00B00000100D10600000200A90000000100D10600000200A90000000100D10600000200A90000000300B00B00000100D10600000200A90000000100B70200000100210200000200AA01000003004F0400000100B00B00000100D10600000200B00B00000100970400000200330600000300A90000000400610000000100970400000200330600000300A90000000100970400000200330600000300A90000000100EB0000000200A900000001006E0900000200EB0000000100D10600000100A90000000100A90000000100D106000002006E0900000100330600000200A90000000100970400000200A90000000100330600000200A90000000100970400000200330600000300A90000000400610000000100A90000000100970400000200330600000300A90000000100D10600000200EB0000000300A90000000100D106000002006E0900000300EB0000000100A90000000100A90000000100A90000000100D10600000100AB0900000200090B00000100A90000000100AB090300410004004100090018070100110018070600190018070A002900180706007100180706003100DF011000890008001500890012001A008900950920008900A009260089002E002D00890039003300890056043A0089005E043F0031005103450091004E064A004100480A4E004100A90454008900BE0B5C008900C70B6900310018077C00A100ED0982003100100B88008900140A8E003100E3069400A900B60B99005100E508A70051007E09AB0031004603B0004100480ABC003100CD01C20031008C01E400B9009D024A00B9005906AB0031002204E9004100540AF5005900E508A7005900A702FB004100DA050401310083060D0139002E06210139009E0C25013900920C2501310025014A004100920C54008900DF0267018900E9026C0189009208720189009B0878014100B80454004100E905040141009E0C5400A100ED099E018900340AA50189003E0AB10141005A0C04014100CE0504014100E209040189004806F001090044004D0209004800520209004C0057022E000B00DF032E001300E8032E001B00070443002300520221012B00520281022B005202A1022B005202E1022B00520201032B005202C1032B005202E1032B00520221042B00520241042B005202A1042B005202C1042B005202E1042B00520201052B00520221052B00520241052B00520261052B00520281052B005202C1052B005202E1052B0052027000DB001501300135014001460152015B017F018B019401B901C001C401C901D101DD01E601F501FA01010206020D02170221022B0235023B020200010003000A0004000E000500130000006C03BF0300002F09C30300000409C30300005209C30300005601C80300002105CD030000C505CD030000CB06C3030000F007D10300004804D7030000A501D7030000C408DB0300007A0CC8030000C407BF0300004804D7030000A501D70300007A0CC8030000C408DB030000CB06C3030000A501D70300004804D70300009D00BF0300007A0CBF03020001000300010002000300020003000500010004000500020005000700010006000700020007000900010008000900020009000B0001000A000B0002000B000D0001000C000D0002000D000F0002000E00110001000F00110002001000130001001100130002001F001500010020001500020021001700010022001700020023001900010024001900020025001B00010026001B0002002E001D0001002F001D0002003F001F00010040001F0002004100210001004200210002004300230001004400230002004500250001004600250002005A00270001005B00270002005C00290001005D00290002005E002B0001005F002B00020060002D00010061002D00020072002F00010073002F002D010480000000000000000000000000000000001F0A000004000000000000000000000044026600000000000200000000000000000000000000B3090000000002000000000000000000000000002B0800000000060002000700020008000200090002000A0002000B0002000C0002000D0003000E0003000F000400100004001100040012000500130005001400050015000500270064003500A2003B00B7003F00CC003F00D1003F00D6003B00FF003B0086013B0099016D00AC016F00AC013B00D8013B00EB013B0012023B001C023B0026023B00300200000055496E7433320047657455496E7436340053657455496E74363400495374616E64617264546F6B656E3235360047657455496E743235360053657455496E74323536003C4D6F64756C653E00696E7465726661636549440076616C75655F5F00646174610053797374656D2E507269766174652E436F72654C696200696E746572666163654964006765745F4E657874546F6B656E4964007365745F4E657874546F6B656E496400746F6B656E496400526566756E64656400456E737572654B796356657269666965640049735265766F6B656400496E76657374656400476574417070726F76656400617070726F7665640069640053616C65506572696F640047657443757272656E74506572696F6400537570706F727473496E74657266616365006765745F42616C616E6365004469766964656E6442616C616E6365006765745F546F6B656E42616C616E6365007365745F546F6B656E42616C616E63650047657442616C616E63650053657442616C616E636500416C6C6F77616E636500494D657373616765006765745F4D657373616765006765745F4E616D65007365745F4E616D65006E616D650056616C75655479706500546F6B656E5479706500746F6B656E54797065004372656174650043616E4F706572617465006765745F53746174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E7453746174650073746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E646578417474726962757465004465706C6F794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650042797465006765745F56616C7565006765745F52657475726E56616C75650076616C7565005265636569766500417070726F76650042616C616E63654F66004F776E65724F6600476574537472696E6700536574537472696E67004F776E6572736869705472616E7366657265644C6F6700417070726F76616C4C6F6700417070726F76616C466F72416C6C4C6F670053544F53657475704C6F67005472616E736665724C6F6700496E766573744C6F670049426C6F636B006765745F426C6F636B006765745F456E64426C6F636B007365745F456E64426C6F636B00536166655472616E7366657246726F6D496E7465726E616C005472616E73666572496E7465726E616C004C6F67417070726F76616C004765744964546F417070726F76616C005365744964546F417070726F76616C00436C656172417070726F76616C00536574417070726F76616C004973417070726F766564466F72416C6C004C6F67417070726F76616C466F72416C6C00536574417070726F76616C466F72416C6C004D696E74416C6C0043616C6C00536D617274436F6E74726163742E646C6C006765745F53796D626F6C007365745F53796D626F6C0073796D626F6C00476574426F6F6C00536574426F6F6C0053797374656D00436C61696D00536166655472616E7366657246726F6D005472616E73666572546F6B656E7346726F6D0066726F6D00456E756D00426F6F6C65616E006F705F477265617465725468616E006F705F4C6573735468616E004164644E46546F6B656E0056616C69644E46546F6B656E0052656D6F76654E46546F6B656E004469766964656E64546F6B656E005374616E64617264546F6B656E006765745F49734E6F6E46756E6769626C65546F6B656E007365745F49734E6F6E46756E6769626C65546F6B656E005072696365506572546F6B656E00436C656172496E6465784F664F776E65724279546F6B656E00476574496E6465784F664F776E65724279546F6B656E00536574496E6465784F664F776E65724279546F6B656E00436C656172496E6465784279546F6B656E00476574496E6465784279546F6B656E00536574496E6465784279546F6B656E00746F6B656E006765745F53616C654F70656E006F705F4469766973696F6E006F705F5375627472616374696F6E006F705F4164646974696F6E004465736372697074696F6E004275726E005472616E73666572546F005472616E73666572546F6B656E73546F0047657453616C65496E666F005A65726F00746F005472616E736665724F776E65727368697000436C656172006765745F4E756D626572006765745F53656E646572005370656E646572007370656E646572004C6F675472616E736665720043616E5472616E73666572006765745F4F776E6572007365745F4F776E6572004765744964546F4F776E6572005365744964546F4F776E65720050726576696F75734F776E6572004E65774F776E6572006F776E6572004953657269616C697A6572006765745F53657269616C697A6572004765744F776E6572546F4F70657261746F72005365744F776E6572546F4F70657261746F72002E63746F720053797374656D2E446961676E6F7374696373006765745F4469766964656E6473007365745F4469766964656E64730043726564697465644469766964656E647300476574576974686472617761626C654469766964656E647300446973747269627574654469766964656E647300476574546F74616C4469766964656E64730057697468647261776E4469766964656E6473004765744469766964656E6473004765744E65774469766964656E647300576974686472617746756E6473006765745F53616C65506572696F6473007365745F53616C65506572696F64730073616C65506572696F64730056616C6964617465506572696F647300536574506572696F647300706572696F647300537472617469732E536D617274436F6E7472616374732E5374616E646172647300536574537570706F72746564496E74657266616365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004765744279746573005365744279746573004475726174696F6E426C6F636B73006765745F446563696D616C73007365745F446563696D616C7300646563696D616C73005769746864726177546F6B656E73006765745F53756363657373006765745F4B594341646472657373007365745F4B594341646472657373006B796341646472657373006765745F546F6B656E41646472657373007365745F546F6B656E41646472657373006765745F4D617070657241646472657373007365745F4D617070657241646472657373006D617070657241646472657373006F70657261746F7241646472657373006765745F4E6577436F6E74726163744164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300436F6E7665727453616C65506572696F64496E70757473006F705F4D6F64756C757300466F726D61740053544F436F6E747261637400437265617465546F6B656E436F6E7472616374004973436F6E747261637400536D617274436F6E7472616374004F626A6563740047657453747275637400536574537472756374006F705F496D706C69636974006F705F4578706C696369740049437265617465526573756C7400495472616E73666572526573756C74004765744F776E6572546F4E46546F6B656E436F756E74005365744F776E6572546F4E46546F6B656E436F756E74005570646174654163636F756E74004765744163636F756E74005365744163636F756E74006163636F756E74004F6C64416D6F756E7400526566756E64416D6F756E7400546F6B656E416D6F756E740063757272656E74416D6F756E7400616D6F756E740041737365727400496E766573740053616C65506572696F64496E70757400576974686472617700436C656172546F6B656E4279496E64657800476574546F6B656E4279496E64657800536574546F6B656E4279496E64657800436C656172546F6B656E4F664F776E65724279496E64657800476574546F6B656E4F664F776E65724279496E64657800536574546F6B656E4F664F776E65724279496E64657800696E64657800546F4172726179004765744172726179005365744172726179004765744964546F417070726F76616C4B657900496E6465784F664F776E65724279546F6B656E4B657900476574496E6465784279546F6B656E4B6579004765744964546F4F776E65724B657900476574546F6B656E4279496E6465784B657900476574546F6B656E4F664F776E65724279496E6465784B657900456E737572654F776E65724F6E6C79006F705F4D756C7469706C79006765745F546F74616C537570706C79007365745F546F74616C537570706C7900746F74616C537570706C79006F705F457175616C697479006F705F496E657175616C69747900456E737572654164647265737349734E6F74456D70747900001145006E00640042006C006F0063006B00001954006F006B0065006E00410064006400720065007300730000154B00590043004100640064007200650073007300001B4D00610070007000650072004100640064007200650073007300001954006F006B0065006E00420061006C0061006E00630065000025490073004E006F006E00460075006E006700690062006C00650054006F006B0065006E00000B4F0077006E00650072000017530061006C00650050006500720069006F0064007300005354006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006200650020006200650074007700650065006E0020003000200061006E006400200032002E00001374006F006B0065006E005400790070006500004354006800650020007B0030007D0020006900730020006E006F00740020006100200063006F006E007400720061006300740020006100640072006500730073002E0000156B00790063004100640064007200650073007300001B6D00610070007000650072004100640064007200650073007300003F4300720065006100740069006E006700200074006F006B0065006E00200063006F006E007400720061006300740020006600610069006C00650064002E00002B5400680065002000530054004F00200069007300200063006F006D0070006C0065007400650064002E00004B540068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000155400720061006E00730066006500720054006F00000F4D0069006E00740041006C006C00002D54006F006B0065006E0020007400720061006E00730066006500720020006600610069006C00650064002E00001D52006500660075006E00640020006600610069006C00650064002E0000274700650074005300650063006F006E0064006100720079004100640064007200650073007300001147006500740043006C00610069006D00003359006F007500720020004B005900430020006900730020006E006F0074002000760065007200690066006900650064002E00003754006800650020006100640064007200650073007300200068006100730020006E006F0020006D0061007000700069006E0067002E00004F4F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E0073006600650072002000660075006E00640073002E00002B530054004F0020006900730020006E006F007400200065006E0064006500640020007900650074002E00006F54006800650020007B0030007D0020006D006500740068006F00640020006900730020006E006F007400200073007500700070006F007200740065006400200066006F00720020004E006F006E002D00460075006E006700690062006C006500200054006F006B0065006E002E00011D5700690074006800640072006100770054006F006B0065006E00730000514F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E007300660065007200200074006F006B0065006E0073002E00004B50006C0065006100730065002000700072006F00760069006400650020006100740020006C006500610073007400200031002000730061006C006500200070006500720069006F006400004D4400750072006100740069006F006E0042006C006F0063006B0073002000730068006F0075006C006400200068006900670068006500720020007400680061006E0020007A00650072006F00000D530079006D0062006F006C0000094E0061006D006500001144006500630069006D0061006C007300001754006F00740061006C0053007500700070006C0079000017420061006C0061006E00630065003A007B0030007D00002341006C006C006F00770061006E00630065003A007B0030007D003A007B0031007D0000134400690076006900640065006E006400730000174100630063006F0075006E0074003A007B0030007D00003B54006800650020006100630063006F0075006E007400200068006100730020006E006F0020006400690076006900640065006E00640073002E0000215400720061006E00730066006500720020006600610069006C00650064002E00002D53007500700070006F00720074006500640049006E0074006500720066006100630065003A007B0030007D00001B4900640054006F004F0077006E00650072003A007B0030007D0000214900640054006F0041007000700072006F00760061006C003A007B0030007D00002F4F0077006E006500720054006F004E00460054006F006B0065006E0043006F0075006E0074003A007B0030007D00002F4F0077006E006500720054006F004F00700065007200610074006F0072003A007B0030007D003A007B0031007D0000174E0065007800740054006F006B0065006E0049006400002154006F006B0065006E004200790049006E006400650078003A007B0030007D00002149006E006400650078004200790054006F006B0065006E003A007B0030007D00003754006F006B0065006E004F0066004F0077006E00650072004200790049006E006400650078003A007B0030007D003A007B0031007D00003749006E006400650078004F0066004F0077006E00650072004200790054006F006B0065006E003A007B0030007D003A007B0031007D00002B540068006500200069006E00640065007800200069007300200069006E00760061006C00690064002E00001D41007300730065007200740020006600610069006C00650064002E0000354F006E004E006F006E00460075006E006700690062006C00650054006F006B0065006E0052006500630065006900760065006400005B4F006E006C00790020006F0077006E006500720020006F0066002000740068006500200063006F006E00740072006100630074002000630061006E00200073006500740020006E006500770020006F0077006E00650072002E00004B740068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000494F006E006C007900200074006F006B0065006E0020006F0077006E00650072002000630061006E0020006200750072006E002000740068006500200074006F006B0065006E002E000039540068006500200061006400640072006500730073002000630061006E0020006E006F00740020006200650020007A00650072006F002E000000C104C21B5AAEEE4E814637C6C61F342C0004200101080320000105200101111104200012450420010B0E052002010E0B052001111D0E062002010E111D05200111210E062002010E1121042001020E052002010E0204200012490320000B0500011121080700020211211121073001011D1E000E040A01111C062002010E124D0B07041D11181130122911240520010112250500020E0E1C05200201020E05200102111D0420001255083001011D1E001D05040A01111803200002042000111D06300101011E00040A01112405000111210B0930010312290B1D1C0B040A01120C040A011210040A0112140807031120122D1128042000125D0B2005122D111D0B0E1D1C0B0500010B11210320001C040A011128080002112111211121072002122D111D0B0B0705122D111D1C111D1D050306111D07000202111D111D021D05040701122D0A0704111C1D111C08111C0507011D111C0B07051D111C0B081118111C0807031D11180811180B0706111C0B11210B0B11200420010E0E052002010E0E0520011D050E062002010E1D0506070211211134040A0111340807031121112111340407011138040A0111380600030E0E1C1C063001011E000E040A01113C07300102010E1E00060702113C11210307010B040701113C070703113C0B122D06070211211140040A0111400807031121112111400407011144040A011144042001010E040701111D0607040B0B0B0B0407020B0B060702111D122D0407011148040A011148040701114C040A01114C0407011150040A0111500407011154040A0111540507030B0B0B080705111D0B0B0B0B087CEC85D7BEA7798E04000000000401000000040200000002060B0306112102060E02060202060903061130042001010B05200101111D042000112105200101112104200101020520001D111C062001011D111C13200A011225111D0911210E0E09111D111D1D050B20051229113011210E0E09042000111C062001011D11180820011D111C1D111804200011200A200501122511210E0E050320000E0320000504200101050620011121111D07200201111D112107200202111D112109200302111D111D112109200302111D1121112109200301111D111D11210820021121111D111D062001113C111D07200201111D113C0820021121111D113C0520010B111D04200102090520020109020420010E0B052001111D0B062002010B111D06200201111D0B07200202111D111D08200301111D111D020420010B0B052002010B0B0620020E111D0B0620020B111D0B07200301111D0B0B0720030112250E0E0A200401111D111D0B1D0508200301111D111D0B06200201111D020328000B042800111D0428001121032800020528001D111C0328000E032800050801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730108010002000000000000000000000000000000000010000000000000000000000000000000F86B00000000000000000000126C0000002000000000000000000000000000000000000000000000046C0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000C000000243C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ``` diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index 41a3e673..e2397412 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -314,7 +314,7 @@ public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply this.Decimals = decimals; this.SetBalance(Message.Sender, totalSupply); } - + public string Symbol { get => State.GetString(nameof(this.Symbol)); From a59eee5e779f70d8de179ce52fff9ec8ee3416de Mon Sep 17 00:00:00 2001 From: YakupIpek Date: Thu, 18 Feb 2021 00:22:12 +0300 Subject: [PATCH 12/12] Referenced contracts moved as seperate file --- Mainnet/STOContract/README.md | 4 +- .../STOContract/STOContract/DividendToken.cs | 304 +++++ .../STOContract/NonFungibleToken.cs | 629 ++++++++++ .../STOContract/STOContract/STOContract.cs | 1106 ----------------- .../STOContract/STOContract/StandardToken.cs | 166 +++ 5 files changed, 1101 insertions(+), 1108 deletions(-) create mode 100644 Mainnet/STOContract/STOContract/DividendToken.cs create mode 100644 Mainnet/STOContract/STOContract/NonFungibleToken.cs create mode 100644 Mainnet/STOContract/STOContract/StandardToken.cs diff --git a/Mainnet/STOContract/README.md b/Mainnet/STOContract/README.md index dbede22e..4fee3375 100644 --- a/Mainnet/STOContract/README.md +++ b/Mainnet/STOContract/README.md @@ -6,10 +6,10 @@ v2.0.0 ``` **Contract Hash** ``` -2b1db3edf5cc6fc4a5fcb9cec0b31350da20367c1cf635bcabf0012933c81b68 +6260cfd4c58e56ec35a8e7151dbe7737bda1c26b9c0f35a370aa45377eeb1ee6 ``` **Contract Byte Code** ``` -4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010200F94775F00000000000000000E00022200B013000004E00000002000000000000226C000000200000008000000000001000200000000200000400000000000000040000000000000000A000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000D06B00004F000000000000000000000000000000000000000000000000000000008000000C000000B46B00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000284C000000200000004E000000020000000000000000000000000000200000602E72656C6F6300000C000000008000000002000000500000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046C0000000000004800000002000500343C0000802F000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004602280600000A72010000706F0700000A2A4A02280600000A7201000070036F0800000A2A4602280600000A72130000706F0900000A2A4A02280600000A7213000070036F0A00000A2A4602280600000A722D0000706F0900000A2A4A02280600000A722D000070036F0A00000A2A4602280600000A72430000706F0900000A2A4A02280600000A7243000070036F0A00000A2A4602280600000A725F0000706F0B00000A2A4A02280600000A725F000070036F0C00000A2A4602280600000A72790000706F0D00000A2A4A02280600000A7279000070036F0E00000A2A9E02280100000602280F00000A6F1000000A371202280900000616281100000A281200000A2A162A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A72AB0000706F0100002B2A4A02280600000A72AB000070036F1400000A2A1330060006010000010000110203281500000A020519FE0572C30000707217010070281600000A281700000A0202280600000A0E086F1800000A722B010070726F010070281600000A281700000A0202280600000A0E096F1800000A722B0100707285010070281600000A281700000A02281900000A0E0A6F0200002B0A0206281C000006050B02070E040E050E060E0728130000060C02086F1B00000A72A1010070281700000A021203FE15090000021203086F1C00000A7D0800000409280300002B020E082806000006020E09280800000602086F1C00000A2804000006020718FE01280C0000060202280B0000062D040E042B07156A281E00000A280A0000060204280F0000060206281A0000062A0000133006007D00000000000000032C0603172E2F2B5A02166A1A8D0C0000012516048C08000001A2251705A225180E04A225190E058C16000001A2166A280400002B2A02166A1A8D0C0000012516048C08000001A2251705A225180E04A225190E058C16000001A2166A280500002B2A02166A188D0C000001251605A225170E04A2166A280600002B2A22022815000006262A00001330080075010000020000110202280D00000672E1010070281700000A0202282000000A6F2100000A166AFE03720D020070281700000A02281600000602281D0000060A02280B0000062D3E02022803000006166A7259020070188D0C000001251602282000000A6F2200000A8C07000001A22517067B070000048C08000001A2166A282300000A2B4102022803000006166A726F020070188D0C000001251602282000000A6F2200000A8C07000001A22517067B07000004282400000A8C18000001A2166A282300000A0B02076F2500000A2C0D076F2600000AA5190000012B0116727F020070281700000A021202FE150A000002120202282000000A6F2200000A7D090000041202067B050000047D0A0000041202067B070000047D0B0000041202067B060000047D0C00000408280700002B02022809000006067B07000004282700000A280A000006067B06000004166A36290202282000000A6F2200000A067B06000004282800000A0B02076F2500000A72AD020070281700000A172A00000013300800DD0000000300001102022807000006166A72CB020070178D0C000001251602282000000A6F2200000A8C07000001A2166A282300000A0A066F2500000A3996000000066F2600000A0C08750700000114FE03252D0B1203FE1507000001092B0608A5070000010B2C6F077E2900000A282A00000A2C6202022805000006166A72F3020070188D0C0000012516078C07000001A22517198C16000001A2166A282300000A0A02066F2500000A2C1F066F2600000A750100001B2513042C0F11042D03162B0911048E16FE032B01167205030070281700000A2A02167239030070281700000A2A000000133003004D000000000000000202282000000A6F2200000A02280E000006282B00000A7271030070281700000A0202280D00000616FE0172C1030070281700000A0202280E00000602282C00000A282800000A6F2500000A2A00000013300800D4000000040000110202280B00000616FE0172ED030070725D040070281600000A281700000A0202282000000A6F2200000A02280E000006282B00000A727B040070281700000A0202280D00000616FE0172C1030070281700000A02280900000616281100000A282D00000A2C02172A02022803000006166A7259020070188D0C000001251602282000000A6F2200000A8C07000001A225170228090000068C08000001A2166A282300000A0A0216281100000A280A00000602066F2500000A2C0D066F2600000AA5190000012B0116727F020070281700000A172A133002003E000000050000111200FE15070000020228100000060B160C2B230708A3070000020D097B0300000402280F00000A6F1000000A3704090A2B0A0817580C08078E6932D7062A00001330040026000000060000110203281B0000060A020628110000060206068E6917DA8F070000027B0300000428020000062A0000133004005C00000007000011038E698D070000020A02280F00000A6F1000000A0B160C2B3B0308A3060000020D07097B01000004D70B06081204FE15070000021204077D030000041204097B020000047D040000041104A4070000020817D60C08038E6932BF062A133003003E0000000800001102038E16FE0372CD040070281700000A030A160B2B210607A3060000020C02087B01000004166AFE037219050070281700000A0717580B07068E6932D92A0000133002009A000000090000110228190000060A02282000000A6F2100000A067B040000045C0B0228090000060C07281E00000A08281200000A2C4108282400000A067B04000004D90D02282000000A6F2100000A09DB13041205FE15080000021205097D05000004120511047D060000041205087D0700000411052A1205FE1508000002120502282000000A6F2100000A7D05000004120507281E00000A7D0700000411052AE20203281500000A0204282600000602052822000006020E042820000006020E0528240000060202282000000A6F2200000A0428280000062A4602280600000A72670500706F2E00000A2A4A02280600000A7267050070036F2F00000A2A4602280600000A72750500706F2E00000A2A4A02280600000A7275050070036F2F00000A2A4E02280600000A727F0500706F3000000A16912A6E02280600000A727F050070178D1A0000012516039C6F3100000A2A4602280600000A72910500706F0B00000A2A4A02280600000A7291050070036F0C00000A2A7202280600000A72A9050070038C07000001281600000A6F0B00000A2A7602280600000A72A9050070038C07000001281600000A046F0C00000A2A00000013300400C20000000A0000110416281100000A282D00000A2C38021201FE150D000002120102282000000A6F2200000A7D140000041201037D15000004120116281100000A7D1600000407280800002B172A0202282000000A6F2200000A28270000060A0604283200000A2C02162A0202282000000A6F2200000A0604282700000A282800000602030203282700000604283300000A2828000006021201FE150D000002120102282000000A6F2200000A7D140000041201037D150000041201047D1600000407280800002B172A000013300500CF0000000B0000110516281100000A282D00000A2C2E021202FE150D0000021202037D140000041202047D15000004120216281100000A7D1600000408280800002B172A020302282000000A6F2200000A282D0000060A020328270000060B0605283200000A2D090705283200000A2C02162A020302282000000A6F2200000A0605282700000A282C00000602030705282700000A282800000602040204282700000605283300000A2828000006021202FE150D0000021202037D140000041202047D150000041202057D1600000408280800002B172A00133004006A0000000C0000110202282000000A6F2200000A03282D00000604283400000A2C02162A0202282000000A6F2200000A0305282C000006021200FE150E000002120002282000000A6F2200000A7D170000041200037D180000041200057D1A0000041200047D1900000406280900002B172A8E02280600000A72C1050070038C07000001048C07000001283500000A056F0C00000A2A8A02280600000A72C1050070038C07000001048C07000001283500000A6F0B00000A2A4602280600000A72E50500706F0700000A2A4A02280600000A72E5050070036F0800000A2A7202280600000A72F9050070038C07000001281600000A6F0A00002B2A7602280600000A72F9050070038C07000001281600000A046F0B00002B2AE20203281500000A0204284400000602052842000006020E042840000006020E0528460000060202282000000A6F2200000A0428480000062A1E0228340000062A660202282E00000602282000000A6F2100000AD7282F0000062A8E0202282000000A6F2200000A283700000626020328370000062602030428490000062A6A0203283700000626020428370000062602030405284A0000062A00133003004E0000000D000011020328300000060A02030628390000060B0716281100000A281200000A2C2D12007C1B00000425710800000107283300000A8108000001120002282E0000067D1D0000040203062831000006062A52047B1B0000040203042839000006283300000A2A0013300200210000000E00001102282E000006047B1D000004DB0A0203284700000606281E00000A283800000A2A4A0202282000000A6F2200000A283B0000062A13300300210000000F000011020328300000060A0203062838000006022843000006283900000A282400000A2A4A0202282000000A6F2200000A283D0000062A13300300280000000F000011020328300000060A0203062838000006022843000006283900000A282400000A067B1C000004D72A1330030099000000100000110202282000000A6F2200000A28370000060A067B1B000004022843000006283900000A282400000A0B0207166AFE037211060070281700000A12007C1C000004254C07D75512007C1B000004257108000001022843000006283A00000A81080000010202282000000A6F2200000A0628310000060202282000000A6F2200000A07282800000A0C02086F2500000A724D060070281700000A2A4602280600000A72670500706F2E00000A2A4A02280600000A7267050070036F2F00000A2A4602280600000A72750500706F2E00000A2A4A02280600000A7275050070036F2F00000A2A4602280600000A72910500706F0B00000A2A4A02280600000A7291050070036F0C00000A2A4E02280600000A727F0500706F3000000A16912A6E02280600000A727F050070178D1A0000012516039C6F3100000A2A7202280600000A72A9050070038C07000001281600000A6F0B00000A2A7602280600000A72A9050070038C07000001281600000A046F0C00000A2A0013300400C2000000110000110416281100000A282D00000A2C38021201FE1510000002120102282000000A6F2200000A7D1E0000041201037D1F000004120116281100000A7D2000000407280C00002B172A0202282000000A6F2200000A28470000060A0604283200000A2C02162A0202282000000A6F2200000A0604282700000A284800000602030203284700000604283300000A2848000006021201FE1510000002120102282000000A6F2200000A7D1E0000041201037D1F0000041201047D2000000407280C00002B172A000013300500CF000000120000110516281100000A282D00000A2C2E021202FE15100000021202037D1E0000041202047D1F000004120216281100000A7D2000000408280C00002B172A020302282000000A6F2200000A284D0000060A020328470000060B0605283200000A2D090705283200000A2C02162A020302282000000A6F2200000A0605282700000A284C00000602030705282700000A284800000602040204284700000605283300000A2848000006021202FE15100000021202037D1E0000041202047D1F0000041202057D2000000408280C00002B172A00133004006A000000130000110202282000000A6F2200000A03284D00000604283400000A2C02162A0202282000000A6F2200000A0305284C000006021200FE1511000002120002282000000A6F2200000A7D210000041200037D220000041200057D240000041200047D2300000406280D00002B172A8E02280600000A72C1050070038C07000001048C07000001283500000A056F0C00000A2A8A02280600000A72C1050070038C07000001048C07000001283500000A6F0B00000A2A7202280600000A726F060070038C16000001281600000A6F0D00000A2A7602280600000A726F060070038C16000001281600000A046F0E00000A2A46729D060070038C18000001281600000A2A4E02280600000A020328500000066F0900000A2A5202280600000A02032850000006046F0A00000A2A4672B9060070038C18000001281600000A2A4E02280600000A020328530000066F0900000A2A5202280600000A02032853000006046F0A00000A2A7202280600000A72DB060070038C07000001281600000A6F0700000A2A7602280600000A72DB060070038C07000001281600000A046F0800000A2A8A02280600000A720B070070038C07000001048C07000001283500000A6F0D00000A2A8E02280600000A720B070070038C07000001048C07000001283500000A056F0E00000A2A4602280600000A729F0000706F0900000A2A4A02280600000A729F000070036F0A00000A2A4602280600000A72750500706F2E00000A2A4A02280600000A7275050070036F2F00000A2A4602280600000A72670500706F2E00000A2A4A02280600000A7267050070036F2F00000A2A4602280600000A723B0700706F0700000A2A4A02280600000A723B070070036F0800000A2A467253070070038C18000001281600000A2A4E02280600000A020328620000066F0700000A2A5202280600000A02032862000006046F0800000A2A4E02280600000A020328620000066F3B00000A2A467275070070038C18000001281600000A2A4E02280600000A020328660000066F0700000A2A5202280600000A02032866000006046F0800000A2A4E02280600000A020328660000066F3B00000A2A5E7297070070038C07000001048C18000001283500000A2A5202280600000A020304286A0000066F0700000A2A5602280600000A020304286A000006056F0800000A2A5202280600000A020304286A0000066F3B00000A2A5E72CF070070038C07000001048C18000001283500000A2A5202280600000A020304286E0000066F0700000A2A5602280600000A020304286E000006056F0800000A2A5202280600000A020304286E0000066F3B00000A2A4602280600000A72910500706F0700000A2A4A02280600000A7291050070036F0800000A2A001330030057000000000000000203281500000A021717284F000006021817284F000006021916284F000006021A17284F000006021B17284F0000060204285D0000060205285F0000060202282000000A6F2200000A285B00000602176A28610000062A720203022872000006FE057207080070281700000A020328630000062A7A020402032856000006FE057207080070281700000A020304286B0000062A32020304050E0428830000062A4202030405168D1A00000128830000062A00000013300300380000001400001102052889000006020528510000060A0206288F0000060204288F000006020603282B00000A7233080070281700000A02040528800000062A133004003A00000014000011020428880000060204288A000006020428510000060A020306282A00000A7233080070281700000A02040328550000060206030428860000062A9E0202282000000A6F2200000A030428590000060202282000000A6F2200000A030428870000062A3E0203288F000006020328560000062A0000133002001100000014000011020328510000060A0206288F000006062A3E0203288A000006020328540000062A2602030428580000062A00133004002900000014000011020428510000060A02042884000006020604288100000602030428820000060206030428850000062A000000133004007B00000015000011020204285100000603282B00000A7233080070281700000A020328560000060A020306176ADB285700000602280600000A020428500000066F3B00000A020304286F0000060B06176ADB0C07082E1B020308286B0000060D02030907287000000602030709286C000006020308286D00000602030428710000062A00133004004C0000001600001102020428510000067E2900000A282B00000A7233080070281700000A0204032852000006020328560000060A020306176AD72857000006060B02030407287000000602030704286C0000062A133008009D00000017000011020528890000060205288A000006020528510000060A020603282B00000A7233080070281700000A0204288F000006020405288000000602280600000A046F1800000A2C570204166A72510800701A8D0C000001251602282000000A6F2200000A8C07000001A22517038C07000001A22518058C18000001A225190E04A2166A282300000A0B02076F2600000AA5190000017233080070281700000A2A9A020328540000067E2900000A282A00000A2C1202280600000A020328530000066F3B00000A2A133003002800000018000011021200FE15120000021200037D250000041200047D260000041200057D2700000406280E00002B2A133003002800000019000011021200FE15130000021200037D280000041200047D290000041200057D2A00000406280F00002B2A13300300280000001A000011021200FE15140000021200037D2B0000041200047D2C0000041200057D2D00000406281000002B2A133004003C00000014000011020328510000060A020602282000000A6F2200000A282B00000A2D14020602282000000A6F2200000A28580000062B01177233080070281700000A2A133004005500000014000011020328510000060A020602282000000A6F2200000A282B00000A2D2D0203285400000602282000000A6F2200000A282B00000A2D14020602282000000A6F2200000A28580000062B01177233080070281700000A2A000000133002001000000014000011020328510000060A0206288F0000062A13300300390000001B00001102288C0000060203288F000006021200FE1515000002120002285A0000067D2E0000041200037D2F00000406281100002B0203285B0000062A8A0202282000000A6F2200000A02285A000006282B00000A7287080070281700000A2A133004007A0000001C00001102288C0000060203288F0000060204166AFE0372E3080070281700000A0228720000060A0604D70B0228600000060C2B2F020308288200000602060828640000060208062868000006027E2900000A0308288500000606176AD70A08176AD70C060737CD0202287200000604D72873000006020828610000062A000013300400850000001D000011020328510000060A020602282000000A6F2200000A282B00000A722F090070281700000A020328840000060206032881000006020328670000060B022872000006176ADB1304021104287300000611040C020828630000060D02070928640000060209072868000006020828650000060203286900000602067E2900000A0328850000062A5E02037E2900000A282A00000A7279090070281700000A2A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C0000007C140000237E0000E8140000C40C000023537472696E677300000000AC210000B409000023555300602B0000100000002347554944000000702B00001004000023426C6F620000000000000002000001571FA209090A000000FA013300160000010000001A000000150000002F0000008F000000C5000000020000003B00000003000000170000001D00000004000000170000002D00000001000000010000000300000010000000110000000000270401000000000006003B02630806007A026308060027021E070F00830800000A006A02B3090A001F0AB3090A00A309B3090A003C00B3090A00E901B3090A00600AB3090A006E0AB30906002D0A66040600AF0166040A005B02B30906009C0466040E001C002B080A001002B3090A004A03B3090600CA0B66040600EC0266040A00D706B3090600010066040A008301B3090600150066040600A1046604060098026604000000004400000000000100010001001000F409000019000100010001001000F8040000190001001E0001001000EA040000190001002E000100100023050000190001004E000A0110001E0B00003500010090000A011000F70000003500030090000A011000250600003500050090000A011000280300003500080090000A011000400300003500090090000A0110006D04000035000D00900002010000B90100003D00100090000A011000340300003500140090000A0110000A0300003500170090000A011000C80A000035001B0090000A0110003403000035001E0090000A0110000A0300003500210090000A011000340300003500250090000A0110000A0300003500280090000A0110001603000035002B0090000A011000F302000035002E0090000600A4085C02060034055C0206006C035C02060034055C020600D6005C020600E20A5C020600EF0A5F0206002F09210106005D0621010600D6005C020600EF0A5F020600B1005C020600460C63020600F50563020600CC0066020606590069025680F8046C025680EA046C02568023056C0206009204210106001F0621010600020B5F020600CB0621010600640621010600D80A5F020600020B5F02060031015F0206009E075C0206004D075C0206009204210106001F0621010600020B5F020600CB0621010600640621010600D80A5F020600020B5F0206009204210106001F0621010600A1005C020600CB0621010600E20021010600A1005C020600CB06210106000F0721010600E20066020600BA0621010600C806210150200000000086085B034A000100622000000000810868037002010075200000000086081A09AB00020087200000000081082B09750202009A20000000008608F108AB000300AC20000000008108000975020300BF200000000086083C09AB000400D1200000000081084E0975020400E42000000000860841017B020500F62000000000810852018002050009210000000086080605A70006001B210000000081081D05860206002E21000000008608C105A700070056210000000086088C06AB00070068210000000081089606750207007B21000000008608DC078B0208008D21000000008108EC0791020800A021000000008618180798020900B422000000008100000AAC0213003D2300000000C600BD02060018004823000000008600170BA7001800CC24000000008100BA0006001800B825000000008600CE07A70018001426000000008600D608A7001800F4260000000081000201B802180040270000000081001808BD0218007427000000008100CA09C4021900DC270000000081000808BD021A0028280000000081002206CD021B00CE280000000086181807D2021B0007290000000086083904DD02200019290000000081084404F00120002C290000000086089801DD0221003E29000000008108A101F0012100512900000000E609B308E10222006529000000008108C008E5022200812900000000E609660C7B0223009329000000008108760C80022300A62900000000E6016301EA022400C3290000000081006E01F1022500E42900000000E6010606F9022700B42A00000000E601770401032900902B00000000E601C5020B032C00062C000000008100D90315032F002A2C00000000E60179011F0332004D2C00000000860831074A0034005F2C0000000081083F0770023400722C000000008100BA0A280335008F2C000000008100C50A2F033600AD2C0000000086181807D2023800E62C00000000C600BD0206003D00EE2C000000008600780706003D00082D00000000E6010606F9023D002C2D00000000E601770401033F00482D000000008100AC0A28034200A22D0000000081005F0737034300B82D000000008100BE0737034500E52D000000008600B1074A004700F82D000000008600B10740034700252E0000000086008C074A004800382E0000000086008C07400348006C2E0000000086002E0B06004900112F0000000086083904DD024900232F0000000081084404F0014900362F0000000086089801DD024A00482F000000008108A101F0014A005B2F00000000E609660C7B024B006D2F000000008108760C80024B00802F00000000E609B308E1024C00942F000000008108C008E5024C00B02F00000000E6016301EA024D00CD2F0000000081006E01F1024E00EC2F0000000081001106F9025000BC30000000008100840401035200983100000000E601C5020B0355000E32000000008100D90315035800323200000000E60179011F035B005532000000008600130146035D0072320000000081004C084B035E0090320000000081000D0C51036000A232000000008100A00656036100B632000000008100AD065C036200CB32000000008100D00B51036400DD32000000008100AB0356036500F132000000008100BB035C03660006330000000081007E0A400368002333000000008100950A630369004133000000008100F2066A036B006433000000008100050772036D0088330000000086088C06AB0070009A33000000008108960675027000AD330000000086089801DD027100BF33000000008108A101F0017100D2330000000086083904DD027200E4330000000081084404F0017200F73300000000810889004A00730009340000000081089900700273001C340000000081001D0C510374002E34000000008100490B7B0375004234000000008100590B800376005734000000008100370B700278006B34000000008100FA0B510379007D340000000081009B057B037A009134000000008100AB0580037B00A634000000008100890570027D00BA34000000008100300C86037E00D234000000008100820B8D038000E734000000008100990B94038200FD34000000008100690B630385001235000000008100E30B860387002A350000000081005B058D0389003F35000000008100720594038B005535000000008100420563038E006A35000000008608660C4A0090007C35000000008108760C70029000903500000000861818079C039100F3350000000086005C0B7B03940010360000000086009C0B8D0395002F360000000086007304A40397003C360000000086007304AF039B0050360000000086007704AF039E009436000000008600C5026303A100DA360000000086000804B803A3000237000000008600CD024003A5001437000000008600D7025603A6003137000000008600DF005603A7004137000000008600E5036A03A8004C370000000081008E036303AA008437000000008100DC046303AC000C38000000008100C4046303AE0064380000000081007503A403B0000D39000000008100CB037002B40034390000000081007406AF03B50068390000000081009F03AF03B8009C39000000008100F6037203BB00D039000000008100D4017002BE00183A00000000810080067002BF007C3A000000008100CF047002C000983A00000000860036067502C100DD3A0000000081004A0C0600C200003B0000000086001A046303C200883B00000000860001067002C400193C000000008600AC0C7502C50000000100B70200000100B70200000100B70200000100B70200000100B70200000100B70200000100B70200000100B70200000100FD0100000200D10600000300C30100000400860C00000500AA01000006004F0400000700CD08000008000F0900000900600900000A00FC0700000100C30100000200860C00000300AA01000004004F0400000500CD0800000100230800000100230800000100230800000100FD0100000200860C00000300AA01000004004F0400000500CD0800000100B70200000100B70200000100B70200000100B70200000100AB0900000100AB0900000200B70200000100330600000200090B00000100970400000200330600000300090B000001006C0600000200FB0A00000300090B00000100D106000002006C0600000300B70200000100D106000002006C0600000100B70200000100AB0900000100AB0900000200D00A00000100210200000200860C00000300AA01000004004F0400000500CD0800000100330600000200090B00000100970400000200330600000300090B00000100AB0900000100AB0900000200D00A00000100AB0900000200D00A00000100AB0900000100AB0900000100B70200000100B70200000100B70200000100B70200000100AB0900000100AB0900000200B70200000100330600000200090B00000100970400000200330600000300090B000001006C0600000200FB0A00000300090B00000100D106000002006C0600000300B70200000100D106000002006C06000001004D00000001007D0000000200B70200000100F40000000100F40000000100F40000000200B70200000100F40000000100F40000000100F40000000200B70200000100AB0900000100AB0900000200B70200000100D106000002006E0900000100D106000002006E0900000300B70200000100B70200000100B70200000100B70200000100B70200000100B00B00000100B00B00000100B00B00000200BB0500000100B00B00000100BB0500000100BB0500000100BB0500000200B00B00000100BB0500000100AB0900000200B00B00000100AB0900000200B00B00000100D10600000200B00B00000300A90000000100D10600000200B00B00000100D10600000200A90000000100D10600000200A90000000100D10600000200A90000000300B00B00000100D10600000200A90000000100B70200000100210200000200AA01000003004F0400000100B00B00000100D10600000200B00B00000100970400000200330600000300A90000000400610000000100970400000200330600000300A90000000100970400000200330600000300A90000000100EB0000000200A900000001006E0900000200EB0000000100D10600000100A90000000100A90000000100D106000002006E0900000100330600000200A90000000100970400000200A90000000100330600000200A90000000100970400000200330600000300A90000000400610000000100A90000000100970400000200330600000300A90000000100D10600000200EB0000000300A90000000100D106000002006E0900000300EB0000000100A90000000100A90000000100A90000000100D10600000100AB0900000200090B00000100A90000000100AB090300410004004100090018070100110018070600190018070A002900180706007100180706003100DF011000890008001500890012001A008900950920008900A009260089002E002D00890039003300890056043A0089005E043F0031005103450091004E064A004100480A4E004100A90454008900BE0B5C008900C70B6900310018077C00A100ED0982003100100B88008900140A8E003100E3069400A900B60B99005100E508A70051007E09AB0031004603B0004100480ABC003100CD01C20031008C01E400B9009D024A00B9005906AB0031002204E9004100540AF5005900E508A7005900A702FB004100DA050401310083060D0139002E06210139009E0C25013900920C2501310025014A004100920C54008900DF0267018900E9026C0189009208720189009B0878014100B80454004100E905040141009E0C5400A100ED099E018900340AA50189003E0AB10141005A0C04014100CE0504014100E209040189004806F001090044004D0209004800520209004C0057022E000B00DF032E001300E8032E001B00070443002300520221012B00520281022B005202A1022B005202E1022B00520201032B005202C1032B005202E1032B00520221042B00520241042B005202A1042B005202C1042B005202E1042B00520201052B00520221052B00520241052B00520261052B00520281052B005202C1052B005202E1052B0052027000DB001501300135014001460152015B017F018B019401B901C001C401C901D101DD01E601F501FA01010206020D02170221022B0235023B020200010003000A0004000E000500130000006C03BF0300002F09C30300000409C30300005209C30300005601C80300002105CD030000C505CD030000CB06C3030000F007D10300004804D7030000A501D7030000C408DB0300007A0CC8030000C407BF0300004804D7030000A501D70300007A0CC8030000C408DB030000CB06C3030000A501D70300004804D70300009D00BF0300007A0CBF03020001000300010002000300020003000500010004000500020005000700010006000700020007000900010008000900020009000B0001000A000B0002000B000D0001000C000D0002000D000F0002000E00110001000F00110002001000130001001100130002001F001500010020001500020021001700010022001700020023001900010024001900020025001B00010026001B0002002E001D0001002F001D0002003F001F00010040001F0002004100210001004200210002004300230001004400230002004500250001004600250002005A00270001005B00270002005C00290001005D00290002005E002B0001005F002B00020060002D00010061002D00020072002F00010073002F002D010480000000000000000000000000000000001F0A000004000000000000000000000044026600000000000200000000000000000000000000B3090000000002000000000000000000000000002B0800000000060002000700020008000200090002000A0002000B0002000C0002000D0003000E0003000F000400100004001100040012000500130005001400050015000500270064003500A2003B00B7003F00CC003F00D1003F00D6003B00FF003B0086013B0099016D00AC016F00AC013B00D8013B00EB013B0012023B001C023B0026023B00300200000055496E7433320047657455496E7436340053657455496E74363400495374616E64617264546F6B656E3235360047657455496E743235360053657455496E74323536003C4D6F64756C653E00696E7465726661636549440076616C75655F5F00646174610053797374656D2E507269766174652E436F72654C696200696E746572666163654964006765745F4E657874546F6B656E4964007365745F4E657874546F6B656E496400746F6B656E496400526566756E64656400456E737572654B796356657269666965640049735265766F6B656400496E76657374656400476574417070726F76656400617070726F7665640069640053616C65506572696F640047657443757272656E74506572696F6400537570706F727473496E74657266616365006765745F42616C616E6365004469766964656E6442616C616E6365006765745F546F6B656E42616C616E6365007365745F546F6B656E42616C616E63650047657442616C616E63650053657442616C616E636500416C6C6F77616E636500494D657373616765006765745F4D657373616765006765745F4E616D65007365745F4E616D65006E616D650056616C75655479706500546F6B656E5479706500746F6B656E54797065004372656174650043616E4F706572617465006765745F53746174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E7453746174650073746174650044656275676761626C6541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500496E646578417474726962757465004465706C6F794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650042797465006765745F56616C7565006765745F52657475726E56616C75650076616C7565005265636569766500417070726F76650042616C616E63654F66004F776E65724F6600476574537472696E6700536574537472696E67004F776E6572736869705472616E7366657265644C6F6700417070726F76616C4C6F6700417070726F76616C466F72416C6C4C6F670053544F53657475704C6F67005472616E736665724C6F6700496E766573744C6F670049426C6F636B006765745F426C6F636B006765745F456E64426C6F636B007365745F456E64426C6F636B00536166655472616E7366657246726F6D496E7465726E616C005472616E73666572496E7465726E616C004C6F67417070726F76616C004765744964546F417070726F76616C005365744964546F417070726F76616C00436C656172417070726F76616C00536574417070726F76616C004973417070726F766564466F72416C6C004C6F67417070726F76616C466F72416C6C00536574417070726F76616C466F72416C6C004D696E74416C6C0043616C6C00536D617274436F6E74726163742E646C6C006765745F53796D626F6C007365745F53796D626F6C0073796D626F6C00476574426F6F6C00536574426F6F6C0053797374656D00436C61696D00536166655472616E7366657246726F6D005472616E73666572546F6B656E7346726F6D0066726F6D00456E756D00426F6F6C65616E006F705F477265617465725468616E006F705F4C6573735468616E004164644E46546F6B656E0056616C69644E46546F6B656E0052656D6F76654E46546F6B656E004469766964656E64546F6B656E005374616E64617264546F6B656E006765745F49734E6F6E46756E6769626C65546F6B656E007365745F49734E6F6E46756E6769626C65546F6B656E005072696365506572546F6B656E00436C656172496E6465784F664F776E65724279546F6B656E00476574496E6465784F664F776E65724279546F6B656E00536574496E6465784F664F776E65724279546F6B656E00436C656172496E6465784279546F6B656E00476574496E6465784279546F6B656E00536574496E6465784279546F6B656E00746F6B656E006765745F53616C654F70656E006F705F4469766973696F6E006F705F5375627472616374696F6E006F705F4164646974696F6E004465736372697074696F6E004275726E005472616E73666572546F005472616E73666572546F6B656E73546F0047657453616C65496E666F005A65726F00746F005472616E736665724F776E65727368697000436C656172006765745F4E756D626572006765745F53656E646572005370656E646572007370656E646572004C6F675472616E736665720043616E5472616E73666572006765745F4F776E6572007365745F4F776E6572004765744964546F4F776E6572005365744964546F4F776E65720050726576696F75734F776E6572004E65774F776E6572006F776E6572004953657269616C697A6572006765745F53657269616C697A6572004765744F776E6572546F4F70657261746F72005365744F776E6572546F4F70657261746F72002E63746F720053797374656D2E446961676E6F7374696373006765745F4469766964656E6473007365745F4469766964656E64730043726564697465644469766964656E647300476574576974686472617761626C654469766964656E647300446973747269627574654469766964656E647300476574546F74616C4469766964656E64730057697468647261776E4469766964656E6473004765744469766964656E6473004765744E65774469766964656E647300576974686472617746756E6473006765745F53616C65506572696F6473007365745F53616C65506572696F64730073616C65506572696F64730056616C6964617465506572696F647300536574506572696F647300706572696F647300537472617469732E536D617274436F6E7472616374732E5374616E646172647300536574537570706F72746564496E74657266616365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004765744279746573005365744279746573004475726174696F6E426C6F636B73006765745F446563696D616C73007365745F446563696D616C7300646563696D616C73005769746864726177546F6B656E73006765745F53756363657373006765745F4B594341646472657373007365745F4B594341646472657373006B796341646472657373006765745F546F6B656E41646472657373007365745F546F6B656E41646472657373006765745F4D617070657241646472657373007365745F4D617070657241646472657373006D617070657241646472657373006F70657261746F7241646472657373006765745F4E6577436F6E74726163744164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300436F6E7665727453616C65506572696F64496E70757473006F705F4D6F64756C757300466F726D61740053544F436F6E747261637400437265617465546F6B656E436F6E7472616374004973436F6E747261637400536D617274436F6E7472616374004F626A6563740047657453747275637400536574537472756374006F705F496D706C69636974006F705F4578706C696369740049437265617465526573756C7400495472616E73666572526573756C74004765744F776E6572546F4E46546F6B656E436F756E74005365744F776E6572546F4E46546F6B656E436F756E74005570646174654163636F756E74004765744163636F756E74005365744163636F756E74006163636F756E74004F6C64416D6F756E7400526566756E64416D6F756E7400546F6B656E416D6F756E740063757272656E74416D6F756E7400616D6F756E740041737365727400496E766573740053616C65506572696F64496E70757400576974686472617700436C656172546F6B656E4279496E64657800476574546F6B656E4279496E64657800536574546F6B656E4279496E64657800436C656172546F6B656E4F664F776E65724279496E64657800476574546F6B656E4F664F776E65724279496E64657800536574546F6B656E4F664F776E65724279496E64657800696E64657800546F4172726179004765744172726179005365744172726179004765744964546F417070726F76616C4B657900496E6465784F664F776E65724279546F6B656E4B657900476574496E6465784279546F6B656E4B6579004765744964546F4F776E65724B657900476574546F6B656E4279496E6465784B657900476574546F6B656E4F664F776E65724279496E6465784B657900456E737572654F776E65724F6E6C79006F705F4D756C7469706C79006765745F546F74616C537570706C79007365745F546F74616C537570706C7900746F74616C537570706C79006F705F457175616C697479006F705F496E657175616C69747900456E737572654164647265737349734E6F74456D70747900001145006E00640042006C006F0063006B00001954006F006B0065006E00410064006400720065007300730000154B00590043004100640064007200650073007300001B4D00610070007000650072004100640064007200650073007300001954006F006B0065006E00420061006C0061006E00630065000025490073004E006F006E00460075006E006700690062006C00650054006F006B0065006E00000B4F0077006E00650072000017530061006C00650050006500720069006F0064007300005354006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006200650020006200650074007700650065006E0020003000200061006E006400200032002E00001374006F006B0065006E005400790070006500004354006800650020007B0030007D0020006900730020006E006F00740020006100200063006F006E007400720061006300740020006100640072006500730073002E0000156B00790063004100640064007200650073007300001B6D00610070007000650072004100640064007200650073007300003F4300720065006100740069006E006700200074006F006B0065006E00200063006F006E007400720061006300740020006600610069006C00650064002E00002B5400680065002000530054004F00200069007300200063006F006D0070006C0065007400650064002E00004B540068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000155400720061006E00730066006500720054006F00000F4D0069006E00740041006C006C00002D54006F006B0065006E0020007400720061006E00730066006500720020006600610069006C00650064002E00001D52006500660075006E00640020006600610069006C00650064002E0000274700650074005300650063006F006E0064006100720079004100640064007200650073007300001147006500740043006C00610069006D00003359006F007500720020004B005900430020006900730020006E006F0074002000760065007200690066006900650064002E00003754006800650020006100640064007200650073007300200068006100730020006E006F0020006D0061007000700069006E0067002E00004F4F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E0073006600650072002000660075006E00640073002E00002B530054004F0020006900730020006E006F007400200065006E0064006500640020007900650074002E00006F54006800650020007B0030007D0020006D006500740068006F00640020006900730020006E006F007400200073007500700070006F007200740065006400200066006F00720020004E006F006E002D00460075006E006700690062006C006500200054006F006B0065006E002E00011D5700690074006800640072006100770054006F006B0065006E00730000514F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E007300660065007200200074006F006B0065006E0073002E00004B50006C0065006100730065002000700072006F00760069006400650020006100740020006C006500610073007400200031002000730061006C006500200070006500720069006F006400004D4400750072006100740069006F006E0042006C006F0063006B0073002000730068006F0075006C006400200068006900670068006500720020007400680061006E0020007A00650072006F00000D530079006D0062006F006C0000094E0061006D006500001144006500630069006D0061006C007300001754006F00740061006C0053007500700070006C0079000017420061006C0061006E00630065003A007B0030007D00002341006C006C006F00770061006E00630065003A007B0030007D003A007B0031007D0000134400690076006900640065006E006400730000174100630063006F0075006E0074003A007B0030007D00003B54006800650020006100630063006F0075006E007400200068006100730020006E006F0020006400690076006900640065006E00640073002E0000215400720061006E00730066006500720020006600610069006C00650064002E00002D53007500700070006F00720074006500640049006E0074006500720066006100630065003A007B0030007D00001B4900640054006F004F0077006E00650072003A007B0030007D0000214900640054006F0041007000700072006F00760061006C003A007B0030007D00002F4F0077006E006500720054006F004E00460054006F006B0065006E0043006F0075006E0074003A007B0030007D00002F4F0077006E006500720054006F004F00700065007200610074006F0072003A007B0030007D003A007B0031007D0000174E0065007800740054006F006B0065006E0049006400002154006F006B0065006E004200790049006E006400650078003A007B0030007D00002149006E006400650078004200790054006F006B0065006E003A007B0030007D00003754006F006B0065006E004F0066004F0077006E00650072004200790049006E006400650078003A007B0030007D003A007B0031007D00003749006E006400650078004F0066004F0077006E00650072004200790054006F006B0065006E003A007B0030007D003A007B0031007D00002B540068006500200069006E00640065007800200069007300200069006E00760061006C00690064002E00001D41007300730065007200740020006600610069006C00650064002E0000354F006E004E006F006E00460075006E006700690062006C00650054006F006B0065006E0052006500630065006900760065006400005B4F006E006C00790020006F0077006E006500720020006F0066002000740068006500200063006F006E00740072006100630074002000630061006E00200073006500740020006E006500770020006F0077006E00650072002E00004B740068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000494F006E006C007900200074006F006B0065006E0020006F0077006E00650072002000630061006E0020006200750072006E002000740068006500200074006F006B0065006E002E000039540068006500200061006400640072006500730073002000630061006E0020006E006F00740020006200650020007A00650072006F002E000000C104C21B5AAEEE4E814637C6C61F342C0004200101080320000105200101111104200012450420010B0E052002010E0B052001111D0E062002010E111D05200111210E062002010E1121042001020E052002010E0204200012490320000B0500011121080700020211211121073001011D1E000E040A01111C062002010E124D0B07041D11181130122911240520010112250500020E0E1C05200201020E05200102111D0420001255083001011D1E001D05040A01111803200002042000111D06300101011E00040A01112405000111210B0930010312290B1D1C0B040A01120C040A011210040A0112140807031120122D1128042000125D0B2005122D111D0B0E1D1C0B0500010B11210320001C040A011128080002112111211121072002122D111D0B0B0705122D111D1C111D1D050306111D07000202111D111D021D05040701122D0A0704111C1D111C08111C0507011D111C0B07051D111C0B081118111C0807031D11180811180B0706111C0B11210B0B11200420010E0E052002010E0E0520011D050E062002010E1D0506070211211134040A0111340807031121112111340407011138040A0111380600030E0E1C1C063001011E000E040A01113C07300102010E1E00060702113C11210307010B040701113C070703113C0B122D06070211211140040A0111400807031121112111400407011144040A011144042001010E040701111D0607040B0B0B0B0407020B0B060702111D122D0407011148040A011148040701114C040A01114C0407011150040A0111500407011154040A0111540507030B0B0B080705111D0B0B0B0B087CEC85D7BEA7798E04000000000401000000040200000002060B0306112102060E02060202060903061130042001010B05200101111D042000112105200101112104200101020520001D111C062001011D111C13200A011225111D0911210E0E09111D111D1D050B20051229113011210E0E09042000111C062001011D11180820011D111C1D111804200011200A200501122511210E0E050320000E0320000504200101050620011121111D07200201111D112107200202111D112109200302111D111D112109200302111D1121112109200301111D111D11210820021121111D111D062001113C111D07200201111D113C0820021121111D113C0520010B111D04200102090520020109020420010E0B052001111D0B062002010B111D06200201111D0B07200202111D111D08200301111D111D020420010B0B052002010B0B0620020E111D0B0620020B111D0B07200301111D0B0B0720030112250E0E0A200401111D111D0B1D0508200301111D111D0B06200201111D020328000B042800111D0428001121032800020528001D111C0328000E032800050801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730108010002000000000000000000000000000000000010000000000000000000000000000000F86B00000000000000000000126C0000002000000000000000000000000000000000000000000000046C0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000C000000243C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0102001EA8B8B40000000000000000E00022200B013000004E00000002000000000000BE6D000000200000008000000000001000200000000200000400000000000000040000000000000000A0000000020000000000000300408500001000001000000000100000100000000000001000000000000000000000006C6D00004F000000000000000000000000000000000000000000000000000000008000000C000000506D00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000C44D000000200000004E000000020000000000000000000000000000200000602E72656C6F6300000C000000008000000002000000500000000000000000000000000000400000420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A06D0000000000004800000002000500343C00001C31000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004602280D00000A72010000706F0E00000A2A4A02280D00000A7201000070036F0F00000A2A7202280D00000A7215000070038C0E000001281000000A6F0100002B2A7602280D00000A7215000070038C0E000001281000000A046F0200002B2AE20203281300000A0204281700000602052815000006020E042813000006020E0528190000060202281400000A6F1500000A04281B0000062A1E0228070000062A660202280100000602281400000A6F1600000AD728020000062A8E0202281400000A6F1500000A280A000006260203280A00000626020304281C0000062A6A0203280A000006260204280A0000062602030405281D0000062A0000133003004E00000001000011020328030000060A020306280C0000060B0716281700000A281800000A2C2D12007C0100000425711000000107281900000A811000000112000228010000067D030000040203062804000006062A52047B01000004020304280C000006281900000A2A00133002002100000002000011022801000006047B03000004DB0A0203281A00000606281A00000A281B00000A2A4A0202281400000A6F1500000A280E0000062A133003002100000003000011020328030000060A020306280B000006022816000006281C00000A281D00000A2A4A0202281400000A6F1500000A28100000062A133003002800000003000011020328030000060A020306280B000006022816000006281C00000A281D00000A067B02000004D72A1330030099000000040000110202281400000A6F1500000A280A0000060A067B01000004022816000006281C00000A281D00000A0B0207166AFE03722D000070281E00000A12007C02000004254C07D75512007C01000004257110000001022816000006281F00000A81100000010202281400000A6F1500000A0628040000060202281400000A6F1500000A07282000000A0C02086F2100000A7269000070281E00000A2A4602280D00000A728B0000706F2200000A2A4A02280D00000A728B000070036F2300000A2A4602280D00000A72990000706F2200000A2A4A02280D00000A7299000070036F2300000A2A4602280D00000A72A30000706F2400000A2A4A02280D00000A72A3000070036F2500000A2A4E02280D00000A72BB0000706F2600000A16912A6E02280D00000A72BB000070178D1B0000012516039C6F2700000A2A7202280D00000A72CD000070038C0E000001281000000A6F2400000A2A7602280D00000A72CD000070038C0E000001281000000A046F2500000A2A0013300400C2000000050000110416281700000A282800000A2C38021201FE1507000002120102281400000A6F1500000A7D040000041201037D05000004120116281700000A7D0600000407280300002B172A0202281400000A6F1500000A281A0000060A0604282A00000A2C02162A0202281400000A6F1500000A0604282B00000A281B00000602030203281A00000604281900000A281B000006021201FE1507000002120102281400000A6F1500000A7D040000041201037D050000041201047D0600000407280300002B172A000013300500CF000000060000110516281700000A282800000A2C2E021202FE15070000021202037D040000041202047D05000004120216281700000A7D0600000408280300002B172A020302281400000A6F1500000A28200000060A0203281A0000060B0605282A00000A2D090705282A00000A2C02162A020302281400000A6F1500000A0605282B00000A281F00000602030705282B00000A281B00000602040204281A00000605281900000A281B000006021202FE15070000021202037D040000041202047D050000041202057D0600000408280300002B172A00133004006A000000070000110202281400000A6F1500000A03282000000604282C00000A2C02162A0202281400000A6F1500000A0305281F000006021200FE1508000002120002281400000A6F1500000A7D070000041200037D080000041200057D0A0000041200047D0900000406280400002B172A8E02280D00000A72E5000070038C0E000001048C0E000001282D00000A056F2500000A2A8A02280D00000A72E5000070038C0E000001048C0E000001282D00000A6F2400000A2A7202280D00000A7209010070038C1C000001281000000A6F2E00000A2A7602280D00000A7209010070038C1C000001281000000A046F2F00000A2A467237010070038C1D000001281000000A2A4E02280D00000A020328230000066F3000000A2A5202280D00000A02032823000006046F3100000A2A467253010070038C1D000001281000000A2A4E02280D00000A020328260000066F3000000A2A5202280D00000A02032826000006046F3100000A2A7202280D00000A7275010070038C0E000001281000000A6F0E00000A2A7602280D00000A7275010070038C0E000001281000000A046F0F00000A2A8A02280D00000A72A5010070038C0E000001048C0E000001282D00000A6F2E00000A2A8E02280D00000A72A5010070038C0E000001048C0E000001282D00000A056F2F00000A2A4602280D00000A72D50100706F3000000A2A4A02280D00000A72D5010070036F3100000A2A4602280D00000A72990000706F2200000A2A4A02280D00000A7299000070036F2300000A2A4602280D00000A728B0000706F2200000A2A4A02280D00000A728B000070036F2300000A2A4602280D00000A72E10100706F0E00000A2A4A02280D00000A72E1010070036F0F00000A2A4672F9010070038C1D000001281000000A2A4E02280D00000A020328350000066F0E00000A2A5202280D00000A02032835000006046F0F00000A2A4E02280D00000A020328350000066F3200000A2A46721B020070038C1D000001281000000A2A4E02280D00000A020328390000066F0E00000A2A5202280D00000A02032839000006046F0F00000A2A4E02280D00000A020328390000066F3200000A2A5E723D020070038C0E000001048C1D000001282D00000A2A5202280D00000A020304283D0000066F0E00000A2A5602280D00000A020304283D000006056F0F00000A2A5202280D00000A020304283D0000066F3200000A2A5E7275020070038C0E000001048C1D000001282D00000A2A5202280D00000A02030428410000066F0E00000A2A5602280D00000A0203042841000006056F0F00000A2A5202280D00000A02030428410000066F3200000A2A4602280D00000A72A30000706F0E00000A2A4A02280D00000A72A3000070036F0F00000A2A001330030057000000000000000203281300000A021717282200000602181728220000060219162822000006021A172822000006021B17282200000602042830000006020528320000060202281400000A6F1500000A282E00000602176A28340000062A720203022845000006FE0572AD020070281E00000A020328360000062A7A020402032829000006FE0572AD020070281E00000A020304283E0000062A32020304050E0428560000062A4202030405168D1B00000128560000062A0000001330030038000000080000110205285C000006020528240000060A0206286200000602042862000006020603283300000A72D9020070281E00000A02040528530000062A133004003A000000080000110204285B0000060204285D000006020428240000060A020306283400000A72D9020070281E00000A02040328280000060206030428590000062A9E0202281400000A6F1500000A0304282C0000060202281400000A6F1500000A0304285A0000062A3E02032862000006020328290000062A0000133002001100000008000011020328240000060A02062862000006062A3E0203285D000006020328270000062A26020304282B0000062A00133004002900000008000011020428240000060A02042857000006020604285400000602030428550000060206030428580000062A000000133004007B00000009000011020204282400000603283300000A72D9020070281E00000A020328290000060A020306176ADB282A00000602280D00000A020428230000066F3200000A02030428420000060B06176ADB0C07082E1B020308283E0000060D02030907284300000602030709283F000006020308284000000602030428440000062A00133004004C0000000A00001102020428240000067E3500000A283300000A72D9020070281E00000A0204032825000006020328290000060A020306176AD7282A000006060B02030407284300000602030704283F0000062A133008009D0000000B0000110205285C0000060205285D000006020528240000060A020603283300000A72D9020070281E00000A02042862000006020405285300000602280D00000A046F3600000A2C570204166A72F70200701A8D16000001251602281400000A6F1500000A8C0E000001A22517038C0E000001A22518058C1D000001A225190E04A2166A283700000A0B02076F3800000AA51E00000172D9020070281E00000A2A9A020328270000067E3500000A283400000A2C1202280D00000A020328260000066F3200000A2A13300300280000000C000011021200FE15090000021200037D0B0000041200047D0C0000041200057D0D00000406280500002B2A13300300280000000D000011021200FE150A0000021200037D0E0000041200047D0F0000041200057D1000000406280600002B2A13300300280000000E000011021200FE150B0000021200037D110000041200047D120000041200057D1300000406280700002B2A133004003C00000008000011020328240000060A020602281400000A6F1500000A283300000A2D14020602281400000A6F1500000A282B0000062B011772D9020070281E00000A2A133004005500000008000011020328240000060A020602281400000A6F1500000A283300000A2D2D0203282700000602281400000A6F1500000A283300000A2D14020602281400000A6F1500000A282B0000062B011772D9020070281E00000A2A000000133002001000000008000011020328240000060A020628620000062A13300300390000000F00001102285F00000602032862000006021200FE150C000002120002282D0000067D140000041200037D1500000406280800002B0203282E0000062A8A0202281400000A6F1500000A02282D000006283300000A722D030070281E00000A2A133004007A0000001000001102285F000006020328620000060204166AFE037289030070281E00000A0228450000060A0604D70B0228330000060C2B2F02030828550000060206082837000006020806283B000006027E3500000A0308285800000606176AD70A08176AD70C060737CD0202284500000604D72846000006020828340000062A0000133004008500000011000011020328240000060A020602281400000A6F1500000A283300000A72D5030070281E00000A0203285700000602060328540000060203283A0000060B022845000006176ADB1304021104284600000611040C020828360000060D0207092837000006020907283B000006020828380000060203283C00000602067E3500000A0328580000062A5E02037E3500000A283400000A721F040070281E00000A2AE20203281300000A0204286B00000602052867000006020E042865000006020E0528690000060202281400000A6F1500000A04286D0000062A4602280D00000A728B0000706F2200000A2A4A02280D00000A728B000070036F2300000A2A4602280D00000A72990000706F2200000A2A4A02280D00000A7299000070036F2300000A2A4E02280D00000A72BB0000706F2600000A16912A6E02280D00000A72BB000070178D1B0000012516039C6F2700000A2A4602280D00000A72A30000706F2400000A2A4A02280D00000A72A3000070036F2500000A2A7202280D00000A72CD000070038C0E000001281000000A6F2400000A2A7602280D00000A72CD000070038C0E000001281000000A046F2500000A2A13300400C2000000120000110416281700000A282800000A2C38021201FE150D000002120102281400000A6F1500000A7D160000041201037D17000004120116281700000A7D1800000407280900002B172A0202281400000A6F1500000A286C0000060A0604282A00000A2C02162A0202281400000A6F1500000A0604282B00000A286D00000602030203286C00000604281900000A286D000006021201FE150D000002120102281400000A6F1500000A7D160000041201037D170000041201047D1800000407280900002B172A000013300500CF000000130000110516281700000A282800000A2C2E021202FE150D0000021202037D160000041202047D17000004120216281700000A7D1800000408280900002B172A020302281400000A6F1500000A28720000060A0203286C0000060B0605282A00000A2D090705282A00000A2C02162A020302281400000A6F1500000A0605282B00000A287100000602030705282B00000A286D00000602040204286C00000605281900000A286D000006021202FE150D0000021202037D160000041202047D170000041202057D1800000408280900002B172A00133004006A000000140000110202281400000A6F1500000A03287200000604282C00000A2C02162A0202281400000A6F1500000A03052871000006021200FE150E000002120002281400000A6F1500000A7D190000041200037D1A0000041200057D1C0000041200047D1B00000406280A00002B172A8E02280D00000A72E5000070038C0E000001048C0E000001282D00000A056F2500000A2A8A02280D00000A72E5000070038C0E000001048C0E000001282D00000A6F2400000A2A4602280D00000A72590400706F0E00000A2A4A02280D00000A7259040070036F0F00000A2A4602280D00000A726B0400706F3000000A2A4A02280D00000A726B040070036F3100000A2A4602280D00000A72850400706F3000000A2A4A02280D00000A7285040070036F3100000A2A4602280D00000A729B0400706F3000000A2A4A02280D00000A729B040070036F3100000A2A4602280D00000A72B70400706F2400000A2A4A02280D00000A72B7040070036F2500000A2A4602280D00000A72D10400706F2E00000A2A4A02280D00000A72D1040070036F2F00000A2A9E02287300000602283900000A6F3A00000A371202287B00000616281700000A281800000A2A162A4602280D00000A72D50100706F3000000A2A4A02280D00000A72D5010070036F3100000A2A4602280D00000A72F70400706F0B00002B2A4A02280D00000A72F7040070036F3C00000A2A0000001330060006010000150000110203281300000A020519FE05720F0500707263050070281000000A281E00000A0202280D00000A0E086F3600000A727705007072BB050070281000000A281E00000A0202280D00000A0E096F3600000A727705007072D1050070281000000A281E00000A02283D00000A0E0A6F0C00002B0A0206288E000006050B02070E040E050E060E0728850000060C02086F3F00000A72ED050070281E00000A021203FE15120000021203086F4000000A7D2400000409280D00002B020E082878000006020E09287A00000602086F4000000A2876000006020718FE01287E0000060202287D0000062D040E042B07156A281A00000A287C000006020428810000060206288C0000062A0000133006007D00000000000000032C0603172E2F2B5A02166A1A8D160000012516048C10000001A2251705A225180E04A225190E058C1C000001A2166A280E00002B2A02166A1A8D160000012516048C10000001A2251705A225180E04A225190E058C1C000001A2166A280F00002B2A02166A188D16000001251605A225170E04A2166A281000002B2A22022887000006262A00001330080075010000160000110202287F000006722D060070281E00000A0202281400000A6F1600000A166AFE037259060070281E00000A02288800000602288F0000060A02287D0000062D3E02022875000006166A72A5060070188D16000001251602281400000A6F1500000A8C0E000001A22517067B230000048C10000001A2166A283700000A2B4102022875000006166A72BB060070188D16000001251602281400000A6F1500000A8C0E000001A22517067B23000004281D00000A8C1D000001A2166A283700000A0B02076F2100000A2C0D076F3800000AA51E0000012B011672CB060070281E00000A021202FE1513000002120202281400000A6F1500000A7D250000041202067B210000047D260000041202067B230000047D270000041202067B220000047D2800000408281100002B0202287B000006067B23000004282B00000A287C000006067B22000004166A36290202281400000A6F1500000A067B22000004282000000A0B02076F2100000A72F9060070281E00000A172A00000013300800DD0000001700001102022879000006166A7217070070178D16000001251602281400000A6F1500000A8C0E000001A2166A283700000A0A066F2100000A3996000000066F3800000A0C08750E00000114FE03252D0B1203FE150E000001092B0608A50E0000010B2C6F077E3500000A283400000A2C6202022877000006166A723F070070188D160000012516078C0E000001A22517198C1C000001A2166A283700000A0A02066F2100000A2C1F066F3800000A750100001B2513042C0F11042D03162B0911048E16FE032B01167251070070281E00000A2A02167285070070281E00000A2A000000133003004D000000000000000202281400000A6F1500000A022880000006283300000A72BD070070281E00000A0202287F00000616FE01720D080070281E00000A0202288000000602284200000A282000000A6F2100000A2A00000013300800D4000000180000110202287D00000616FE01723908007072A9080070281000000A281E00000A0202281400000A6F1500000A022880000006283300000A72C7080070281E00000A0202287F00000616FE01720D080070281E00000A02287B00000616281700000A282800000A2C02172A02022875000006166A72A5060070188D16000001251602281400000A6F1500000A8C0E000001A2251702287B0000068C10000001A2166A283700000A0A0216281700000A287C00000602066F2100000A2C0D066F3800000AA51E0000012B011672CB060070281E00000A172A133002003E000000190000111200FE15100000020228820000060B160C2B230708A3100000020D097B1F00000402283900000A6F3A00000A3704090A2B0A0817580C08078E6932D7062A000013300400260000001A0000110203288D0000060A020628830000060206068E6917DA8F100000027B1F00000428740000062A0000133004005C0000001B000011038E698D100000020A02283900000A6F3A00000A0B160C2B3B0308A30F0000020D07097B1D000004D70B06081204FE15100000021204077D1F0000041204097B1E0000047D200000041104A4100000020817D60C08038E6932BF062A133003003E0000001C00001102038E16FE037219090070281E00000A030A160B2B210607A30F0000020C02087B1D000004166AFE037265090070281E00000A0717580B07068E6932D92A0000133002009A0000001D00001102288B0000060A02281400000A6F1600000A067B200000045C0B02287B0000060C07281A00000A08281800000A2C4108281D00000A067B20000004D90D02281400000A6F1600000A09DB13041205FE15110000021205097D21000004120511047D220000041205087D2300000411052A1205FE1511000002120502281400000A6F1600000A7D21000004120507281A00000A7D2300000411052A000042534A4201000100000000000C00000076342E302E33303331390000000005006C000000D8140000237E000044150000980D000023537472696E677300000000DC220000B409000023555300902C0000100000002347554944000000A02C00007C04000023426C6F620000000000000002000001571FA209090A000000FA0133001600000100000021000000140000002C0000008F000000C50000000200000042000000030000001E0000001D00000004000000170000002D0000000100000001000000030000000F000000110000000000FB040100000000000600C302370906003403370906001D02F2070F005709000006004802990306000B03B7060600A402B70606006102B70606007E02B7060600E302B70606003102B7060A00F30A870A0E001C00FF080A00770A870A0A00DF01870A0A003C00870A0A00420B870A0600A5013A050A00FC02870A0A002403870A0A00340B870A0600010B3A0506006A053A050A000602870A0600C0033A050A007901870A060052033A05060001003A05060015003A0506006F053A050A001E04870A06009E0C3A050A00AB07870A000000004400000000000100010001001000B805000031000100010001001000F105000031000100210001001000C605000031000100630001001000C80A00003100010073000A0110009C0B00004900010090000A011000080400004900040090000A011000DE0300004900070090000A0110000804000049000B0090000A011000DE03000049000E0090000A011000EA0300004900110090000A011000C70300004900140090000A011000080400004900160090000A011000DE0300004900190090000A011000F20B000049001D0090000A011000ED00000049001F0090000A011000F90600004900210090000A011000FC0300004900240090000A0110001404000049002500900002010000AF0100005D0029009000060027015E020600720862020600210862020600600523010600F30623010600D60B5E0206009F0723010600380723010600AC0B5E020600D60B5E020600600523010600F30623010600A100620206009F0723010600D80023010600A100620206009F0723010600E30723010600D800650206008E07230106009C0723010600600523010600F30623010600D60B5E0206009F0723010600380723010600AC0B5E020600D60B5E020600780962020600020662020600400462020600020662020600CC0062020600B60B62020600C30B5E020600030A23010600310723010600CC0062020600C30B5E020600B10062020606590068025680C6056B025680B8056B025680F1056B02502000000000860805084F000100622000000000810813086F02010075200000000081008E0B740202009220000000008100990B7B020300B020000000008618EC0783020500E92000000000C600770306000A00F1200000000086004C0806000A000B2100000000E601DA068E020A002F2100000000E601450596020C004C21000000008100800B74020F00A6210000000081003308A0021000BC210000000081009208A0021200E92100000000860085084F001400FC210000000086008508A9021400292200000000860060084F0015003C220000000086006008A90215007022000000008600020C0600160015230000000086080D05AF02160027230000000081081805100016003A230000000086088E01AF0217004C230000000081089701100017005F2300000000E6093A0DB302180071230000000081084A0DB8021800842300000000E6098709BE02190098230000000081089409C2021900B42300000000E6015901C7021A00D1230000000081006401CE021B00F023000000008100E5068E021D00C024000000008100520596021F009C2500000000E6017F03D60222001226000000008100AD04E0022500362600000000E6016F01EA02280059260000000086000901F3022A0076260000000081002009F8022B009426000000008100E10CFE022D00A626000000008100740703032E00BA26000000008100810709032F00CF26000000008100A40CFE023100E1260000000081007F0403033200F5260000000081008F04090333000A27000000008100520BA90235002727000000008100690B100336004527000000008100C607170338006827000000008100D9071F033A008C2700000000860860074A003D009E270000000081086A0728033D00B1270000000086088E01AF023E00C327000000008108970110003E00D6270000000086080D05AF023F00E827000000008108180510003F00FB2700000000810889004F0040000D2800000000810899006F0240002028000000008100F10CFE02410032280000000081001D0C2E03420046280000000081002D0C330343005B280000000081000B0C6F0245006F28000000008100CE0CFE024600812800000000810069062E0347009528000000008100790633034800AA2800000000810057066F024A00BE28000000008100040D39034B00D628000000008100560C40034D00EB280000000081006D0C47034F0001290000000081003D0C100352001629000000008100B70C390354002E2900000000810029064003560043290000000081004006470358005929000000008100100610035B006E290000000086083A0D4F005D0080290000000081084A0D6F025D009429000000008618EC074F035E00F729000000008600300C2E036100142A000000008600700C40036200332A000000008600410557036400402A000000008600410562036800542A000000008600450562036B00982A0000000086007F0310036E00DE2A000000008600DC046B037000062B0000000086008703A9027200182B000000008600910303037300352B000000008600D50003037400452B000000008600B90417037500502B000000008100620410037700882B000000008100AA0510037900102C000000008100920510037B00682C000000008100490457037D00112D0000000081009F046F028100382D0000000081004807620382006C2D000000008100730462038500A02D000000008100CA041F038800D42D000000008100CA016F028B001C2E00000000810054076F028C00802E0000000081009D056F028D009C2E0000000086000A0728038E00E12E0000000081001E0D06008F00042F000000008600EE0410038F008C2F000000008600D5066F0291001D30000000008600800D280392003530000000008618EC07830293006E300000000086080D05AF029800803000000000810818051000980093300000000086088E01AF029900A530000000008108970110009900B83000000000E6098709BE029A00CC300000000081089409C2029A00E83000000000E6093A0DB3029B00FA300000000081084A0DB8029B000D3100000000E6015901C7029C002A310000000081006401CE029D00483100000000E601DA068E029F00183200000000E60145059602A100F43200000000E6017F03D602A4006A33000000008100AD04E002A7008E3300000000E6016F01EA02AA00B1330000000086082F044F00AC00C3330000000081083C046F02AC00D633000000008608EE094A00AD00E833000000008108FF092803AD00FB33000000008608C5094A00AE000D34000000008108D4092803AE002034000000008608100A4A00AF003234000000008108220A2803AF0045340000000086083701B302B00057340000000081084801B802B0006A34000000008608D4059C00B1007C34000000008108EB057203B1008F340000000086088F069C00B200B73400000000860860074A00B200C9340000000081086A072803B200DC34000000008608B0087703B300EE34000000008108C0087D03B3000435000000008618EC078403B4001836000000008100D40A9803BE00A13600000000C60077030600C300AC36000000008600EB0B9C00C3003038000000008100BA000600C3001C39000000008600A2089C00C3007839000000008600AA099C00C300583A000000008100F800A403C300A43A000000008100EC08A903C300D83A0000000081009E0AB003C400403B000000008100DC08A903C5008C3B000000008100F606B903C600000001007103000001007F0A000001007F0A00000200A40B000001001702000002005A0D00000300A00100000400230500000500A10900000100070700000200DD0B00000100650500000200070700000300DD0B000001007F0A000001007F0A00000200A40B000001007F0A00000200A40B000001007F0A000001007F0A000001007103000001007103000001007103000001007103000001007F0A000001007F0A00000200710300000100070700000200DD0B00000100650500000200070700000300DD0B00000100400700000200CF0B00000300DD0B00000100A50700000200400700000300710300000100A507000002004007000001004D00000001007D0000000200710300000100EA0000000100EA0000000100EA0000000200710300000100EA0000000100EA0000000100EA00000002007103000001007F0A000001007F0A00000200710300000100A50700000200420A00000100A50700000200420A00000300710300000100710300000100710300000100710300000100710300000100840C00000100840C00000100840C00000200890600000100840C00000100890600000100890600000100890600000200840C000001008906000001007F0A00000200840C000001007F0A00000200840C00000100A50700000200840C00000300A90000000100A50700000200840C00000100A50700000200A90000000100A50700000200A90000000100A50700000200A90000000300840C00000100A50700000200A90000000100710300000100170200000200A00100000300230500000100840C00000100A50700000200840C00000100650500000200070700000300A90000000400610000000100650500000200070700000300A90000000100650500000200070700000300A90000000100E10000000200A90000000100420A00000200E10000000100A50700000100A90000000100A90000000100A50700000200420A00000100070700000200A90000000100650500000200A90000000100070700000200A90000000100650500000200070700000300A90000000400610000000100A90000000100650500000200070700000300A90000000100A50700000200E10000000300A90000000100A50700000200420A00000300E10000000100A90000000100A90000000100A90000000100A507000001007F0A00000200DD0B00000100A900000001007F0A00000100F301000002005A0D00000300A00100000400230500000500A109000001007103000001007103000001007103000001007103000001007F0A000001007F0A00000200710300000100070700000200DD0B00000100650500000200070700000300DD0B00000100400700000200CF0B00000300DD0B00000100A50700000200400700000300710300000100A50700000200400700000100710300000100710300000100710300000100710300000100710300000100710300000100710300000100710300000100F30100000200A50700000300B901000004005A0D00000500A00100000600230500000700A10900000800E30900000900340A00000A00D00800000100B901000002005A0D00000300A00100000400230500000500A10900000100F70800000100F70800000100F70802003500040035000900EC0701001100EC0706001900EC070A002900EC0710003100EC0710003900EC0710004100EC0710004900EC0710005100EC0710005900EC0710009900EC070600A100EC0706006100D5011500C10008001A00C10012001F00C900C10A2500C100080B2B00C100120B37006100EC073F00610082014500D1002D074A00D10057034F0081001C0B5A008100770560008100C906680081001C0B750081002E0D680081009C0668008100280B80006100E40B8E008100B60A68006100570794008900B9099C00C100B303A000C100BD03A500C1002E00AB00C1003900B100C1006609B800C1006F09BE008100660D600061001A04CC008100860560008100A80668008100720D6000C900C10AEB00C1002A05F200C1003205F700C100690AFD00C100740A0301C1001C0710007100660D0F017100720D0F01710002072301C100E80A2E016100F6043401890061034001610025049A01F90022074F00C100920C9F01C1009B0CAC016100B707C00109018A0CC601A900B9099C00A900520A4A006100C301D90161001B014F000900A8004F020900AC0054020900B00059022E000B00DE032E001300E7032E001B0006042E0023000F042E002B0045042E00330056042E003B0061042E0043006E042E004B0045042E005300450481005B005402A1005B005402A30063005402E1005B00540201015B00540261015B00540281015B005402A1015B005402C1015B005402E1015B00540201025B00540221025B00540241025B00540281025B005402A1025B005402C1025B005402E1025B00540221035B00540241035B005402A1045B005402530071007B008600C500D800E1000A0117011E01270144014E01580162016C0172017B0187019001B401F20100020F0214021F02250231023A02020001000300060004000B0005000F0000009808BE0300001C05C20300009B01C20300004E0DC60300009809CB0300009F07CF0300009B01C20300001C05C20300009D00BE0300004E0DBE0300001C05C20300009B01C20300009809CB0300004E0DC60300004004BE030000030ACF030000D809CF030000260ACF0300004C01C6030000EF05D40300009306D40300009F07CF030000C408D803020001000300010002000300020012000500010013000500020014000700010015000700020016000900010017000900020018000B00010019000B0002002D000D0001002E000D0002002F000F00010030000F00020031001100010032001100020033001300010034001300020045001500010046001500020064001700010065001700020066001900010067001900020068001B00010069001B0002006A001D0001006B001D00020073001F00010074001F0002007500210001007600210002007700230001007800230002007900250001007A00250002007B00270001007C00270002007D00290001007E00290002007F002B00020080002D00010081002D00020082002F00010083002F000C02048000000100000000000000000000000000F30A000004000000000000000000000046026600000000000200000000000000000000000000870A000000000200000000000000000000000000FF0800000000060002000700020008000200090003000A0003000B0003000C0003000D0004000E0004000F000500100005001100050012000500130005001400050023003200250032005300D3005300E600530049015300530153005D015300670153008201530095017700A7017D00CF015300D4018300E3018300E8018300ED015300FB01000000000055496E7433320047657455496E7436340053657455496E74363400495374616E64617264546F6B656E3235360047657455496E743235360053657455496E74323536003C4D6F64756C653E00696E7465726661636549440076616C75655F5F00646174610053797374656D2E507269766174652E436F72654C696200696E746572666163654964006765745F4E657874546F6B656E4964007365745F4E657874546F6B656E496400746F6B656E496400526566756E64656400456E737572654B7963566572696669656400496E76657374656400476574417070726F76656400617070726F7665640069640053616C65506572696F640047657443757272656E74506572696F6400537570706F727473496E74657266616365006765745F42616C616E6365004469766964656E6442616C616E6365006765745F546F6B656E42616C616E6365007365745F546F6B656E42616C616E63650047657442616C616E63650053657442616C616E636500416C6C6F77616E636500494D657373616765006765745F4D657373616765006765745F4E616D65007365745F4E616D65006E616D650056616C75655479706500546F6B656E5479706500746F6B656E54797065004372656174650043616E4F706572617465006765745F53746174650049536D617274436F6E7472616374537461746500736D617274436F6E74726163745374617465004950657273697374656E7453746174650073746174650044656275676761626C6541747472696275746500417373656D626C795469746C65417474726962757465005461726765744672616D65776F726B41747472696275746500417373656D626C7946696C6556657273696F6E41747472696275746500417373656D626C79496E666F726D6174696F6E616C56657273696F6E41747472696275746500417373656D626C79436F6E66696775726174696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E7341747472696275746500417373656D626C7950726F6475637441747472696275746500496E64657841747472696275746500417373656D626C79436F6D70616E79417474726962757465004465706C6F794174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650042797465006765745F56616C7565006765745F52657475726E56616C75650076616C7565005265636569766500417070726F76650042616C616E63654F66004F776E65724F660053797374656D2E52756E74696D652E56657273696F6E696E6700476574537472696E6700536574537472696E67004F776E6572736869705472616E7366657265644C6F6700417070726F76616C4C6F6700417070726F76616C466F72416C6C4C6F670053544F53657475704C6F67005472616E736665724C6F6700496E766573744C6F670049426C6F636B006765745F426C6F636B006765745F456E64426C6F636B007365745F456E64426C6F636B00536166655472616E7366657246726F6D496E7465726E616C005472616E73666572496E7465726E616C004C6F67417070726F76616C004765744964546F417070726F76616C005365744964546F417070726F76616C00436C656172417070726F76616C00536574417070726F76616C004973417070726F766564466F72416C6C004C6F67417070726F76616C466F72416C6C00536574417070726F76616C466F72416C6C004D696E74416C6C0043616C6C00536D617274436F6E74726163742E646C6C006765745F53796D626F6C007365745F53796D626F6C0073796D626F6C00476574426F6F6C00536574426F6F6C0053797374656D00536166655472616E7366657246726F6D005472616E73666572546F6B656E7346726F6D0066726F6D00456E756D00426F6F6C65616E006F705F477265617465725468616E006F705F4C6573735468616E004164644E46546F6B656E0056616C69644E46546F6B656E0052656D6F76654E46546F6B656E004469766964656E64546F6B656E005374616E64617264546F6B656E006765745F49734E6F6E46756E6769626C65546F6B656E007365745F49734E6F6E46756E6769626C65546F6B656E005072696365506572546F6B656E00436C656172496E6465784F664F776E65724279546F6B656E00476574496E6465784F664F776E65724279546F6B656E00536574496E6465784F664F776E65724279546F6B656E00436C656172496E6465784279546F6B656E00476574496E6465784279546F6B656E00536574496E6465784279546F6B656E00746F6B656E006765745F53616C654F70656E006F705F4469766973696F6E006F705F5375627472616374696F6E0053797374656D2E5265666C656374696F6E006F705F4164646974696F6E004275726E005472616E73666572546F005472616E73666572546F6B656E73546F0047657453616C65496E666F005A65726F00746F005472616E736665724F776E65727368697000436C656172006765745F4E756D626572006765745F53656E646572005370656E646572007370656E646572004C6F675472616E736665720043616E5472616E73666572006765745F4F776E6572007365745F4F776E6572004765744964546F4F776E6572005365744964546F4F776E65720050726576696F75734F776E6572004E65774F776E6572006F776E6572004953657269616C697A6572006765745F53657269616C697A6572004765744F776E6572546F4F70657261746F72005365744F776E6572546F4F70657261746F72002E63746F720053797374656D2E446961676E6F7374696373006765745F4469766964656E6473007365745F4469766964656E64730043726564697465644469766964656E647300476574576974686472617761626C654469766964656E647300446973747269627574654469766964656E647300476574546F74616C4469766964656E64730057697468647261776E4469766964656E6473004765744469766964656E6473004765744E65774469766964656E647300576974686472617746756E6473006765745F53616C65506572696F6473007365745F53616C65506572696F64730073616C65506572696F64730056616C6964617465506572696F647300536574506572696F647300706572696F647300537472617469732E536D617274436F6E7472616374732E5374616E646172647300536574537570706F72746564496E74657266616365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004765744279746573005365744279746573004475726174696F6E426C6F636B73006765745F446563696D616C73007365745F446563696D616C7300646563696D616C73005769746864726177546F6B656E73006765745F53756363657373006765745F4B594341646472657373007365745F4B594341646472657373006B796341646472657373006765745F546F6B656E41646472657373007365745F546F6B656E41646472657373006765745F4D617070657241646472657373007365745F4D617070657241646472657373006D617070657241646472657373006F70657261746F7241646472657373006765745F4E6577436F6E74726163744164647265737300476574416464726573730053657441646472657373006164647265737300537472617469732E536D617274436F6E74726163747300436F6E7665727453616C65506572696F64496E70757473006F705F4D6F64756C757300466F726D61740053544F436F6E747261637400437265617465546F6B656E436F6E7472616374004973436F6E747261637400536D617274436F6E7472616374004F626A6563740047657453747275637400536574537472756374006F705F496D706C69636974006F705F4578706C696369740049437265617465526573756C7400495472616E73666572526573756C74004765744F776E6572546F4E46546F6B656E436F756E74005365744F776E6572546F4E46546F6B656E436F756E74005570646174654163636F756E74004765744163636F756E74005365744163636F756E74006163636F756E74004F6C64416D6F756E7400526566756E64416D6F756E7400546F6B656E416D6F756E740063757272656E74416D6F756E7400616D6F756E740041737365727400496E766573740053616C65506572696F64496E70757400576974686472617700436C656172546F6B656E4279496E64657800476574546F6B656E4279496E64657800536574546F6B656E4279496E64657800436C656172546F6B656E4F664F776E65724279496E64657800476574546F6B656E4F664F776E65724279496E64657800536574546F6B656E4F664F776E65724279496E64657800696E64657800546F4172726179004765744172726179005365744172726179004765744964546F417070726F76616C4B657900496E6465784F664F776E65724279546F6B656E4B657900476574496E6465784279546F6B656E4B6579004765744964546F4F776E65724B657900476574546F6B656E4279496E6465784B657900476574546F6B656E4F664F776E65724279496E6465784B657900456E737572654F776E65724F6E6C79006F705F4D756C7469706C79006765745F546F74616C537570706C79007365745F546F74616C537570706C7900746F74616C537570706C79006F705F457175616C697479006F705F496E657175616C69747900456E737572654164647265737349734E6F74456D7074790000134400690076006900640065006E006400730000174100630063006F0075006E0074003A007B0030007D00003B54006800650020006100630063006F0075006E007400200068006100730020006E006F0020006400690076006900640065006E00640073002E0000215400720061006E00730066006500720020006600610069006C00650064002E00000D530079006D0062006F006C0000094E0061006D006500001754006F00740061006C0053007500700070006C007900001144006500630069006D0061006C0073000017420061006C0061006E00630065003A007B0030007D00002341006C006C006F00770061006E00630065003A007B0030007D003A007B0031007D00002D53007500700070006F00720074006500640049006E0074006500720066006100630065003A007B0030007D00001B4900640054006F004F0077006E00650072003A007B0030007D0000214900640054006F0041007000700072006F00760061006C003A007B0030007D00002F4F0077006E006500720054006F004E00460054006F006B0065006E0043006F0075006E0074003A007B0030007D00002F4F0077006E006500720054006F004F00700065007200610074006F0072003A007B0030007D003A007B0031007D00000B4F0077006E006500720000174E0065007800740054006F006B0065006E0049006400002154006F006B0065006E004200790049006E006400650078003A007B0030007D00002149006E006400650078004200790054006F006B0065006E003A007B0030007D00003754006F006B0065006E004F0066004F0077006E00650072004200790049006E006400650078003A007B0030007D003A007B0031007D00003749006E006400650078004F0066004F0077006E00650072004200790054006F006B0065006E003A007B0030007D003A007B0031007D00002B540068006500200069006E00640065007800200069007300200069006E00760061006C00690064002E00001D41007300730065007200740020006600610069006C00650064002E0000354F006E004E006F006E00460075006E006700690062006C00650054006F006B0065006E0052006500630065006900760065006400005B4F006E006C00790020006F0077006E006500720020006F0066002000740068006500200063006F006E00740072006100630074002000630061006E00200073006500740020006E006500770020006F0077006E00650072002E00004B740068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000494F006E006C007900200074006F006B0065006E0020006F0077006E00650072002000630061006E0020006200750072006E002000740068006500200074006F006B0065006E002E000039540068006500200061006400640072006500730073002000630061006E0020006E006F00740020006200650020007A00650072006F002E00001145006E00640042006C006F0063006B00001954006F006B0065006E00410064006400720065007300730000154B00590043004100640064007200650073007300001B4D00610070007000650072004100640064007200650073007300001954006F006B0065006E00420061006C0061006E00630065000025490073004E006F006E00460075006E006700690062006C00650054006F006B0065006E000017530061006C00650050006500720069006F0064007300005354006800650020007B0030007D00200070006100720061006D0065007400650072002000630061006E0020006200650020006200650074007700650065006E0020003000200061006E006400200032002E00001374006F006B0065006E005400790070006500004354006800650020007B0030007D0020006900730020006E006F00740020006100200063006F006E007400720061006300740020006100640072006500730073002E0000156B00790063004100640064007200650073007300001B6D00610070007000650072004100640064007200650073007300003F4300720065006100740069006E006700200074006F006B0065006E00200063006F006E007400720061006300740020006600610069006C00650064002E00002B5400680065002000530054004F00200069007300200063006F006D0070006C0065007400650064002E00004B540068006500200061006D006F0075006E0074002000730068006F0075006C006400200062006500200068006900670068006500720020007400680061006E0020007A00650072006F0000155400720061006E00730066006500720054006F00000F4D0069006E00740041006C006C00002D54006F006B0065006E0020007400720061006E00730066006500720020006600610069006C00650064002E00001D52006500660075006E00640020006600610069006C00650064002E0000274700650074005300650063006F006E0064006100720079004100640064007200650073007300001147006500740043006C00610069006D00003359006F007500720020004B005900430020006900730020006E006F0074002000760065007200690066006900650064002E00003754006800650020006100640064007200650073007300200068006100730020006E006F0020006D0061007000700069006E0067002E00004F4F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E0073006600650072002000660075006E00640073002E00002B530054004F0020006900730020006E006F007400200065006E0064006500640020007900650074002E00006F54006800650020007B0030007D0020006D006500740068006F00640020006900730020006E006F007400200073007500700070006F007200740065006400200066006F00720020004E006F006E002D00460075006E006700690062006C006500200054006F006B0065006E002E00011D5700690074006800640072006100770054006F006B0065006E00730000514F006E006C007900200063006F006E007400720061006300740020006F0077006E00650072002000630061006E0020007400720061006E007300660065007200200074006F006B0065006E0073002E00004B50006C0065006100730065002000700072006F00760069006400650020006100740020006C006500610073007400200031002000730061006C006500200070006500720069006F006400004D4400750072006100740069006F006E0042006C006F0063006B0073002000730068006F0075006C006400200068006900670068006500720020007400680061006E0020007A00650072006F000000F4631C212C70084788C53B6008B0C31200042001010803200001052001011111042001010E04200012610420010B0E052002010E0B0500020E0E1C063001011E000E040A01111807300102010E1E0005200101123D042000126904200011390320000B0607021118114105000111410807000202114111410800021141114111410307010B05000111410B04070111180500010B114107070311180B124505200201020E072002124511390B032000020420010E0E052002010E0E05200111410E062002010E11410520011D050E062002010E1D050607021141111C06300101011E00040A01111C08070311411141111C0407011120040A0111200600030E0E1C1C042001020E052002010E0205200111390E062002010E1139040701113907000202113911390607040B0B0B0B0407020B0B03061139060702113912450520010211390B2005124511390B0E1D1C0B0320001C0407011124040A0111240407011128040A011128040701112C040A01112C0407011130040A0111300507030B0B0B08070511390B0B0B0B06070211411134040A0111340807031141114111340407011138040A011138042000127D073001011D1E000E040A011140072002010E1280810B07041D113C115012551148052000128085083001011D1E001D05040A01113C040A0111480930010312550B1D1C0B040A011210040A011208040A01120C08070311441245114C040A01114C0B0705124511391C11391D05021D0504070112450A070411401D11400811400507011D11400B07051D11400B08113C11400807031D113C08113C0B070611400B11410B0B1144087CEC85D7BEA7798E0400000000040100000004020000000306114102060B02060202060903061150042001010B0620011118113907200201113911180A200501123D11410E0E050720020211391141092003021139113911410820021141113911180520010B11390320000E0420001141052001011141032000050420010105062001114111390720020111391141092003021139114111410920030111391139114108200211411139113904200102090520020109020420010E0B05200111390B062002010B11390620020111390B07200202113911390820030111391139020520010111390420010B0B052002010B0B0620020E11390B0620020B11390B0720030111390B0B07200301123D0E0E0A200401113911390B1D0508200301113911390B0620020111390204200101020520001D1140062001011D114013200A01123D11390911410E0E09113911391D050B20051255115011410E0E090420001140062001011D113C0820011D11401D113C04200011440328000B0328000E0428001141032800050428001139032800020528001D11400801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100020000000000350100182E4E4554436F72654170702C56657273696F6E3D76322E310100540E144672616D65776F726B446973706C61794E616D65001001000B49434F436F6E747261637400000A010005446562756700000C010007312E302E302E3000000A010005312E302E30000000000000000000000000000000000010000000000000000000000000000000946D00000000000000000000AE6D0000002000000000000000000000000000000000000000000000A06D0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF2500200010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000C000000C03D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ``` diff --git a/Mainnet/STOContract/STOContract/DividendToken.cs b/Mainnet/STOContract/STOContract/DividendToken.cs new file mode 100644 index 00000000..3b032045 --- /dev/null +++ b/Mainnet/STOContract/STOContract/DividendToken.cs @@ -0,0 +1,304 @@ +using Stratis.SmartContracts; +using Stratis.SmartContracts.Standards; + +public class DividendToken : SmartContract, IStandardToken256 +{ + public ulong Dividends + { + get => State.GetUInt64(nameof(this.Dividends)); + private set => State.SetUInt64(nameof(this.Dividends), value); + } + + private Account GetAccount(Address address) => State.GetStruct($"Account:{address}"); + + private void SetAccount(Address address, Account account) => State.SetStruct($"Account:{address}", account); + + + public DividendToken(ISmartContractState state, UInt256 totalSupply, string name, string symbol, byte decimals) + : base(state) + { + this.TotalSupply = totalSupply; + this.Name = name; + this.Symbol = symbol; + this.Decimals = decimals; + this.SetBalance(Message.Sender, totalSupply); + } + + + public override void Receive() + { + DistributeDividends(); + } + + /// + /// It is advised that deposit amount should to be evenly divided by total supply, + /// otherwise small amount of satoshi may lost(burn) + /// + public void DistributeDividends() + { + Dividends += Message.Value; + } + + public bool TransferTo(Address to, UInt256 amount) + { + UpdateAccount(Message.Sender); + UpdateAccount(to); + + return TransferTokensTo(to, amount); + } + + public bool TransferFrom(Address from, Address to, UInt256 amount) + { + UpdateAccount(from); + UpdateAccount(to); + + return TransferTokensFrom(from, to, amount); + } + + private Account UpdateAccount(Address address) + { + var account = GetAccount(address); + var newDividends = GetNewDividends(address, account); + + if (newDividends > 0) + { + account.DividendBalance += newDividends; + account.CreditedDividends = Dividends; + SetAccount(address, account); + } + + return account; + } + + private UInt256 GetWithdrawableDividends(Address address, Account account) + { + return account.DividendBalance + GetNewDividends(address, account); //Delay divide by TotalSupply to final stage for avoid decimal value loss. + } + + private UInt256 GetNewDividends(Address address, Account account) + { + var notCreditedDividends = checked(Dividends - account.CreditedDividends); + return GetBalance(address) * notCreditedDividends; + } + + /// + /// Get Withdrawable dividends + /// + /// + public ulong GetDividends() => GetDividends(Message.Sender); + + /// + /// Get Withdrawable dividends + /// + /// + /// + public ulong GetDividends(Address address) + { + var account = GetAccount(address); + + var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; + + return (ulong)withdrawable; + } + + /// + /// Get the all divididends since beginning (Withdrawable Dividends + Withdrawn Dividends) + /// + /// + public ulong GetTotalDividends() => GetTotalDividends(Message.Sender); + + /// + /// Get the all divididends since beginning (Withdrawable Dividends + Withdrawn Dividends) + /// + /// + /// + public ulong GetTotalDividends(Address address) + { + var account = GetAccount(address); + var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; + return (ulong)withdrawable + account.WithdrawnDividends; + } + + /// + /// Withdraws all dividends + /// + public void Withdraw() + { + var account = UpdateAccount(Message.Sender); + var balance = (ulong)(account.DividendBalance / TotalSupply); + + Assert(balance > 0, "The account has no dividends."); + + account.WithdrawnDividends += balance; + + account.DividendBalance %= TotalSupply; + + SetAccount(Message.Sender, account); + + var transfer = Transfer(Message.Sender, balance); + + Assert(transfer.Success, "Transfer failed."); + } + + public struct Account + { + /// + /// Withdrawable Dividend Balance. Exact value should to divided by + /// + public UInt256 DividendBalance; + + public ulong WithdrawnDividends; + + /// + /// Dividends computed and added to + /// + + public ulong CreditedDividends; + } + + #region StandardToken code is inlined + + + public string Symbol + { + get => State.GetString(nameof(this.Symbol)); + private set => State.SetString(nameof(this.Symbol), value); + } + + public string Name + { + get => State.GetString(nameof(this.Name)); + private set => State.SetString(nameof(this.Name), value); + } + + /// + public UInt256 TotalSupply + { + get => State.GetUInt256(nameof(this.TotalSupply)); + private set => State.SetUInt256(nameof(this.TotalSupply), value); + } + + public byte Decimals + { + get => State.GetBytes(nameof(Decimals))[0]; + private set => State.SetBytes(nameof(Decimals), new[] { value }); + } + + + /// + public UInt256 GetBalance(Address address) + { + return State.GetUInt256($"Balance:{address}"); + } + + private void SetBalance(Address address, UInt256 value) + { + State.SetUInt256($"Balance:{address}", value); + } + + /// + private bool TransferTokensTo(Address to, UInt256 amount) + { + if (amount == 0) + { + Log(new TransferLog { From = Message.Sender, To = to, Amount = 0 }); + + return true; + } + + UInt256 senderBalance = GetBalance(Message.Sender); + + if (senderBalance < amount) + { + return false; + } + + SetBalance(Message.Sender, senderBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = Message.Sender, To = to, Amount = amount }); + + return true; + } + + /// + private bool TransferTokensFrom(Address from, Address to, UInt256 amount) + { + if (amount == 0) + { + Log(new TransferLog { From = from, To = to, Amount = 0 }); + + return true; + } + + UInt256 senderAllowance = Allowance(from, Message.Sender); + UInt256 fromBalance = GetBalance(from); + + if (senderAllowance < amount || fromBalance < amount) + { + return false; + } + + SetApproval(from, Message.Sender, senderAllowance - amount); + + SetBalance(from, fromBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = from, To = to, Amount = amount }); + + return true; + } + + /// + public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) + { + if (Allowance(Message.Sender, spender) != currentAmount) + { + return false; + } + + SetApproval(Message.Sender, spender, amount); + + Log(new ApprovalLog { Owner = Message.Sender, Spender = spender, Amount = amount, OldAmount = currentAmount }); + + return true; + } + + private void SetApproval(Address owner, Address spender, UInt256 value) + { + State.SetUInt256($"Allowance:{owner}:{spender}", value); + } + + /// + public UInt256 Allowance(Address owner, Address spender) + { + return State.GetUInt256($"Allowance:{owner}:{spender}"); + } + + public struct TransferLog + { + [Index] + public Address From; + + [Index] + public Address To; + + public UInt256 Amount; + } + + public struct ApprovalLog + { + [Index] + public Address Owner; + + [Index] + public Address Spender; + + public UInt256 OldAmount; + + public UInt256 Amount; + } + #endregion +} diff --git a/Mainnet/STOContract/STOContract/NonFungibleToken.cs b/Mainnet/STOContract/STOContract/NonFungibleToken.cs new file mode 100644 index 00000000..c1e9fe43 --- /dev/null +++ b/Mainnet/STOContract/STOContract/NonFungibleToken.cs @@ -0,0 +1,629 @@ +using Stratis.SmartContracts; +/// +/// A non fungible token contract. +/// +public class NonFungibleToken : SmartContract +{ + public struct TransferLog + { + [Index] + public Address From; + [Index] + public Address To; + [Index] + public ulong TokenId; + } + + public struct ApprovalLog + { + [Index] + public Address Owner; + [Index] + public Address Approved; + [Index] + public ulong TokenId; + } + + public struct ApprovalForAllLog + { + [Index] + public Address Owner; + [Index] + public Address Operator; + + public bool Approved; + } + + public struct OwnershipTransferedLog + { + [Index] + public Address PreviousOwner; + + [Index] + public Address NewOwner; + } + + /// + /// Function to check which interfaces are supported by this contract. + /// + /// Id of the interface. + /// True if is supported, false otherwise. + public bool SupportsInterface(uint interfaceID) + { + return State.GetBool($"SupportedInterface:{interfaceID}"); + } + + /// + /// Sets the supported interface value. + /// + /// The interface id. + /// A value indicating if the interface id is supported. + private void SetSupportedInterfaces(uint interfaceId, bool value) => State.SetBool($"SupportedInterface:{interfaceId}", value); + + /// + /// Gets the key to the persistent state for the owner by NFT ID. + /// + /// The NFT ID. + /// The persistent storage key to get or set the NFT owner. + private string GetIdToOwnerKey(ulong id) => $"IdToOwner:{id}"; + + /// + /// Gets the address of the owner of the NFT ID. + /// + /// The ID of the NFT + ///The owner address. + private Address GetIdToOwner(ulong id) => State.GetAddress(GetIdToOwnerKey(id)); + + /// + /// Sets the owner to the NFT ID. + /// + /// The ID of the NFT + /// The address of the owner. + private void SetIdToOwner(ulong id, Address value) => State.SetAddress(GetIdToOwnerKey(id), value); + + /// + /// Gets the key to the persistent state for the approval address by NFT ID. + /// + /// The NFT ID. + /// The persistent storage key to get or set the NFT approval. + private string GetIdToApprovalKey(ulong id) => $"IdToApproval:{id}"; + + /// + /// Getting from NFT ID the approval address. + /// + /// The ID of the NFT + /// Address of the approval. + private Address GetIdToApproval(ulong id) => State.GetAddress(GetIdToApprovalKey(id)); + + /// + /// Setting to NFT ID to approval address. + /// + /// The ID of the NFT + /// The address of the approval. + private void SetIdToApproval(ulong id, Address value) => State.SetAddress(GetIdToApprovalKey(id), value); + + /// + /// Gets the amount of non fungible tokens the owner has. + /// + /// The address of the owner. + /// The amount of non fungible tokens. + private ulong GetOwnerToNFTokenCount(Address address) => State.GetUInt64($"OwnerToNFTokenCount:{address}"); + + /// + /// Sets the owner count of this non fungible tokens. + /// + /// The address of the owner. + /// The amount of tokens. + private void SetOwnerToNFTokenCount(Address address, ulong value) => State.SetUInt64($"OwnerToNFTokenCount:{address}", value); + + /// + /// Gets the permission value of the operator authorization to perform actions on behalf of the owner. + /// + /// The owner address of the NFT. + /// >Address of the authorized operators + /// A value indicating if the operator has permissions to act on behalf of the owner. + private bool GetOwnerToOperator(Address owner, Address operatorAddress) => State.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); + + /// + /// Sets the owner to operator permission. + /// + /// The owner address of the NFT. + /// >Address to add to the set of authorized operators. + /// The permission value. + private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => State.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); + + /// + /// Owner of the contract is responsible to for minting/burning + /// + public Address Owner + { + get => State.GetAddress(nameof(Owner)); + private set => State.SetAddress(nameof(Owner), value); + } + + /// + /// Name for non-fungible token contract + /// + public string Name + { + get => State.GetString(nameof(Name)); + private set => State.SetString(nameof(Name), value); + } + + /// + /// Symbol for non-fungible token contract + /// + public string Symbol + { + get => State.GetString(nameof(Symbol)); + private set => State.SetString(nameof(Symbol), value); + } + + /// + /// The next token index which is going to be minted + /// + private ulong NextTokenId + { + get => State.GetUInt64(nameof(NextTokenId)); + set => State.SetUInt64(nameof(NextTokenId), value); + } + + private string GetTokenByIndexKey(ulong index) => $"TokenByIndex:{index}"; + + private ulong GetTokenByIndex(ulong index) => State.GetUInt64(GetTokenByIndexKey(index)); + + private void SetTokenByIndex(ulong index, ulong token) => State.SetUInt64(GetTokenByIndexKey(index), token); + + private void ClearTokenByIndex(ulong index) => State.Clear(GetTokenByIndexKey(index)); + + private string GetIndexByTokenKey(ulong token) => $"IndexByToken:{token}"; + + private ulong GetIndexByToken(ulong token) => State.GetUInt64(GetIndexByTokenKey(token)); + + private void SetIndexByToken(ulong token, ulong index) => State.SetUInt64(GetIndexByTokenKey(token), index); + + private void ClearIndexByToken(ulong token) => State.Clear(GetIndexByTokenKey(token)); + + private string GetTokenOfOwnerByIndexKey(Address address, ulong index) => $"TokenOfOwnerByIndex:{address}:{index}"; + + private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => State.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); + + private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => State.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); + + private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => State.Clear(GetTokenOfOwnerByIndexKey(owner, index)); + + private string IndexOfOwnerByTokenKey(Address owner, ulong tokenId) => $"IndexOfOwnerByToken:{owner}:{tokenId}"; + private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => State.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); + private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => State.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); + private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => State.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); + public ulong TotalSupply + { + get => State.GetUInt64(nameof(TotalSupply)); + private set => State.SetUInt64(nameof(TotalSupply), value); + } + + /// + /// Constructor. Initializes the supported interfaces. + /// + /// The smart contract state. + public NonFungibleToken(ISmartContractState state, string name, string symbol) : base(state) + { + // todo: discuss callback handling and supported interface numbering with community. + this.SetSupportedInterfaces((uint)0x00000001, true); // (ERC165) - ISupportsInterface + this.SetSupportedInterfaces((uint)0x00000002, true); // (ERC721) - INonFungibleToken, + this.SetSupportedInterfaces((uint)0x00000003, false); // (ERC721) - INonFungibleTokenReceiver + this.SetSupportedInterfaces((uint)0x00000004, true); // (ERC721) - INonFungibleTokenMetadata + this.SetSupportedInterfaces((uint)0x00000005, true); // (ERC721) - IERC721Enumerable + + this.Name = name; + this.Symbol = symbol; + this.Owner = Message.Sender; + this.NextTokenId = 1; + } + + public ulong TokenByIndex(ulong index) + { + Assert(index < TotalSupply, "The index is invalid."); + + return GetTokenByIndex(index); + } + + public ulong TokenOfOwnerByIndex(Address owner, ulong index) + { + Assert(index < GetOwnerToNFTokenCount(owner), "The index is invalid."); + + return GetTokenOfOwnerByIndex(owner, index); + } + + + + /// + /// Transfers the ownership of an NFT from one address to another address. This function can + /// be changed to payable. + /// + /// Throws unless is the current owner, an authorized operator, or the + /// approved address for this NFT.Throws if 'from' is not the current owner.Throws if 'to' is + /// the zero address.Throws if 'tokenId' is not a valid NFT. When transfer is complete, this + /// function checks if 'to' is a smart contract. If so, it calls + /// 'OnNonFungibleTokenReceived' on 'to' and throws if the return value true. + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + /// Additional data with no specified format, sent in call to 'to'. + public void SafeTransferFrom(Address from, Address to, ulong tokenId, byte[] data) + { + SafeTransferFromInternal(from, to, tokenId, data); + } + + /// + /// Transfers the ownership of an NFT from one address to another address. This function can + /// be changed to payable. + /// + /// This works identically to the other function with an extra data parameter, except this + /// function just sets data to an empty byte array. + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + public void SafeTransferFrom(Address from, Address to, ulong tokenId) + { + SafeTransferFromInternal(from, to, tokenId, new byte[0]); + } + + /// + /// Throws unless is the current owner, an authorized operator, or the approved + /// address for this NFT.Throws if is not the current owner.Throws if is the zero + /// address.Throws if is not a valid NFT. This function can be changed to payable. + /// + /// The caller is responsible to confirm that is capable of receiving NFTs or else + /// they maybe be permanently lost. + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + public void TransferFrom(Address from, Address to, ulong tokenId) + { + CanTransfer(tokenId); + + Address tokenOwner = GetIdToOwner(tokenId); + EnsureAddressIsNotEmpty(tokenOwner); + EnsureAddressIsNotEmpty(to); + Assert(tokenOwner == from); + + TransferInternal(to, tokenId); + } + + /// + /// Set or reaffirm the approved address for an NFT. This function can be changed to payable. + /// + /// + /// The zero address indicates there is no approved address. Throws unless is + /// the current NFT owner, or an authorized operator of the current owner. + /// + /// Address to be approved for the given NFT ID. + /// ID of the token to be approved. + public void Approve(Address approved, ulong tokenId) + { + CanOperate(tokenId); + ValidNFToken(tokenId); + + Address tokenOwner = GetIdToOwner(tokenId); + Assert(approved != tokenOwner); + + SetIdToApproval(tokenId, approved); + LogApproval(tokenOwner, approved, tokenId); + } + + /// + /// Enables or disables approval for a third party ("operator") to manage all of + /// 's assets. It also Logs the ApprovalForAll event. + /// + /// This works even if sender doesn't own any tokens at the time. + /// Address to add to the set of authorized operators. + /// True if the operators is approved, false to revoke approval. + public void SetApprovalForAll(Address operatorAddress, bool approved) + { + SetOwnerToOperator(Message.Sender, operatorAddress, approved); + LogApprovalForAll(Message.Sender, operatorAddress, approved); + } + + /// + /// Returns the number of NFTs owned by 'owner'. NFTs assigned to the zero address are + /// considered invalid, and this function throws for queries about the zero address. + /// + /// Address for whom to query the balance. + /// Balance of owner. + public ulong BalanceOf(Address owner) + { + EnsureAddressIsNotEmpty(owner); + return GetOwnerToNFTokenCount(owner); + } + + /// + /// Returns the address of the owner of the NFT. NFTs assigned to zero address are considered invalid, and queries about them do throw. + /// + /// The identifier for an NFT. + /// Address of tokenId owner. + public Address OwnerOf(ulong tokenId) + { + Address owner = GetIdToOwner(tokenId); + EnsureAddressIsNotEmpty(owner); + return owner; + } + + /// + /// Get the approved address for a single NFT. + /// + /// Throws if 'tokenId' is not a valid NFT. + /// ID of the NFT to query the approval of. + /// Address that tokenId is approved for. + public Address GetApproved(ulong tokenId) + { + ValidNFToken(tokenId); + + return GetIdToApproval(tokenId); + } + + /// + /// Checks if 'operator' is an approved operator for 'owner'. + /// + /// The address that owns the NFTs. + /// The address that acts on behalf of the owner. + /// True if approved for all, false otherwise. + public bool IsApprovedForAll(Address owner, Address operatorAddress) + { + return GetOwnerToOperator(owner, operatorAddress); + } + + /// + /// Actually preforms the transfer. + /// + /// Does NO checks. + /// Address of a new owner. + /// The NFT that is being transferred. + private void TransferInternal(Address to, ulong tokenId) + { + Address from = GetIdToOwner(tokenId); + ClearApproval(tokenId); + + RemoveNFToken(from, tokenId); + AddNFToken(to, tokenId); + + LogTransfer(from, to, tokenId); + } + + /// + /// Removes a NFT from owner. + /// + /// Use and override this function with caution. Wrong usage can have serious consequences. + /// Address from wich we want to remove the NFT. + /// Which NFT we want to remove. + private void RemoveNFToken(Address from, ulong tokenId) + { + Assert(GetIdToOwner(tokenId) == from); + var tokenCount = GetOwnerToNFTokenCount(from); + SetOwnerToNFTokenCount(from, checked(tokenCount - 1)); + State.Clear(GetIdToOwnerKey(tokenId)); + + ulong index = GetIndexOfOwnerByToken(from, tokenId); + ulong lastIndex = tokenCount - 1; + + if (index != lastIndex) + { + ulong lastToken = GetTokenOfOwnerByIndex(from, lastIndex); + SetIndexOfOwnerByToken(from, lastToken, index); + SetTokenOfOwnerByIndex(from, index, lastToken); + } + + ClearTokenOfOwnerByIndex(from, lastIndex); + ClearIndexOfOwnerByToken(from, tokenId); + } + + /// + /// Assignes a new NFT to owner. + /// + /// Use and override this function with caution. Wrong usage can have serious consequences. + /// Address to which we want to add the NFT. + /// Which NFT we want to add. + private void AddNFToken(Address to, ulong tokenId) + { + Assert(GetIdToOwner(tokenId) == Address.Zero); + + SetIdToOwner(tokenId, to); + ulong currentTokenAmount = GetOwnerToNFTokenCount(to); + SetOwnerToNFTokenCount(to, checked(currentTokenAmount + 1)); + + var index = currentTokenAmount; + SetIndexOfOwnerByToken(to, tokenId, index); + SetTokenOfOwnerByIndex(to, index, tokenId); + } + + /// + /// Actually perform the safeTransferFrom. + /// + /// The current owner of the NFT. + /// The new owner. + /// The NFT to transfer. + /// Additional data with no specified format, sent in call to 'to' if it is a contract. + private void SafeTransferFromInternal(Address from, Address to, ulong tokenId, byte[] data) + { + CanTransfer(tokenId); + ValidNFToken(tokenId); + + Address tokenOwner = GetIdToOwner(tokenId); + Assert(tokenOwner == from); + EnsureAddressIsNotEmpty(to); + + TransferInternal(to, tokenId); + + if (State.IsContract(to)) + { + ITransferResult result = Call(to, 0, "OnNonFungibleTokenReceived", new object[] { Message.Sender, from, tokenId, data }, 0); + Assert((bool)result.ReturnValue); + } + } + + /// + /// Clears the current approval of a given NFT ID. + /// + /// ID of the NFT to be transferred + private void ClearApproval(ulong tokenId) + { + if (GetIdToApproval(tokenId) != Address.Zero) + { + State.Clear(GetIdToApprovalKey(tokenId)); + } + } + + /// + /// This logs when ownership of any NFT changes by any mechanism. This event logs when NFTs are + /// created('from' == 0) and destroyed('to' == 0). Exception: during contract creation, any + /// number of NFTs may be created and assigned without logging Transfer.At the time of any + /// transfer, the approved Address for that NFT (if any) is reset to none. + /// + /// The from address. + /// The to address. + /// The NFT ID. + private void LogTransfer(Address from, Address to, ulong tokenId) + { + Log(new TransferLog() { From = from, To = to, TokenId = tokenId }); + } + + /// + /// This logs when the approved Address for an NFT is changed or reaffirmed. The zero + /// Address indicates there is no approved Address. When a Transfer logs, this also + /// indicates that the approved Address for that NFT (if any) is reset to none. + /// + /// The owner address. + /// The approved address. + /// The NFT ID. + private void LogApproval(Address owner, Address approved, ulong tokenId) + { + Log(new ApprovalLog() { Owner = owner, Approved = approved, TokenId = tokenId }); + } + + /// + /// This logs when an operator is enabled or disabled for an owner. The operator can manage all NFTs of the owner. + /// + /// The owner address + /// The operator address. + /// A boolean indicating if it has been approved. + private void LogApprovalForAll(Address owner, Address operatorAddress, bool approved) + { + Log(new ApprovalForAllLog() { Owner = owner, Operator = operatorAddress, Approved = approved }); + } + + + /// + /// Guarantees that the is an owner or operator of the given NFT. + /// + /// ID of the NFT to validate. + private void CanOperate(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + Assert(tokenOwner == Message.Sender || GetOwnerToOperator(tokenOwner, Message.Sender)); + } + + /// + /// Guarantees that the msg.sender is allowed to transfer NFT. + /// + /// ID of the NFT to transfer. + private void CanTransfer(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + Assert( + tokenOwner == Message.Sender + || GetIdToApproval(tokenId) == Message.Sender + || GetOwnerToOperator(tokenOwner, Message.Sender) + ); + } + + /// + /// Guarantees that tokenId is a valid Token. + /// + /// ID of the NFT to validate. + private void ValidNFToken(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + EnsureAddressIsNotEmpty(tokenOwner); + } + + /// + /// Sets the contract owner who can mint/bur + /// + /// + public void TransferOwnership(Address owner) + { + EnsureOwnerOnly(); + EnsureAddressIsNotEmpty(owner); + + Log(new OwnershipTransferedLog { PreviousOwner = Owner, NewOwner = owner }); + + Owner = owner; + } + + private void EnsureOwnerOnly() + { + Assert(Message.Sender == Owner, "Only owner of the contract can set new owner."); + } + + /// + /// Mints new tokens + /// + /// The address that will own the minted NFT + /// Number of tokens will be created + public void MintAll(Address address, ulong amount) + { + EnsureOwnerOnly(); + EnsureAddressIsNotEmpty(address); + Assert(amount > 0, "the amount should be higher than zero"); + + var index = TotalSupply; + var lastIndex = checked(index + amount); + var tokenId = NextTokenId; + + while (index < lastIndex) + { + AddNFToken(address, tokenId); + SetTokenByIndex(index, tokenId); + SetIndexByToken(tokenId, index); + + LogTransfer(Address.Zero, address, tokenId); + + checked + { + index++; + tokenId++; + } + } + + TotalSupply = checked(TotalSupply + amount); + NextTokenId = tokenId; + } + + public void Burn(ulong tokenId) + { + Address tokenOwner = GetIdToOwner(tokenId); + + Assert(tokenOwner == Message.Sender, "Only token owner can burn the token."); + + ClearApproval(tokenId); + RemoveNFToken(tokenOwner, tokenId); + + //move last token to removed token and delete last token info + var index = GetIndexByToken(tokenId); + var lastTokenIndex = checked(--TotalSupply); + var lastToken = GetTokenByIndex(lastTokenIndex); + + SetTokenByIndex(index, lastToken); + SetIndexByToken(lastToken, index); + + ClearTokenByIndex(lastTokenIndex); + ClearIndexByToken(tokenId); + + LogTransfer(tokenOwner, Address.Zero, tokenId); + } + + public void EnsureAddressIsNotEmpty(Address address) + { + Assert(address != Address.Zero, "The address can not be zero."); + } +} \ No newline at end of file diff --git a/Mainnet/STOContract/STOContract/STOContract.cs b/Mainnet/STOContract/STOContract/STOContract.cs index e2397412..7347324d 100644 --- a/Mainnet/STOContract/STOContract/STOContract.cs +++ b/Mainnet/STOContract/STOContract/STOContract.cs @@ -275,16 +275,6 @@ public struct InvestLog public UInt256 TokenAmount; public ulong Refunded; } - - public struct Claim - { - public string Key; - - public string Description; - - public bool IsRevoked; - } - public enum TokenType : uint { StandardToken, @@ -292,1099 +282,3 @@ public enum TokenType : uint NonFungibleToken } } - -/// -/// Implementation of a standard token contract for the Stratis Platform. -/// -public class StandardToken : SmartContract, IStandardToken256 -{ - /// - /// Constructor used to create a new instance of the token. Assigns the total token supply to the creator of the contract. - /// - /// The execution state for the contract. - /// The total token supply. - /// The name of the token. - /// The symbol used to identify the token. - public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply, string name, string symbol, byte decimals) - : base(smartContractState) - { - this.TotalSupply = totalSupply; - this.Name = name; - this.Symbol = symbol; - this.Decimals = decimals; - this.SetBalance(Message.Sender, totalSupply); - } - - public string Symbol - { - get => State.GetString(nameof(this.Symbol)); - private set => State.SetString(nameof(this.Symbol), value); - } - - public string Name - { - get => State.GetString(nameof(this.Name)); - private set => State.SetString(nameof(this.Name), value); - } - - /// - public byte Decimals - { - get => State.GetBytes(nameof(this.Decimals))[0]; - private set => State.SetBytes(nameof(this.Decimals), new[] { value }); - } - - /// - public UInt256 TotalSupply - { - get => State.GetUInt256(nameof(this.TotalSupply)); - private set => State.SetUInt256(nameof(this.TotalSupply), value); - } - - /// - public UInt256 GetBalance(Address address) - { - return State.GetUInt256($"Balance:{address}"); - } - - private void SetBalance(Address address, UInt256 value) - { - State.SetUInt256($"Balance:{address}", value); - } - - /// - public bool TransferTo(Address to, UInt256 amount) - { - if (amount == 0) - { - Log(new TransferLog { From = Message.Sender, To = to, Amount = 0 }); - - return true; - } - - UInt256 senderBalance = GetBalance(Message.Sender); - - if (senderBalance < amount) - { - return false; - } - - SetBalance(Message.Sender, senderBalance - amount); - - SetBalance(to, checked(GetBalance(to) + amount)); - - Log(new TransferLog { From = Message.Sender, To = to, Amount = amount }); - - return true; - } - - /// - public bool TransferFrom(Address from, Address to, UInt256 amount) - { - if (amount == 0) - { - Log(new TransferLog { From = from, To = to, Amount = 0 }); - - return true; - } - - UInt256 senderAllowance = Allowance(from, Message.Sender); - UInt256 fromBalance = GetBalance(from); - - if (senderAllowance < amount || fromBalance < amount) - { - return false; - } - - SetApproval(from, Message.Sender, senderAllowance - amount); - - SetBalance(from, fromBalance - amount); - - SetBalance(to, checked(GetBalance(to) + amount)); - - Log(new TransferLog { From = from, To = to, Amount = amount }); - - return true; - } - - /// - public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) - { - if (Allowance(Message.Sender, spender) != currentAmount) - { - return false; - } - - SetApproval(Message.Sender, spender, amount); - - Log(new ApprovalLog { Owner = Message.Sender, Spender = spender, Amount = amount, OldAmount = currentAmount }); - - return true; - } - - private void SetApproval(Address owner, Address spender, UInt256 value) - { - State.SetUInt256($"Allowance:{owner}:{spender}", value); - } - - /// - public UInt256 Allowance(Address owner, Address spender) - { - return State.GetUInt256($"Allowance:{owner}:{spender}"); - } - - public struct TransferLog - { - [Index] - public Address From; - - [Index] - public Address To; - - public UInt256 Amount; - } - - public struct ApprovalLog - { - [Index] - public Address Owner; - - [Index] - public Address Spender; - - public UInt256 OldAmount; - - public UInt256 Amount; - } -} - -public class DividendToken : SmartContract, IStandardToken256 -{ - public ulong Dividends - { - get => State.GetUInt64(nameof(this.Dividends)); - private set => State.SetUInt64(nameof(this.Dividends), value); - } - - private Account GetAccount(Address address) => State.GetStruct($"Account:{address}"); - - private void SetAccount(Address address, Account account) => State.SetStruct($"Account:{address}", account); - - - public DividendToken(ISmartContractState state, UInt256 totalSupply, string name, string symbol, byte decimals) - : base(state) - { - this.TotalSupply = totalSupply; - this.Name = name; - this.Symbol = symbol; - this.Decimals = decimals; - this.SetBalance(Message.Sender, totalSupply); - } - - - public override void Receive() - { - DistributeDividends(); - } - - /// - /// It is advised that deposit amount should to be evenly divided by total supply, - /// otherwise small amount of satoshi may lost(burn) - /// - public void DistributeDividends() - { - Dividends += Message.Value; - } - - public bool TransferTo(Address to, UInt256 amount) - { - UpdateAccount(Message.Sender); - UpdateAccount(to); - - return TransferTokensTo(to, amount); - } - - public bool TransferFrom(Address from, Address to, UInt256 amount) - { - UpdateAccount(from); - UpdateAccount(to); - - return TransferTokensFrom(from, to, amount); - } - - private Account UpdateAccount(Address address) - { - var account = GetAccount(address); - var newDividends = GetNewDividends(address, account); - - if (newDividends > 0) - { - account.DividendBalance += newDividends; - account.CreditedDividends = Dividends; - SetAccount(address, account); - } - - return account; - } - - private UInt256 GetWithdrawableDividends(Address address, Account account) - { - return account.DividendBalance + GetNewDividends(address, account); //Delay divide by TotalSupply to final stage for avoid decimal value loss. - } - - private UInt256 GetNewDividends(Address address, Account account) - { - var notCreditedDividends = checked(Dividends - account.CreditedDividends); - return GetBalance(address) * notCreditedDividends; - } - - /// - /// Get Withdrawable dividends - /// - /// - public ulong GetDividends() => GetDividends(Message.Sender); - - /// - /// Get Withdrawable dividends - /// - /// - /// - public ulong GetDividends(Address address) - { - var account = GetAccount(address); - - var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; - - return (ulong)withdrawable; - } - - /// - /// Get the all divididends since beginning (Withdrawable Dividends + Withdrawn Dividends) - /// - /// - public ulong GetTotalDividends() => GetTotalDividends(Message.Sender); - - /// - /// Get the all divididends since beginning (Withdrawable Dividends + Withdrawn Dividends) - /// - /// - /// - public ulong GetTotalDividends(Address address) - { - var account = GetAccount(address); - var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply; - return (ulong)withdrawable + account.WithdrawnDividends; - } - - /// - /// Withdraws all dividends - /// - public void Withdraw() - { - var account = UpdateAccount(Message.Sender); - var balance = (ulong)(account.DividendBalance / TotalSupply); - - Assert(balance > 0, "The account has no dividends."); - - account.WithdrawnDividends += balance; - - account.DividendBalance %= TotalSupply; - - SetAccount(Message.Sender, account); - - var transfer = Transfer(Message.Sender, balance); - - Assert(transfer.Success, "Transfer failed."); - } - - public struct Account - { - /// - /// Withdrawable Dividend Balance. Exact value should to divided by - /// - public UInt256 DividendBalance; - - public ulong WithdrawnDividends; - - /// - /// Dividends computed and added to - /// - - public ulong CreditedDividends; - } - - #region StandardToken code is inlined - - - public string Symbol - { - get => State.GetString(nameof(this.Symbol)); - private set => State.SetString(nameof(this.Symbol), value); - } - - public string Name - { - get => State.GetString(nameof(this.Name)); - private set => State.SetString(nameof(this.Name), value); - } - - /// - public UInt256 TotalSupply - { - get => State.GetUInt256(nameof(this.TotalSupply)); - private set => State.SetUInt256(nameof(this.TotalSupply), value); - } - - public byte Decimals - { - get => State.GetBytes(nameof(Decimals))[0]; - private set => State.SetBytes(nameof(Decimals), new[] { value }); - } - - - /// - public UInt256 GetBalance(Address address) - { - return State.GetUInt256($"Balance:{address}"); - } - - private void SetBalance(Address address, UInt256 value) - { - State.SetUInt256($"Balance:{address}", value); - } - - /// - private bool TransferTokensTo(Address to, UInt256 amount) - { - if (amount == 0) - { - Log(new TransferLog { From = Message.Sender, To = to, Amount = 0 }); - - return true; - } - - UInt256 senderBalance = GetBalance(Message.Sender); - - if (senderBalance < amount) - { - return false; - } - - SetBalance(Message.Sender, senderBalance - amount); - - SetBalance(to, checked(GetBalance(to) + amount)); - - Log(new TransferLog { From = Message.Sender, To = to, Amount = amount }); - - return true; - } - - /// - private bool TransferTokensFrom(Address from, Address to, UInt256 amount) - { - if (amount == 0) - { - Log(new TransferLog { From = from, To = to, Amount = 0 }); - - return true; - } - - UInt256 senderAllowance = Allowance(from, Message.Sender); - UInt256 fromBalance = GetBalance(from); - - if (senderAllowance < amount || fromBalance < amount) - { - return false; - } - - SetApproval(from, Message.Sender, senderAllowance - amount); - - SetBalance(from, fromBalance - amount); - - SetBalance(to, checked(GetBalance(to) + amount)); - - Log(new TransferLog { From = from, To = to, Amount = amount }); - - return true; - } - - /// - public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) - { - if (Allowance(Message.Sender, spender) != currentAmount) - { - return false; - } - - SetApproval(Message.Sender, spender, amount); - - Log(new ApprovalLog { Owner = Message.Sender, Spender = spender, Amount = amount, OldAmount = currentAmount }); - - return true; - } - - private void SetApproval(Address owner, Address spender, UInt256 value) - { - State.SetUInt256($"Allowance:{owner}:{spender}", value); - } - - /// - public UInt256 Allowance(Address owner, Address spender) - { - return State.GetUInt256($"Allowance:{owner}:{spender}"); - } - - public struct TransferLog - { - [Index] - public Address From; - - [Index] - public Address To; - - public UInt256 Amount; - } - - public struct ApprovalLog - { - [Index] - public Address Owner; - - [Index] - public Address Spender; - - public UInt256 OldAmount; - - public UInt256 Amount; - } - #endregion -} - -/// -/// A non fungible token contract. -/// -public class NonFungibleToken : SmartContract -{ - public struct TransferLog - { - [Index] - public Address From; - [Index] - public Address To; - [Index] - public ulong TokenId; - } - - public struct ApprovalLog - { - [Index] - public Address Owner; - [Index] - public Address Approved; - [Index] - public ulong TokenId; - } - - public struct ApprovalForAllLog - { - [Index] - public Address Owner; - [Index] - public Address Operator; - - public bool Approved; - } - - public struct OwnershipTransferedLog - { - [Index] - public Address PreviousOwner; - - [Index] - public Address NewOwner; - } - - /// - /// Function to check which interfaces are supported by this contract. - /// - /// Id of the interface. - /// True if is supported, false otherwise. - public bool SupportsInterface(uint interfaceID) - { - return State.GetBool($"SupportedInterface:{interfaceID}"); - } - - /// - /// Sets the supported interface value. - /// - /// The interface id. - /// A value indicating if the interface id is supported. - private void SetSupportedInterfaces(uint interfaceId, bool value) => State.SetBool($"SupportedInterface:{interfaceId}", value); - - /// - /// Gets the key to the persistent state for the owner by NFT ID. - /// - /// The NFT ID. - /// The persistent storage key to get or set the NFT owner. - private string GetIdToOwnerKey(ulong id) => $"IdToOwner:{id}"; - - /// - /// Gets the address of the owner of the NFT ID. - /// - /// The ID of the NFT - ///The owner address. - private Address GetIdToOwner(ulong id) => State.GetAddress(GetIdToOwnerKey(id)); - - /// - /// Sets the owner to the NFT ID. - /// - /// The ID of the NFT - /// The address of the owner. - private void SetIdToOwner(ulong id, Address value) => State.SetAddress(GetIdToOwnerKey(id), value); - - /// - /// Gets the key to the persistent state for the approval address by NFT ID. - /// - /// The NFT ID. - /// The persistent storage key to get or set the NFT approval. - private string GetIdToApprovalKey(ulong id) => $"IdToApproval:{id}"; - - /// - /// Getting from NFT ID the approval address. - /// - /// The ID of the NFT - /// Address of the approval. - private Address GetIdToApproval(ulong id) => State.GetAddress(GetIdToApprovalKey(id)); - - /// - /// Setting to NFT ID to approval address. - /// - /// The ID of the NFT - /// The address of the approval. - private void SetIdToApproval(ulong id, Address value) => State.SetAddress(GetIdToApprovalKey(id), value); - - /// - /// Gets the amount of non fungible tokens the owner has. - /// - /// The address of the owner. - /// The amount of non fungible tokens. - private ulong GetOwnerToNFTokenCount(Address address) => State.GetUInt64($"OwnerToNFTokenCount:{address}"); - - /// - /// Sets the owner count of this non fungible tokens. - /// - /// The address of the owner. - /// The amount of tokens. - private void SetOwnerToNFTokenCount(Address address, ulong value) => State.SetUInt64($"OwnerToNFTokenCount:{address}", value); - - /// - /// Gets the permission value of the operator authorization to perform actions on behalf of the owner. - /// - /// The owner address of the NFT. - /// >Address of the authorized operators - /// A value indicating if the operator has permissions to act on behalf of the owner. - private bool GetOwnerToOperator(Address owner, Address operatorAddress) => State.GetBool($"OwnerToOperator:{owner}:{operatorAddress}"); - - /// - /// Sets the owner to operator permission. - /// - /// The owner address of the NFT. - /// >Address to add to the set of authorized operators. - /// The permission value. - private void SetOwnerToOperator(Address owner, Address operatorAddress, bool value) => State.SetBool($"OwnerToOperator:{owner}:{operatorAddress}", value); - - /// - /// Owner of the contract is responsible to for minting/burning - /// - public Address Owner - { - get => State.GetAddress(nameof(Owner)); - private set => State.SetAddress(nameof(Owner), value); - } - - /// - /// Name for non-fungible token contract - /// - public string Name - { - get => State.GetString(nameof(Name)); - private set => State.SetString(nameof(Name), value); - } - - /// - /// Symbol for non-fungible token contract - /// - public string Symbol - { - get => State.GetString(nameof(Symbol)); - private set => State.SetString(nameof(Symbol), value); - } - - /// - /// The next token index which is going to be minted - /// - private ulong NextTokenId - { - get => State.GetUInt64(nameof(NextTokenId)); - set => State.SetUInt64(nameof(NextTokenId), value); - } - - private string GetTokenByIndexKey(ulong index) => $"TokenByIndex:{index}"; - - private ulong GetTokenByIndex(ulong index) => State.GetUInt64(GetTokenByIndexKey(index)); - - private void SetTokenByIndex(ulong index, ulong token) => State.SetUInt64(GetTokenByIndexKey(index), token); - - private void ClearTokenByIndex(ulong index) => State.Clear(GetTokenByIndexKey(index)); - - private string GetIndexByTokenKey(ulong token) => $"IndexByToken:{token}"; - - private ulong GetIndexByToken(ulong token) => State.GetUInt64(GetIndexByTokenKey(token)); - - private void SetIndexByToken(ulong token, ulong index) => State.SetUInt64(GetIndexByTokenKey(token), index); - - private void ClearIndexByToken(ulong token) => State.Clear(GetIndexByTokenKey(token)); - - private string GetTokenOfOwnerByIndexKey(Address address, ulong index) => $"TokenOfOwnerByIndex:{address}:{index}"; - - private ulong GetTokenOfOwnerByIndex(Address address, ulong index) => State.GetUInt64(GetTokenOfOwnerByIndexKey(address, index)); - - private void SetTokenOfOwnerByIndex(Address owner, ulong index, ulong tokenId) => State.SetUInt64(GetTokenOfOwnerByIndexKey(owner, index), tokenId); - - private void ClearTokenOfOwnerByIndex(Address owner, ulong index) => State.Clear(GetTokenOfOwnerByIndexKey(owner, index)); - - private string IndexOfOwnerByTokenKey(Address owner, ulong tokenId) => $"IndexOfOwnerByToken:{owner}:{tokenId}"; - private ulong GetIndexOfOwnerByToken(Address owner, ulong tokenId) => State.GetUInt64(IndexOfOwnerByTokenKey(owner, tokenId)); - private void SetIndexOfOwnerByToken(Address owner, ulong tokenId, ulong index) => State.SetUInt64(IndexOfOwnerByTokenKey(owner, tokenId), index); - private void ClearIndexOfOwnerByToken(Address owner, ulong tokenId) => State.Clear(IndexOfOwnerByTokenKey(owner, tokenId)); - public ulong TotalSupply - { - get => State.GetUInt64(nameof(TotalSupply)); - private set => State.SetUInt64(nameof(TotalSupply), value); - } - - /// - /// Constructor. Initializes the supported interfaces. - /// - /// The smart contract state. - public NonFungibleToken(ISmartContractState state, string name, string symbol) : base(state) - { - // todo: discuss callback handling and supported interface numbering with community. - this.SetSupportedInterfaces((uint)0x00000001, true); // (ERC165) - ISupportsInterface - this.SetSupportedInterfaces((uint)0x00000002, true); // (ERC721) - INonFungibleToken, - this.SetSupportedInterfaces((uint)0x00000003, false); // (ERC721) - INonFungibleTokenReceiver - this.SetSupportedInterfaces((uint)0x00000004, true); // (ERC721) - INonFungibleTokenMetadata - this.SetSupportedInterfaces((uint)0x00000005, true); // (ERC721) - IERC721Enumerable - - this.Name = name; - this.Symbol = symbol; - this.Owner = Message.Sender; - this.NextTokenId = 1; - } - - public ulong TokenByIndex(ulong index) - { - Assert(index < TotalSupply, "The index is invalid."); - - return GetTokenByIndex(index); - } - - public ulong TokenOfOwnerByIndex(Address owner, ulong index) - { - Assert(index < GetOwnerToNFTokenCount(owner), "The index is invalid."); - - return GetTokenOfOwnerByIndex(owner, index); - } - - - - /// - /// Transfers the ownership of an NFT from one address to another address. This function can - /// be changed to payable. - /// - /// Throws unless is the current owner, an authorized operator, or the - /// approved address for this NFT.Throws if 'from' is not the current owner.Throws if 'to' is - /// the zero address.Throws if 'tokenId' is not a valid NFT. When transfer is complete, this - /// function checks if 'to' is a smart contract. If so, it calls - /// 'OnNonFungibleTokenReceived' on 'to' and throws if the return value true. - /// The current owner of the NFT. - /// The new owner. - /// The NFT to transfer. - /// Additional data with no specified format, sent in call to 'to'. - public void SafeTransferFrom(Address from, Address to, ulong tokenId, byte[] data) - { - SafeTransferFromInternal(from, to, tokenId, data); - } - - /// - /// Transfers the ownership of an NFT from one address to another address. This function can - /// be changed to payable. - /// - /// This works identically to the other function with an extra data parameter, except this - /// function just sets data to an empty byte array. - /// The current owner of the NFT. - /// The new owner. - /// The NFT to transfer. - public void SafeTransferFrom(Address from, Address to, ulong tokenId) - { - SafeTransferFromInternal(from, to, tokenId, new byte[0]); - } - - /// - /// Throws unless is the current owner, an authorized operator, or the approved - /// address for this NFT.Throws if is not the current owner.Throws if is the zero - /// address.Throws if is not a valid NFT. This function can be changed to payable. - /// - /// The caller is responsible to confirm that is capable of receiving NFTs or else - /// they maybe be permanently lost. - /// The current owner of the NFT. - /// The new owner. - /// The NFT to transfer. - public void TransferFrom(Address from, Address to, ulong tokenId) - { - CanTransfer(tokenId); - - Address tokenOwner = GetIdToOwner(tokenId); - EnsureAddressIsNotEmpty(tokenOwner); - EnsureAddressIsNotEmpty(to); - Assert(tokenOwner == from); - - TransferInternal(to, tokenId); - } - - /// - /// Set or reaffirm the approved address for an NFT. This function can be changed to payable. - /// - /// - /// The zero address indicates there is no approved address. Throws unless is - /// the current NFT owner, or an authorized operator of the current owner. - /// - /// Address to be approved for the given NFT ID. - /// ID of the token to be approved. - public void Approve(Address approved, ulong tokenId) - { - CanOperate(tokenId); - ValidNFToken(tokenId); - - Address tokenOwner = GetIdToOwner(tokenId); - Assert(approved != tokenOwner); - - SetIdToApproval(tokenId, approved); - LogApproval(tokenOwner, approved, tokenId); - } - - /// - /// Enables or disables approval for a third party ("operator") to manage all of - /// 's assets. It also Logs the ApprovalForAll event. - /// - /// This works even if sender doesn't own any tokens at the time. - /// Address to add to the set of authorized operators. - /// True if the operators is approved, false to revoke approval. - public void SetApprovalForAll(Address operatorAddress, bool approved) - { - SetOwnerToOperator(Message.Sender, operatorAddress, approved); - LogApprovalForAll(Message.Sender, operatorAddress, approved); - } - - /// - /// Returns the number of NFTs owned by 'owner'. NFTs assigned to the zero address are - /// considered invalid, and this function throws for queries about the zero address. - /// - /// Address for whom to query the balance. - /// Balance of owner. - public ulong BalanceOf(Address owner) - { - EnsureAddressIsNotEmpty(owner); - return GetOwnerToNFTokenCount(owner); - } - - /// - /// Returns the address of the owner of the NFT. NFTs assigned to zero address are considered invalid, and queries about them do throw. - /// - /// The identifier for an NFT. - /// Address of tokenId owner. - public Address OwnerOf(ulong tokenId) - { - Address owner = GetIdToOwner(tokenId); - EnsureAddressIsNotEmpty(owner); - return owner; - } - - /// - /// Get the approved address for a single NFT. - /// - /// Throws if 'tokenId' is not a valid NFT. - /// ID of the NFT to query the approval of. - /// Address that tokenId is approved for. - public Address GetApproved(ulong tokenId) - { - ValidNFToken(tokenId); - - return GetIdToApproval(tokenId); - } - - /// - /// Checks if 'operator' is an approved operator for 'owner'. - /// - /// The address that owns the NFTs. - /// The address that acts on behalf of the owner. - /// True if approved for all, false otherwise. - public bool IsApprovedForAll(Address owner, Address operatorAddress) - { - return GetOwnerToOperator(owner, operatorAddress); - } - - /// - /// Actually preforms the transfer. - /// - /// Does NO checks. - /// Address of a new owner. - /// The NFT that is being transferred. - private void TransferInternal(Address to, ulong tokenId) - { - Address from = GetIdToOwner(tokenId); - ClearApproval(tokenId); - - RemoveNFToken(from, tokenId); - AddNFToken(to, tokenId); - - LogTransfer(from, to, tokenId); - } - - /// - /// Removes a NFT from owner. - /// - /// Use and override this function with caution. Wrong usage can have serious consequences. - /// Address from wich we want to remove the NFT. - /// Which NFT we want to remove. - private void RemoveNFToken(Address from, ulong tokenId) - { - Assert(GetIdToOwner(tokenId) == from); - var tokenCount = GetOwnerToNFTokenCount(from); - SetOwnerToNFTokenCount(from, checked(tokenCount - 1)); - State.Clear(GetIdToOwnerKey(tokenId)); - - ulong index = GetIndexOfOwnerByToken(from, tokenId); - ulong lastIndex = tokenCount - 1; - - if (index != lastIndex) - { - ulong lastToken = GetTokenOfOwnerByIndex(from, lastIndex); - SetIndexOfOwnerByToken(from, lastToken, index); - SetTokenOfOwnerByIndex(from, index, lastToken); - } - - ClearTokenOfOwnerByIndex(from, lastIndex); - ClearIndexOfOwnerByToken(from, tokenId); - } - - /// - /// Assignes a new NFT to owner. - /// - /// Use and override this function with caution. Wrong usage can have serious consequences. - /// Address to which we want to add the NFT. - /// Which NFT we want to add. - private void AddNFToken(Address to, ulong tokenId) - { - Assert(GetIdToOwner(tokenId) == Address.Zero); - - SetIdToOwner(tokenId, to); - ulong currentTokenAmount = GetOwnerToNFTokenCount(to); - SetOwnerToNFTokenCount(to, checked(currentTokenAmount + 1)); - - var index = currentTokenAmount; - SetIndexOfOwnerByToken(to, tokenId, index); - SetTokenOfOwnerByIndex(to, index, tokenId); - } - - /// - /// Actually perform the safeTransferFrom. - /// - /// The current owner of the NFT. - /// The new owner. - /// The NFT to transfer. - /// Additional data with no specified format, sent in call to 'to' if it is a contract. - private void SafeTransferFromInternal(Address from, Address to, ulong tokenId, byte[] data) - { - CanTransfer(tokenId); - ValidNFToken(tokenId); - - Address tokenOwner = GetIdToOwner(tokenId); - Assert(tokenOwner == from); - EnsureAddressIsNotEmpty(to); - - TransferInternal(to, tokenId); - - if (State.IsContract(to)) - { - ITransferResult result = Call(to, 0, "OnNonFungibleTokenReceived", new object[] { Message.Sender, from, tokenId, data }, 0); - Assert((bool)result.ReturnValue); - } - } - - /// - /// Clears the current approval of a given NFT ID. - /// - /// ID of the NFT to be transferred - private void ClearApproval(ulong tokenId) - { - if (GetIdToApproval(tokenId) != Address.Zero) - { - State.Clear(GetIdToApprovalKey(tokenId)); - } - } - - /// - /// This logs when ownership of any NFT changes by any mechanism. This event logs when NFTs are - /// created('from' == 0) and destroyed('to' == 0). Exception: during contract creation, any - /// number of NFTs may be created and assigned without logging Transfer.At the time of any - /// transfer, the approved Address for that NFT (if any) is reset to none. - /// - /// The from address. - /// The to address. - /// The NFT ID. - private void LogTransfer(Address from, Address to, ulong tokenId) - { - Log(new TransferLog() { From = from, To = to, TokenId = tokenId }); - } - - /// - /// This logs when the approved Address for an NFT is changed or reaffirmed. The zero - /// Address indicates there is no approved Address. When a Transfer logs, this also - /// indicates that the approved Address for that NFT (if any) is reset to none. - /// - /// The owner address. - /// The approved address. - /// The NFT ID. - private void LogApproval(Address owner, Address approved, ulong tokenId) - { - Log(new ApprovalLog() { Owner = owner, Approved = approved, TokenId = tokenId }); - } - - /// - /// This logs when an operator is enabled or disabled for an owner. The operator can manage all NFTs of the owner. - /// - /// The owner address - /// The operator address. - /// A boolean indicating if it has been approved. - private void LogApprovalForAll(Address owner, Address operatorAddress, bool approved) - { - Log(new ApprovalForAllLog() { Owner = owner, Operator = operatorAddress, Approved = approved }); - } - - - /// - /// Guarantees that the is an owner or operator of the given NFT. - /// - /// ID of the NFT to validate. - private void CanOperate(ulong tokenId) - { - Address tokenOwner = GetIdToOwner(tokenId); - Assert(tokenOwner == Message.Sender || GetOwnerToOperator(tokenOwner, Message.Sender)); - } - - /// - /// Guarantees that the msg.sender is allowed to transfer NFT. - /// - /// ID of the NFT to transfer. - private void CanTransfer(ulong tokenId) - { - Address tokenOwner = GetIdToOwner(tokenId); - Assert( - tokenOwner == Message.Sender - || GetIdToApproval(tokenId) == Message.Sender - || GetOwnerToOperator(tokenOwner, Message.Sender) - ); - } - - /// - /// Guarantees that tokenId is a valid Token. - /// - /// ID of the NFT to validate. - private void ValidNFToken(ulong tokenId) - { - Address tokenOwner = GetIdToOwner(tokenId); - EnsureAddressIsNotEmpty(tokenOwner); - } - - /// - /// Sets the contract owner who can mint/bur - /// - /// - public void TransferOwnership(Address owner) - { - EnsureOwnerOnly(); - EnsureAddressIsNotEmpty(owner); - - Log(new OwnershipTransferedLog { PreviousOwner = Owner, NewOwner = owner }); - - Owner = owner; - } - - private void EnsureOwnerOnly() - { - Assert(Message.Sender == Owner, "Only owner of the contract can set new owner."); - } - - /// - /// Mints new tokens - /// - /// The address that will own the minted NFT - /// Number of tokens will be created - public void MintAll(Address address, ulong amount) - { - EnsureOwnerOnly(); - EnsureAddressIsNotEmpty(address); - Assert(amount > 0, "the amount should be higher than zero"); - - var index = TotalSupply; - var lastIndex = checked(index + amount); - var tokenId = NextTokenId; - - while (index < lastIndex) - { - AddNFToken(address, tokenId); - SetTokenByIndex(index, tokenId); - SetIndexByToken(tokenId, index); - - LogTransfer(Address.Zero, address, tokenId); - - checked - { - index++; - tokenId++; - } - } - - TotalSupply = checked(TotalSupply + amount); - NextTokenId = tokenId; - } - - public void Burn(ulong tokenId) - { - Address tokenOwner = GetIdToOwner(tokenId); - - Assert(tokenOwner == Message.Sender, "Only token owner can burn the token."); - - ClearApproval(tokenId); - RemoveNFToken(tokenOwner, tokenId); - - //move last token to removed token and delete last token info - var index = GetIndexByToken(tokenId); - var lastTokenIndex = checked(--TotalSupply); - var lastToken = GetTokenByIndex(lastTokenIndex); - - SetTokenByIndex(index, lastToken); - SetIndexByToken(lastToken, index); - - ClearTokenByIndex(lastTokenIndex); - ClearIndexByToken(tokenId); - - LogTransfer(tokenOwner, Address.Zero, tokenId); - } - - public void EnsureAddressIsNotEmpty(Address address) - { - Assert(address != Address.Zero, "The address can not be zero."); - } -} \ No newline at end of file diff --git a/Mainnet/STOContract/STOContract/StandardToken.cs b/Mainnet/STOContract/STOContract/StandardToken.cs new file mode 100644 index 00000000..d72fafd7 --- /dev/null +++ b/Mainnet/STOContract/STOContract/StandardToken.cs @@ -0,0 +1,166 @@ +using Stratis.SmartContracts; +using Stratis.SmartContracts.Standards; +/// +/// Implementation of a standard token contract for the Stratis Platform. +/// +public class StandardToken : SmartContract, IStandardToken256 +{ + /// + /// Constructor used to create a new instance of the token. Assigns the total token supply to the creator of the contract. + /// + /// The execution state for the contract. + /// The total token supply. + /// The name of the token. + /// The symbol used to identify the token. + public StandardToken(ISmartContractState smartContractState, UInt256 totalSupply, string name, string symbol, byte decimals) + : base(smartContractState) + { + this.TotalSupply = totalSupply; + this.Name = name; + this.Symbol = symbol; + this.Decimals = decimals; + this.SetBalance(Message.Sender, totalSupply); + } + + public string Symbol + { + get => State.GetString(nameof(this.Symbol)); + private set => State.SetString(nameof(this.Symbol), value); + } + + public string Name + { + get => State.GetString(nameof(this.Name)); + private set => State.SetString(nameof(this.Name), value); + } + + /// + public byte Decimals + { + get => State.GetBytes(nameof(this.Decimals))[0]; + private set => State.SetBytes(nameof(this.Decimals), new[] { value }); + } + + /// + public UInt256 TotalSupply + { + get => State.GetUInt256(nameof(this.TotalSupply)); + private set => State.SetUInt256(nameof(this.TotalSupply), value); + } + + /// + public UInt256 GetBalance(Address address) + { + return State.GetUInt256($"Balance:{address}"); + } + + private void SetBalance(Address address, UInt256 value) + { + State.SetUInt256($"Balance:{address}", value); + } + + /// + public bool TransferTo(Address to, UInt256 amount) + { + if (amount == 0) + { + Log(new TransferLog { From = Message.Sender, To = to, Amount = 0 }); + + return true; + } + + UInt256 senderBalance = GetBalance(Message.Sender); + + if (senderBalance < amount) + { + return false; + } + + SetBalance(Message.Sender, senderBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = Message.Sender, To = to, Amount = amount }); + + return true; + } + + /// + public bool TransferFrom(Address from, Address to, UInt256 amount) + { + if (amount == 0) + { + Log(new TransferLog { From = from, To = to, Amount = 0 }); + + return true; + } + + UInt256 senderAllowance = Allowance(from, Message.Sender); + UInt256 fromBalance = GetBalance(from); + + if (senderAllowance < amount || fromBalance < amount) + { + return false; + } + + SetApproval(from, Message.Sender, senderAllowance - amount); + + SetBalance(from, fromBalance - amount); + + SetBalance(to, checked(GetBalance(to) + amount)); + + Log(new TransferLog { From = from, To = to, Amount = amount }); + + return true; + } + + /// + public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount) + { + if (Allowance(Message.Sender, spender) != currentAmount) + { + return false; + } + + SetApproval(Message.Sender, spender, amount); + + Log(new ApprovalLog { Owner = Message.Sender, Spender = spender, Amount = amount, OldAmount = currentAmount }); + + return true; + } + + private void SetApproval(Address owner, Address spender, UInt256 value) + { + State.SetUInt256($"Allowance:{owner}:{spender}", value); + } + + /// + public UInt256 Allowance(Address owner, Address spender) + { + return State.GetUInt256($"Allowance:{owner}:{spender}"); + } + + public struct TransferLog + { + [Index] + public Address From; + + [Index] + public Address To; + + public UInt256 Amount; + } + + public struct ApprovalLog + { + [Index] + public Address Owner; + + [Index] + public Address Spender; + + public UInt256 OldAmount; + + public UInt256 Amount; + } +}