├── .gitattributes ├── .soliumignore ├── docs ├── eventflow.png ├── gas_usage.png └── eventflow.mmd ├── migrations ├── 1_initial_migration.js └── 2_deploy_cybercon.js ├── .gitignore ├── .soliumrc.json ├── package.json ├── README.md ├── contracts ├── Migrations.sol └── Cybercon.sol ├── test ├── helpers │ └── helpers.js └── cybercon.test.js └── truffle.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/Migrations.sol 3 | -------------------------------------------------------------------------------- /docs/eventflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybercongress/cybaca/HEAD/docs/eventflow.png -------------------------------------------------------------------------------- /docs/gas_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybercongress/cybaca/HEAD/docs/gas_usage.png -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | 4 | infura_rinkeby_deploy.json 5 | infura_main_deploy.json 6 | cybercon0_full.sol 7 | 8 | .DS_Store 9 | yarn.lock 10 | yarn-error.log 11 | package-lock.json -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "quotes": [ 8 | "error", 9 | "double" 10 | ], 11 | "indentation": [ 12 | "error", 13 | 4 14 | ], 15 | "linebreak-style": [ 16 | "error", 17 | "unix" 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /docs/eventflow.mmd: -------------------------------------------------------------------------------- 1 | gantt 2 | dateFormat X 3 | title Cybercon0 Event Flow (Testnet 0/Rinkeby) 4 | 5 | section Flow 6 | Auction : auction, 1543302000, 1543330800 7 | Talk Applications : applications, 1543302000, 1543323600 8 | Talk Accept or Decline : verification, 1543302000, 1543330800 9 | Self Decline Talk : return, 1543323600, 1543330800 10 | CheckIn, Conference : checkin, 1543330800, 1543338000 11 | Distribution : distribution, 1543339800, 1543345200 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cybaca", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "", 6 | "scripts": {}, 7 | "keywords": [], 8 | "author": "@litvintech", 9 | "license": "MIT", 10 | "dependencies": { 11 | "@openzeppelin/contracts": "^2.3.0", 12 | "bn-chai": "^1.0.1", 13 | "chai": "^4.2.0", 14 | "chai-as-promised": "^7.1.1", 15 | "eth-gas-reporter": "^0.1.12", 16 | "ethjs-unit": "^0.1.6", 17 | "mocha": "^5.2.0", 18 | "openzeppelin-test-helpers": "^0.4.2", 19 | "truffle-hdwallet-provider": "^1.0.16" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## cybaca 2 | 3 | Self-driven community event organizer 4 | 5 | ### Prepare 6 | ``` 7 | Truffle and ganache-cli should be installed 8 | ``` 9 | 10 | ### Bootstrap 11 | ``` 12 | npm i 13 | ``` 14 | 15 | ### Tests 16 | ``` 17 | ganache-cli -p 7545 -a 250 -e 100 -i 5777 18 | ``` 19 | 20 | ### Migration 21 | ``` 22 | truffle migrate --network development --reset 23 | ``` 24 | 25 | ### Code for verification 26 | ``` 27 | truffle-flattener contracts/Cybercon.sol > cybercon0_full.sol 28 | ``` 29 | 30 | ### Event Flow 31 | ![eventflow](docs/eventflow.png) 32 | 33 | ### Gas Usage 34 | ![gas_usage](docs/gas_usage.png) 35 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/helpers/helpers.js: -------------------------------------------------------------------------------- 1 | function getRandomString (len, charSet) { 2 | charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 3 | var randomString = ''; 4 | for (var i = 0; i < len; i++) { 5 | var randomPoz = Math.floor(Math.random() * charSet.length); 6 | randomString += charSet.substring(randomPoz,randomPoz+1); 7 | } 8 | return randomString; 9 | } 10 | 11 | function getRandomInt (min, max) { 12 | min = Math.ceil(min); 13 | max = Math.floor(max); 14 | return Math.floor(Math.random() * (max - min + 1)) + min; 15 | } 16 | 17 | module.exports = { 18 | getRandomString, 19 | getRandomInt 20 | } -------------------------------------------------------------------------------- /migrations/2_deploy_cybercon.js: -------------------------------------------------------------------------------- 1 | var Cybercon = artifacts.require("Cybercon"); 2 | const Web3 = require('web3'); 3 | const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:7545')); 4 | require('openzeppelin-test-helpers/configure')({ web3: web3 }); 5 | const { BN } = require('openzeppelin-test-helpers'); 6 | 7 | module.exports = async function(deployer) { 8 | 9 | const TALKS_APPLICATION_END = Math.round(Date.now() / 1000) + 2*24*60*60; 10 | const CHECKIN_START = TALKS_APPLICATION_END + 1*24*60*60; 11 | const CHECKIN_END = CHECKIN_START + 12*60*60; 12 | const DISTRIBUTION_START = CHECKIN_END + 1*60*60; 13 | 14 | const INITIAL_PRICE = web3.utils.toWei(new BN(3000), 'finney'); 15 | const MINIMAL_PRICE = web3.utils.toWei(new BN(500), 'finney'); 16 | const BID_BLOCK_DECREASE = web3.utils.toWei(new BN(30), 'szabo'); 17 | const MINIMAL_SPEAKER_DEPOSIT = web3.utils.toWei(new BN(3000), 'finney'); 18 | 19 | const CYBERCON_NAME = "cyberc0n"; 20 | const CYBERCON_SYMBOL = "CYBERC0N"; 21 | const CYBERCON_DESCRIPTION = "CYBACA RELEASE"; 22 | const CYBERCON_PLACE = "Andromeda"; 23 | 24 | const TICKETS_AMOUNT = 146; 25 | const SPEAKERS_SLOTS = 24; 26 | 27 | const TIMINGS_SET = [TALKS_APPLICATION_END, CHECKIN_START, CHECKIN_END, DISTRIBUTION_START]; 28 | const ECONOMY_SET = [INITIAL_PRICE, MINIMAL_PRICE, BID_BLOCK_DECREASE, MINIMAL_SPEAKER_DEPOSIT]; 29 | const EVENT_SET = [TICKETS_AMOUNT, SPEAKERS_SLOTS] 30 | 31 | const cybaca = await deployer.deploy( 32 | Cybercon, 33 | TIMINGS_SET, 34 | ECONOMY_SET, 35 | EVENT_SET, 36 | CYBERCON_NAME, 37 | CYBERCON_SYMBOL, 38 | CYBERCON_DESCRIPTION, 39 | CYBERCON_PLACE 40 | ); 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | const { toWei } = require('ethjs-unit'); 2 | const HDWalletProvider = require('truffle-hdwallet-provider'); 3 | 4 | const infuraConfigRinkeby = require('./infura_rinkeby_deploy.json'); 5 | const infuraConfigMainnet = require('./infura_main_deploy.json'); 6 | 7 | module.exports = { 8 | 9 | migrations_directory: "./migrations", 10 | 11 | networks: { 12 | infura_rinkeby: { 13 | provider() { 14 | return new HDWalletProvider(infuraConfigRinkeby.privateKey, infuraConfigRinkeby.infuraUrl); 15 | }, 16 | from: infuraConfigRinkeby.fromAddress, 17 | network_id: 4, 18 | gasPrice: toWei(10, 'gwei').toNumber(), 19 | gas: toWei(6, 'mwei').toNumber() 20 | }, 21 | 22 | infura_main: { 23 | provider() { 24 | return new HDWalletProvider(infuraConfigMainnet.privateKey, infuraConfigMainnet.infuraUrl); 25 | }, 26 | from: infuraConfigMainnet.fromAddress, 27 | network_id: 0, 28 | gasPrice: toWei(50, 'gwei').toNumber(), 29 | gas: toWei(6, 'mwei').toNumber() 30 | }, 31 | 32 | development: { 33 | host: "localhost", 34 | port: 7545, 35 | network_id: "5777" 36 | } 37 | }, 38 | 39 | compilers: { 40 | solc: { 41 | version: "0.5.0", 42 | settings: { 43 | optimizer: { 44 | enabled: true, 45 | runs: 500 46 | }, 47 | evmVersion: "byzantium" 48 | } 49 | } 50 | }, 51 | 52 | mocha: { 53 | reporter: 'eth-gas-reporter', 54 | reporterOptions: { 55 | currency: 'USD', 56 | gasPrice: 10 57 | } 58 | } 59 | }; -------------------------------------------------------------------------------- /test/cybercon.test.js: -------------------------------------------------------------------------------- 1 | const { getRandomString, getRandomInt } = require('./helpers/helpers') 2 | const { BN, time } = require('openzeppelin-test-helpers'); 3 | 4 | const chai = require("chai"); 5 | const bnChai = require("bn-chai"); 6 | const expect = chai.expect; 7 | 8 | chai.use(bnChai(BN)); 9 | chai.use(require('chai-as-promised')) 10 | chai.should(); 11 | 12 | const Cybercon = artifacts.require("Cybercon"); 13 | 14 | contract("Cybercon", (accounts) => { 15 | 16 | let cybercon; 17 | let auctionStartBlock; 18 | let auctionStartTime; 19 | let ticketsFunds = new BN('0'); 20 | let ticketsBids = []; 21 | let buyTicketTxs = []; 22 | 23 | const ApplicationStatus = { 24 | Applied: 0, 25 | Accepted: 1, 26 | Declined: 2 27 | } 28 | 29 | function calculatedPrice(currentBlock) { 30 | let passedBlocks = (new BN(currentBlock.toString())).sub(new BN(auctionStartBlock.toString())); 31 | let currentDiscount = passedBlocks.mul(BID_BLOCK_DECREASE); 32 | if ((INITIAL_PRICE.sub(MINIMAL_PRICE)).cmp(currentDiscount)) { 33 | return INITIAL_PRICE.sub(currentDiscount); 34 | } else { return MINIMAL_PRICE; } 35 | } 36 | 37 | function calculatedOrganizersShares(time) { 38 | let timeDiff = (new BN(time.toString())).sub(auctionStartTime); 39 | let totalTime = (new BN(CHECKIN_START.toString())).sub(new BN(auctionStartTime.toString())); 40 | let timeDiff100 = new BN(timeDiff.mul(new BN('100'))); // refactor this line 41 | let mul = timeDiff100.div(totalTime); // with thiss 42 | let divShares = (new BN(SPEAKERS_START_SHARES.toString())).sub(new BN(SPEAKERS_END_SHARES.toString())); 43 | let divSharesMul = divShares.mul(mul); 44 | let calcCurrentSharesAdd = divSharesMul.div(new BN('100')); 45 | let organizerShares = calcCurrentSharesAdd.add(new BN(SPEAKERS_END_SHARES.toString())); 46 | return organizerShares; 47 | } 48 | 49 | const CYBERCON_ORGANIZER = accounts[0]; 50 | 51 | const CYBERCON_NAME = "cyberc1n"; 52 | const CYBERCON_SYMBOL = "CYBERC1N"; 53 | const CYBERCON_DESCRIPTION = "CYBACA RELEASE"; 54 | const CYBERCON_PLACE = "Phangan"; 55 | 56 | const TICKETS_AMOUNT = 146; 57 | const SPEAKERS_SLOTS = 24; 58 | 59 | const SPEAKERS_START_SHARES = 80; 60 | const SPEAKERS_END_SHARES = 20; 61 | 62 | const INITIAL_PRICE = web3.utils.toWei(new BN(3000), 'finney'); 63 | const MINIMAL_PRICE = web3.utils.toWei(new BN(500), 'finney'); 64 | const BID_BLOCK_DECREASE = web3.utils.toWei(new BN(30), 'szabo'); 65 | const MINIMAL_SPEAKER_DEPOSIT = web3.utils.toWei(new BN(1000), 'finney'); 66 | 67 | const TALKS_APPLICATION_END = Math.round(Date.now() / 1000) + 2*24*60*60; 68 | const CHECKIN_START = TALKS_APPLICATION_END + 1*24*60*60; 69 | const CHECKIN_END = CHECKIN_START + 12*60*60; 70 | const DISTRIBUTION_START = CHECKIN_END + 1*60*60; 71 | 72 | const TIMINGS_SET = [TALKS_APPLICATION_END, CHECKIN_START, CHECKIN_END, DISTRIBUTION_START]; 73 | const ECONOMY_SET = [INITIAL_PRICE, MINIMAL_PRICE, BID_BLOCK_DECREASE, MINIMAL_SPEAKER_DEPOSIT]; 74 | const EVENT_SET = [TICKETS_AMOUNT, SPEAKERS_SLOTS] 75 | 76 | before(async () => { 77 | 78 | cybercon = await Cybercon.new( 79 | TIMINGS_SET, 80 | ECONOMY_SET, 81 | EVENT_SET, 82 | CYBERCON_NAME, 83 | CYBERCON_SYMBOL, 84 | CYBERCON_DESCRIPTION, 85 | CYBERCON_PLACE, 86 | { from: CYBERCON_ORGANIZER } 87 | ); 88 | auctionStartBlock = await cybercon.getAuctionStartBlock(); 89 | auctionStartTime = await cybercon.getAuctionStartTime(); 90 | console.log("cybercon0 address: ", cybercon.address); 91 | console.log("deployed time: ", auctionStartTime.toString()); 92 | }) 93 | 94 | describe("when deployed", () => { 95 | it("name should equal", async() => { 96 | (await cybercon.name()).should.equal(CYBERCON_NAME); 97 | }); 98 | 99 | it("token symbol should equal", async() => { 100 | (await cybercon.symbol()).should.equal(CYBERCON_SYMBOL); 101 | }); 102 | 103 | it("checkin start time should equal", async() => { 104 | (await cybercon.getEventStartTime()).toNumber().should.equal(CHECKIN_START); 105 | }); 106 | 107 | it("checkin end time should equal", async() => { 108 | (await cybercon.getEventEndTime()).toNumber().should.equal(CHECKIN_END); 109 | }); 110 | 111 | it("distribution start time time should equal", async() => { 112 | (await cybercon.getDistributionTime()).toNumber().should.equal(DISTRIBUTION_START); 113 | }); 114 | 115 | it("initial organizers and speakers shares should be equal", async() => { 116 | (await cybercon.getOrganizersShares()).toNumber().should.equal(SPEAKERS_END_SHARES); 117 | (await cybercon.getSpeakersShares()).toNumber().should.equal(SPEAKERS_START_SHARES); 118 | (SPEAKERS_END_SHARES + SPEAKERS_START_SHARES).should.be.equal(100); 119 | }); 120 | 121 | it("tickets amount should equal", async() => { 122 | (await cybercon.getTicketsAmount()).toNumber().should.equal(TICKETS_AMOUNT); 123 | }); 124 | 125 | it("speakers slots should equeal", async() => { 126 | (await cybercon.getSpeakersSlots()).toNumber().should.equal(SPEAKERS_SLOTS); 127 | }); 128 | 129 | it("place symbol should equeal", async() => { 130 | (await cybercon.getPlace()).should.equal(CYBERCON_PLACE); 131 | }); 132 | 133 | it("initial price should be equal", async() => { 134 | expect(await cybercon.getCurrentPrice()).to.eq.BN(calculatedPrice(await time.latestBlock())); 135 | }); 136 | }) 137 | 138 | describe("when auction started", () => { 139 | it("should start dutch auction, distribute first half of tickets", async() => { 140 | for (var i = 0; i < TICKETS_AMOUNT/2; i++){ 141 | let bid = calculatedPrice((await time.latestBlock()).add(new BN('1'))); 142 | ticketsBids.push(bid); 143 | let tx = await cybercon.buyTicket({ from: accounts[i], value: bid }).should.be.fulfilled; 144 | buyTicketTxs.push(tx); 145 | ticketsFunds.iadd(bid); 146 | 147 | let bidFromContract = await cybercon.getTicket(i); 148 | expect(bidFromContract[0]).to.eq.BN(bid); 149 | bidFromContract[1].should.be.equal(accounts[i]); 150 | bidFromContract[2].should.be.equal(false); 151 | await cybercon.buyTicket({ from: accounts[i], value: bid }).should.be.rejected; 152 | 153 | await time.increase(50); 154 | } 155 | 156 | expect(await web3.eth.getBalance(cybercon.address)).to.eq.BN(ticketsFunds); 157 | expect(await cybercon.getTicketsFunds()).to.eq.BN(ticketsFunds); 158 | }); 159 | 160 | it("should apply speakers", async() => { 161 | let speakersDeposits = new BN('0'); 162 | for (var i = 200; i < 232; i++){ 163 | let talk = { 164 | sn: getRandomString(32), 165 | ds: getRandomString(128), 166 | dt: getRandomString(256), 167 | du: getRandomInt(900, 3600), 168 | va: (new BN(getRandomInt(1000, 5000).toString())).mul(new BN('1000000000000000')), 169 | pr: getRandomString(132) 170 | } 171 | await cybercon.applyForTalk(talk.sn, talk.ds, talk.dt, talk.du, talk.pr, 172 | { 173 | from: accounts[i], 174 | value: talk.va 175 | } 176 | ).should.be.fulfilled; 177 | speakersDeposits.iadd(talk.va); 178 | 179 | let talkFromContract = await cybercon.getTalkById(i-200); 180 | talkFromContract[0].should.be.equal(talk.sn); 181 | talkFromContract[1].should.be.equal(talk.ds); 182 | talkFromContract[2].should.be.equal(talk.dt); 183 | talkFromContract[3].toNumber().should.be.equal(talk.du); 184 | expect(talkFromContract[4]).to.eq.BN(talk.va); 185 | talkFromContract[5].should.be.equal(accounts[i]); 186 | talkFromContract[8].toNumber().should.be.equal(ApplicationStatus.Applied); 187 | talkFromContract[9].should.be.equal(talk.pr); 188 | 189 | await time.increase(60); 190 | } 191 | 192 | expect(await web3.eth.getBalance(cybercon.address)).to.eq.BN(ticketsFunds.add(speakersDeposits)); 193 | (await cybercon.totalSupply()).toNumber().should.be.equal(TICKETS_AMOUNT/2); 194 | }); 195 | 196 | it("should allow speakers to udpate their talks", async() => { 197 | for (var i = 200; i < 232; i++){ 198 | let talk = { 199 | ds: getRandomString(128), 200 | dt: getRandomString(256), 201 | pr: getRandomString(132) 202 | } 203 | await cybercon.updateTalkDescription((i-200), talk.ds, talk.dt, talk.pr, 204 | { 205 | from: accounts[i] 206 | } 207 | ).should.be.fulfilled; 208 | 209 | let talkFromContract = await cybercon.getTalkById(i-200); 210 | talkFromContract[1].should.be.equal(talk.ds); 211 | talkFromContract[2].should.be.equal(talk.dt); 212 | talkFromContract[9].should.be.equal(talk.pr); 213 | } 214 | }) 215 | 216 | it("should allow organizer accept talks", async() => { 217 | for (var i = 200; i < 200 + SPEAKERS_SLOTS; i++){ 218 | await cybercon.acceptTalk((i-200), 219 | { 220 | from: CYBERCON_ORGANIZER 221 | }).should.be.fulfilled; 222 | let talkFromContract = await cybercon.getTalkById(i-200); 223 | talkFromContract[8].toNumber().should.be.equal(ApplicationStatus.Accepted); 224 | } 225 | (await cybercon.getAvailableSpeaksersSlots()).toNumber().should.be.equal(0); 226 | }) 227 | 228 | it("should allow organizer to decline talks", async() => { 229 | for (var i = 200 + SPEAKERS_SLOTS; i < 230; i++){ 230 | let declinedSpeakerBalanceBefore = new BN(await web3.eth.getBalance(accounts[i])); 231 | await cybercon.declineTalk((i-200), 232 | { 233 | from: CYBERCON_ORGANIZER 234 | }).should.be.fulfilled; 235 | let declinedSpeakerBalanceAfter = new BN(await web3.eth.getBalance(accounts[i])); 236 | let talkFromContract = await cybercon.getTalkById(i-200); 237 | talkFromContract[8].toNumber().should.be.equal(ApplicationStatus.Declined); 238 | expect(declinedSpeakerBalanceAfter.sub(declinedSpeakerBalanceBefore)).to.eq.BN(talkFromContract[4]); 239 | } 240 | }) 241 | 242 | it("should allow self decline for speaker", async() => { 243 | await time.increaseTo(TALKS_APPLICATION_END); 244 | for (var i = 230; i < 232; i++){ 245 | let declinedSpeakerBalanceBefore = new BN(await web3.eth.getBalance(accounts[i])); 246 | await cybercon.selfDeclineTalk((i-200), 247 | { 248 | from: accounts[i] 249 | }).should.be.fulfilled; 250 | let declinedSpeakerBalanceAfter = new BN(await web3.eth.getBalance(accounts[i])); 251 | let talkFromContract = await cybercon.getTalkById(i-200); 252 | talkFromContract[8].toNumber().should.be.equal(ApplicationStatus.Declined); 253 | expect(declinedSpeakerBalanceAfter.sub(declinedSpeakerBalanceBefore)).to.gt.BN(new BN('0')); 254 | } 255 | }) 256 | 257 | it("shoud continuon the dutch auction, distribute second half of tickets", async() => { 258 | for (var i = TICKETS_AMOUNT/2; i < TICKETS_AMOUNT; i++){ 259 | let bid = await calculatedPrice((await time.latestBlock()).add(new BN('1'))); 260 | ticketsBids.push(bid); 261 | let tx = await cybercon.buyTicket({ from: accounts[i], value: bid }).should.be.fulfilled; 262 | buyTicketTxs.push(tx); 263 | ticketsFunds.iadd(bid); 264 | 265 | let bidFromContract = await cybercon.getTicket(i); 266 | expect(bidFromContract[0]).to.eq.BN(bid); 267 | bidFromContract[1].should.be.equal(accounts[i]); 268 | bidFromContract[2].should.be.equal(false); 269 | await cybercon.buyTicket({ from: accounts[i], value: bid }).should.be.rejected; 270 | 271 | await time.increase(10); 272 | } 273 | 274 | expect(await cybercon.getTicketsFunds()).to.eq.BN(ticketsFunds); 275 | expect(ticketsBids.slice(-1)[0]).to.eq.BN(await cybercon.getEndPrice()); 276 | (await cybercon.getTicketsAmount()).toNumber().should.be.equal(0); 277 | (await cybercon.totalSupply()).toNumber().should.be.equal(TICKETS_AMOUNT); 278 | }); 279 | }) 280 | 281 | describe("when conference starts", () => { 282 | it("shoud checkin speaksers, send tokens for them", async() => { 283 | await time.increaseTo(CHECKIN_START); 284 | for (var i = 200; i < 200 + SPEAKERS_SLOTS; i++){ 285 | await time.increase(100); 286 | await cybercon.checkinSpeaker(i-200, { from: CYBERCON_ORGANIZER }).should.be.fulfilled; 287 | let talkFromContract = await cybercon.getTalkById(i-200); 288 | talkFromContract[7].should.be.equal(true); 289 | } 290 | (await cybercon.totalSupply()).toNumber().should.be.equal(TICKETS_AMOUNT+SPEAKERS_SLOTS); 291 | }) 292 | 293 | it("should allow organizer checkin members", async() => { 294 | for (var i = 0; i < TICKETS_AMOUNT; i++){ 295 | await cybercon.checkinMember(i, { from: accounts[i] }); 296 | let bidFromContract = await cybercon.getTicket(i); 297 | bidFromContract[2].should.be.equal(true); 298 | } 299 | }) 300 | }) 301 | 302 | describe("when distribution starts", () => { 303 | 304 | it("should correctly distribute overbids", async() => { 305 | await time.increaseTo(DISTRIBUTION_START) 306 | let membersBalancesBefore = []; 307 | let endPrice = new BN(await cybercon.getEndPrice()); 308 | for (var i = 0; i < TICKETS_AMOUNT; i++){ 309 | membersBalancesBefore.push(new BN(await web3.eth.getBalance(accounts[i]))); 310 | } 311 | await cybercon.distributeOverbids(0, TICKETS_AMOUNT/2-1, { from: CYBERCON_ORGANIZER }).should.be.fulfilled; 312 | await cybercon.distributeOverbids(TICKETS_AMOUNT/2, TICKETS_AMOUNT-1, { from: CYBERCON_ORGANIZER }).should.be.fulfilled; 313 | // for (var i = 0; i < TICKETS_AMOUNT; i++){ 314 | // let balanceAfter = new BN(await web3.eth.getBalance(accounts[i])); 315 | // let diff = new BN(balanceAfter.sub(membersBalancesBefore[i])); 316 | // let overbid = new BN(ticketsBids[i].sub(endPrice)); 317 | // expect(diff).to.eq.BN(overbid); 318 | // } 319 | expect(await cybercon.getAmountReturnedOverbids()).to.eq.BN(new BN(TICKETS_AMOUNT.toString())); 320 | }) 321 | 322 | it("should correctly distribute rewards", async() => { 323 | let speakersBalances = []; 324 | for (var i = 200; i < 200+SPEAKERS_SLOTS; i++){ 325 | speakersBalances.push(new BN(await web3.eth.getBalance(accounts[i]))); 326 | } 327 | let calculatedShares = calculatedOrganizersShares(await cybercon.getAuctionEndTime()); 328 | let calculatedSpeakersShares = (new BN('100')).sub(calculatedShares); 329 | console.log("_______________"); 330 | console.log("Calculated Speakers Shares: ", calculatedSpeakersShares.toString(10)); 331 | console.log("Actual Speakers shares: ", (await cybercon.getSpeakersShares()).toString()); 332 | console.log("_______________"); 333 | expect(await cybercon.getSpeakersShares()).to.eq.BN(calculatedSpeakersShares); 334 | 335 | let endPrice = new BN(await cybercon.getEndPrice()); 336 | console.log("End Price: ", web3.utils.fromWei(await cybercon.getEndPrice(), 'ether').toString()); 337 | console.log("_______________"); 338 | let valueFromTicketsForReward = endPrice.mul(new BN(TICKETS_AMOUNT)); 339 | console.log("Tickets Funds: ", web3.utils.fromWei(valueFromTicketsForReward, 'ether').toString()); 340 | console.log("Speakers part of Funds: ", web3.utils.fromWei(valueFromTicketsForReward.mul(calculatedSpeakersShares).div(new BN('100')), 'ether').toString()); 341 | console.log("Organers part of Funds: ", web3.utils.fromWei(valueFromTicketsForReward.mul(calculatedShares).div(new BN('100')), 'ether').toString()); 342 | console.log("_______________"); 343 | let organizerBalance = new BN(await web3.eth.getBalance(CYBERCON_ORGANIZER)); 344 | let tx = await cybercon.distributeRewards({ from: CYBERCON_ORGANIZER }).should.be.fulfilled; 345 | console.log("Gas used by organizer to call distributeRewards: ", ((new BN(tx.receipt.gasUsed.toString())).mul(new BN('20000000000'))).toString()); 346 | let valuePerSpeaker = (valueFromTicketsForReward.mul(calculatedSpeakersShares).div(new BN('100'))).div(new BN(SPEAKERS_SLOTS.toString())); 347 | for (var i = 200; i < 200+SPEAKERS_SLOTS; i++){ 348 | let talkFromContract = await cybercon.getTalkById(i-200); 349 | let speakerBalanceAfter = new BN(await web3.eth.getBalance(accounts[i])); 350 | console.log("Speaker's talk ID: ", (i-200)); 351 | console.log("Value for speaker's from tickets: ", web3.utils.fromWei(valuePerSpeaker, 'ether')); 352 | console.log("Deposit of speaker's: ", web3.utils.fromWei(talkFromContract[4], 'ether')); 353 | console.log("Speaker's balance before: ", web3.utils.fromWei(speakersBalances[i-200], 'ether')); 354 | console.log("Speaker's balance after: ", web3.utils.fromWei(speakerBalanceAfter, 'ether')); 355 | console.log("Speaker's balance diff: ", web3.utils.fromWei(speakerBalanceAfter.sub(speakersBalances[i-200]), 'ether')); 356 | console.log("_______________"); 357 | let balanceDiff = speakerBalanceAfter.sub(speakersBalances[i-200]); 358 | let payment = valuePerSpeaker.add(new BN(talkFromContract[4])); 359 | expect(balanceDiff).to.eq.BN(payment); 360 | } 361 | let valueForOrganizer = (valueFromTicketsForReward.mul(calculatedShares).div(new BN('100'))); 362 | let organizerBalanceAfter = new BN(await web3.eth.getBalance(CYBERCON_ORGANIZER)); 363 | console.log("Value for organizer: ", web3.utils.fromWei(valueForOrganizer, 'ether')); 364 | console.log("Organizer's balance before: ", web3.utils.fromWei(organizerBalance, 'ether')); 365 | console.log("Organizer's balance after: ", web3.utils.fromWei(organizerBalanceAfter, 'ether')); 366 | console.log("Organizer's balance diff: ", web3.utils.fromWei((organizerBalanceAfter.sub(organizerBalance)), 'ether')); 367 | // expect(organizerBalanceAfter.sub(organizerBalance)).to.eq.BN(valueForOrganizer.sub((new BN(tx.receipt.gasUsed.toString())).mul(new BN('20000000000')))); 368 | }) 369 | }) 370 | }) -------------------------------------------------------------------------------- /contracts/Cybercon.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 4 | import "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol"; 5 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 6 | import "openzeppelin-solidity/contracts/utils/Address.sol"; 7 | 8 | 9 | contract Cybercon is Ownable, ERC721Full { 10 | 11 | using SafeMath for uint256; 12 | using Address for address; 13 | using Address for address payable; 14 | 15 | enum ApplicationStatus {Applied, Accepted, Declined} 16 | 17 | struct Talk { 18 | string speakerName; 19 | string descSpeaker; 20 | string deskTalk; 21 | uint256 duration; 22 | uint256 deposit; 23 | address payable speakerAddress; 24 | uint256 appliedAt; 25 | bool checkedIn; 26 | ApplicationStatus status; 27 | string proof; 28 | } 29 | struct Ticket { 30 | uint256 value; 31 | address payable bidderAddress; 32 | bool checkedIn; 33 | bool overbidReturned; 34 | } 35 | struct CommunityBuilderMessage { 36 | string message; 37 | string link1; 38 | string link2; 39 | uint256 donation; 40 | } 41 | 42 | uint256 private auctionStartBlock; 43 | uint256 private auctionStartTime; 44 | uint256 private talksApplicationEnd; 45 | uint256 private checkinStart; 46 | uint256 private checkinEnd; 47 | uint256 private distributionStart; 48 | uint256 private auctionEnd; 49 | 50 | uint256 private initialPrice; 51 | uint256 private minimalPrice; 52 | uint256 private bidBlockDecrease; 53 | uint256 private endPrice; 54 | 55 | uint256 private ticketsAmount; 56 | uint256 private totalTickets; 57 | uint256 private speakersSlots; 58 | uint256 private minimalSpeakerDeposit; 59 | uint256 private ticketsFunds = 0; 60 | uint256 private acceptedSpeakersSlots = 0; 61 | uint256 constant private SPEAKERS_START_SHARES = 80; 62 | uint256 constant private SPEAKERS_END_SHARES = 20; 63 | 64 | string private eventPlace; 65 | string private eventDescription; 66 | 67 | mapping(address => bool) private membersBidded; 68 | mapping(address => uint256) private ticketByAddressIndex; 69 | uint256 private amountReturnedBids = 0; 70 | bool private overbidsDistributed = false; 71 | 72 | Talk[] private speakersTalks; 73 | Ticket[] private membersTickets; 74 | CommunityBuilderMessage[] private communityBuildersBoard; 75 | 76 | string private talksGrid = ""; 77 | string private workshopsGrid = ""; 78 | 79 | event TicketBid( 80 | uint256 _id, 81 | address _member, 82 | uint256 _value 83 | ); 84 | 85 | event TalkApplication( 86 | string _name, 87 | address _member, 88 | uint256 _value 89 | ); 90 | 91 | event BuilderMessage( 92 | uint256 _talkId, 93 | string _message, 94 | string _link1, 95 | string _link2 96 | ); 97 | 98 | event TalkAccepted(uint256 _talkId); 99 | event TalkDeclined(uint256 _talkId); 100 | event TalkSelfDeclined(uint256 _talkId); 101 | event MemberCheckin(uint256 _ticketId); 102 | event SpeakerCheckin(uint256 _talkId); 103 | event OverbidsDistributed(); 104 | event RewardsDistributed(); 105 | event UpdatedTalksGrid(string _grid); 106 | event UpdatedWorkshopsGrid(string _grid); 107 | event debug(uint256 _passed); 108 | 109 | 110 | // tickets amount 111 | // ticket fix price 112 | // organizator bond amount 113 | // max time investments round 114 | // limb time 115 | // speculative time? 116 | 117 | //function cancel 118 | 119 | 120 | 121 | constructor( 122 | uint256[] memory _timingsSet, 123 | uint256[] memory _economySet, 124 | uint256[] memory _eventSet, 125 | string memory _name, 126 | string memory _symbol, 127 | string memory _description, 128 | string memory _place 129 | ) ERC721Full(_name, _symbol) 130 | public 131 | { 132 | require(_timingsSet[0] > block.timestamp, "talks applications end should be later than auction started"); // solium-disable-line security/no-block-members, max-len 133 | require(_timingsSet[1] > _timingsSet[0], "checkin start should be later than applications end"); 134 | require(_timingsSet[2] > _timingsSet[1], "checking end should later than checkin start"); 135 | require(_timingsSet[3] > _timingsSet[2], "distribution end should be later than checkin end"); 136 | 137 | require(_economySet[0] > _economySet[1], "start price should be highter than minimal price"); 138 | require(_economySet[3] >= 1 ether, "minimal speaker deposit should be highter than 1 eth"); 139 | 140 | require(_eventSet[0] <= 200, "tickets amount should be less than 200"); 141 | require(_eventSet[1] <= 24, "speaker's slots amount should be less than 24"); 142 | 143 | auctionStartBlock = block.number; 144 | auctionStartTime = block.timestamp; // solium-disable-line security/no-block-members 145 | 146 | talksApplicationEnd = _timingsSet[0]; 147 | checkinStart = _timingsSet[1]; 148 | checkinEnd = _timingsSet[2]; 149 | distributionStart = _timingsSet[3]; 150 | auctionEnd = checkinStart; 151 | 152 | initialPrice = _economySet[0]; 153 | minimalPrice = _economySet[1]; 154 | bidBlockDecrease = _economySet[2]; 155 | minimalSpeakerDeposit = _economySet[3]; 156 | endPrice = minimalPrice; 157 | 158 | ticketsAmount = _eventSet[0]; 159 | speakersSlots = _eventSet[1]; 160 | totalTickets = ticketsAmount; 161 | 162 | eventPlace = _place; 163 | eventDescription = _description; 164 | } 165 | 166 | function() external {} 167 | 168 | modifier beforeApplicationStop() { 169 | require(block.timestamp < talksApplicationEnd, "only before application finished"); // solium-disable-line security/no-block-members 170 | _; 171 | } 172 | 173 | modifier beforeEventStart() { 174 | require(block.timestamp < checkinStart, "only before event started"); // solium-disable-line security/no-block-members 175 | _; 176 | } 177 | 178 | modifier duringEvent() { 179 | require(block.timestamp >= checkinStart && block.timestamp <= checkinEnd, "only when event going"); // solium-disable-line security/no-block-members, max-len 180 | _; 181 | } 182 | 183 | modifier afterDistributionStart() { 184 | require(block.timestamp > distributionStart, "only when distribuion allowed"); // solium-disable-line security/no-block-members 185 | _; 186 | } 187 | 188 | function buyTicket() 189 | external 190 | beforeEventStart 191 | payable 192 | { 193 | require(msg.value >= getCurrentPrice(), "bid should be equal or more than current price"); 194 | require(membersBidded[msg.sender] == false, "should buy only one ticket by address"); 195 | require(ticketsAmount > 0, "no more tickets, sorry"); 196 | 197 | uint256 bidId = totalSupply(); 198 | membersTickets.push(Ticket(msg.value, msg.sender, false, false)); 199 | super._mint(msg.sender, bidId); 200 | membersBidded[msg.sender] = true; 201 | ticketByAddressIndex[msg.sender] = bidId; 202 | ticketsFunds = ticketsFunds.add(msg.value); 203 | ticketsAmount = ticketsAmount.sub(1); 204 | 205 | if (ticketsAmount == 0) { 206 | auctionEnd = block.timestamp; // solium-disable-line security/no-block-members 207 | endPrice = getCurrentPrice(); 208 | } 209 | 210 | emit TicketBid(bidId, msg.sender, msg.value); 211 | } 212 | 213 | function applyForTalk( 214 | string calldata _speakerName, 215 | string calldata _descSpeaker, 216 | string calldata _deskTalk, 217 | uint256 _duration, 218 | string calldata _proof 219 | ) 220 | external 221 | beforeApplicationStop 222 | payable 223 | { 224 | require(_duration >= 900 && _duration <= 3600, "talks duration should me in given limits"); 225 | require(msg.value >= minimalSpeakerDeposit, "amount should be equal or more than minimal deposit"); 226 | require(speakersTalks.length < 36, "exceed amount of speakers applications"); 227 | 228 | Talk memory t = (Talk( 229 | { 230 | speakerName: _speakerName, 231 | descSpeaker: _descSpeaker, 232 | deskTalk: _deskTalk, 233 | duration: _duration, 234 | deposit: msg.value, 235 | speakerAddress: msg.sender, 236 | appliedAt: block.timestamp, // solium-disable-line security/no-block-members, no-trailing-whitespace, whitespace 237 | checkedIn: false, 238 | status: ApplicationStatus.Applied, 239 | proof: _proof 240 | })); 241 | speakersTalks.push(t); 242 | 243 | emit TalkApplication(_speakerName, msg.sender, msg.value); 244 | } 245 | 246 | function sendCommunityBuilderMessage( 247 | uint256 _talkId, 248 | string calldata _message, 249 | string calldata _link1, 250 | string calldata _link2 251 | ) 252 | external 253 | beforeEventStart 254 | payable 255 | { 256 | require(speakersTalks[_talkId].speakerAddress == msg.sender, "should allow to add builder message only for speaker //todo remove this"); 257 | require(speakersTalks[_talkId].status == ApplicationStatus.Accepted, "should allow add builder message only for accepted speakers"); 258 | require(msg.value > 0, "builder should provide fee"); 259 | 260 | CommunityBuilderMessage memory m = (CommunityBuilderMessage( 261 | { 262 | message: _message, 263 | link1: _link1, 264 | link2: _link2, 265 | donation: msg.value 266 | })); 267 | communityBuildersBoard.push(m); 268 | 269 | emit BuilderMessage(_talkId, _message, _link1, _link2); 270 | } 271 | 272 | function updateTalkDescription( 273 | uint256 _talkId, 274 | string calldata _descSpeaker, 275 | string calldata _deskTalk, 276 | string calldata _proof 277 | ) 278 | external 279 | beforeApplicationStop 280 | { 281 | require(msg.sender == speakersTalks[_talkId].speakerAddress, "should allow change only given speakers talk's data"); 282 | speakersTalks[_talkId].descSpeaker = _descSpeaker; 283 | speakersTalks[_talkId].deskTalk = _deskTalk; 284 | speakersTalks[_talkId].proof = _proof; 285 | } 286 | 287 | function acceptTalk(uint256 _talkId) 288 | external 289 | onlyOwner 290 | beforeEventStart 291 | { 292 | require(acceptedSpeakersSlots < speakersSlots, "should allow accept amount of talks which less than available slots"); 293 | require(speakersTalks[_talkId].status == ApplicationStatus.Applied, "should only accept not declined talks"); 294 | acceptedSpeakersSlots = acceptedSpeakersSlots.add(1); 295 | speakersTalks[_talkId].status = ApplicationStatus.Accepted; 296 | 297 | emit TalkAccepted(_talkId); 298 | } 299 | 300 | function declineTalk(uint256 _talkId) 301 | external 302 | onlyOwner 303 | beforeEventStart 304 | { 305 | speakersTalks[_talkId].status = ApplicationStatus.Declined; 306 | address payable speakerAddress = speakersTalks[_talkId].speakerAddress; 307 | if (speakerAddress.isContract() == false) { 308 | address(speakerAddress).transfer(speakersTalks[_talkId].deposit); 309 | } 310 | 311 | emit TalkDeclined(_talkId); 312 | } 313 | 314 | function selfDeclineTalk(uint256 _talkId) 315 | external 316 | { 317 | require(block.timestamp >= talksApplicationEnd && block.timestamp < checkinStart, "only may self decline after onboarding end"); // solium-disable-line security/no-block-members, max-len 318 | 319 | address payable speakerAddress = speakersTalks[_talkId].speakerAddress; 320 | 321 | require(msg.sender == speakerAddress, "only speaker may make self decline"); 322 | require(speakersTalks[_talkId].status == ApplicationStatus.Applied, "//to do, think about self checkin or admin checkin"); 323 | 324 | speakersTalks[_talkId].status = ApplicationStatus.Declined; 325 | 326 | if (speakerAddress.isContract() == false) { 327 | address(speakerAddress).transfer(speakersTalks[_talkId].deposit); 328 | } 329 | 330 | emit TalkSelfDeclined(_talkId); 331 | } 332 | 333 | function checkinMember(uint256 _ticketId) 334 | external 335 | duringEvent 336 | { 337 | require(membersTickets[_ticketId].bidderAddress == msg.sender, "member should make self check in"); 338 | membersTickets[_ticketId].checkedIn = true; 339 | 340 | emit MemberCheckin(_ticketId); 341 | } 342 | 343 | function checkinSpeaker(uint256 _talkId) 344 | external 345 | onlyOwner 346 | duringEvent 347 | { 348 | require(speakersTalks[_talkId].checkedIn == false, "speaker shouldn't be checked in before"); 349 | require(speakersTalks[_talkId].status == ApplicationStatus.Accepted, "speaker should be accepted before"); 350 | 351 | uint256 bidId = totalSupply(); 352 | address speakerAddress = speakersTalks[_talkId].speakerAddress; 353 | super._mint(speakerAddress, bidId); 354 | ticketByAddressIndex[speakerAddress] = bidId; //? 355 | speakersTalks[_talkId].checkedIn = true; 356 | 357 | emit SpeakerCheckin(_talkId); 358 | } 359 | 360 | function distributeOverbids(uint256 _fromBid, uint256 _toBid) 361 | external 362 | onlyOwner 363 | afterDistributionStart 364 | { 365 | require(_fromBid <= _toBid, "bids window should be correct"); 366 | uint256 checkedInSpeakers = 0; 367 | for (uint256 y = 0; y < speakersTalks.length; y++){ 368 | if (speakersTalks[y].checkedIn) checkedInSpeakers++; 369 | } 370 | uint256 ticketsForMembersSupply = totalSupply().sub(checkedInSpeakers); 371 | require(_fromBid < ticketsForMembersSupply && _toBid < ticketsForMembersSupply, "bids window should be correct"); 372 | for (uint256 i = _fromBid; i <= _toBid; i++) { 373 | require(membersTickets[i].overbidReturned == false, "bid's overbid shouldn't be distrubuted yet"); 374 | address payable bidderAddress = membersTickets[i].bidderAddress; 375 | uint256 overbid = (membersTickets[i].value).sub(endPrice); 376 | if(bidderAddress.isContract() == false) { 377 | address(bidderAddress).transfer(overbid); 378 | } 379 | membersTickets[i].overbidReturned = true; 380 | amountReturnedBids++; 381 | } 382 | if (amountReturnedBids == ticketsForMembersSupply) { 383 | overbidsDistributed = true; 384 | } 385 | 386 | emit OverbidsDistributed(); 387 | } 388 | 389 | function distributeRewards() 390 | external 391 | onlyOwner 392 | afterDistributionStart 393 | { 394 | require(overbidsDistributed == true, "overbids should be disbributed before"); 395 | if (acceptedSpeakersSlots > 0) { 396 | uint256 checkedInSpeakers = 0; 397 | for (uint256 i = 0; i < speakersTalks.length; i++){ 398 | if (speakersTalks[i].checkedIn) checkedInSpeakers++; 399 | } 400 | uint256 valueForTicketsForReward = endPrice.mul(membersTickets.length); 401 | uint256 valueFromTicketsForSpeakers = valueForTicketsForReward.mul(getSpeakersShares()).div(100); 402 | 403 | uint256 valuePerSpeakerFromTickets = valueFromTicketsForSpeakers.div(checkedInSpeakers); 404 | for (uint256 y = 0; y < speakersTalks.length; y++) { 405 | address payable speakerAddress = speakersTalks[y].speakerAddress; 406 | if (speakersTalks[y].checkedIn == true && speakerAddress.isContract() == false) { 407 | speakerAddress.transfer(valuePerSpeakerFromTickets.add(speakersTalks[y].deposit)); 408 | } 409 | } 410 | } 411 | address(msg.sender).transfer(address(this).balance); 412 | 413 | emit RewardsDistributed(); 414 | } 415 | 416 | function setTalksGrid(string calldata _grid) 417 | external 418 | onlyOwner 419 | { 420 | talksGrid = _grid; 421 | 422 | emit UpdatedTalksGrid(_grid); 423 | } 424 | 425 | function setWorkshopsGrid(string calldata _grid) 426 | external 427 | onlyOwner 428 | { 429 | workshopsGrid = _grid; 430 | 431 | emit UpdatedWorkshopsGrid(_grid); 432 | } 433 | 434 | function getTalkById(uint256 _id) 435 | external 436 | view 437 | returns( 438 | string memory, 439 | string memory, 440 | string memory, 441 | uint256, 442 | uint256, 443 | address, 444 | uint256, 445 | bool, 446 | ApplicationStatus, 447 | string memory 448 | ) 449 | { 450 | require(_id < uint256(speakersTalks.length), "out of index of speakers"); 451 | Talk memory m = speakersTalks[_id]; 452 | return( 453 | m.speakerName, 454 | m.descSpeaker, 455 | m.deskTalk, 456 | m.duration, 457 | m.deposit, 458 | m.speakerAddress, 459 | m.appliedAt, 460 | m.checkedIn, 461 | m.status, 462 | m.proof 463 | ); 464 | } 465 | 466 | function getTicket(uint256 _id) 467 | external 468 | view 469 | returns( 470 | uint256, 471 | address, 472 | bool, 473 | bool 474 | ) 475 | { 476 | return( 477 | membersTickets[_id].value, 478 | membersTickets[_id].bidderAddress, 479 | membersTickets[_id].checkedIn, 480 | membersTickets[_id].overbidReturned 481 | ); 482 | } 483 | 484 | function getTicketIdByAddress(address _address) 485 | external 486 | view 487 | returns(uint256) 488 | { 489 | return ticketByAddressIndex[_address]; 490 | } 491 | 492 | function getAuctionStartBlock() 493 | external 494 | view 495 | returns(uint256) 496 | { 497 | return auctionStartBlock; 498 | } 499 | 500 | function getAuctionStartTime() 501 | external 502 | view 503 | returns(uint256) 504 | { 505 | return auctionStartTime; 506 | } 507 | 508 | function getAuctionEndTime() 509 | external 510 | view 511 | returns(uint256) 512 | { 513 | return auctionEnd; 514 | } 515 | 516 | function getEventStartTime() 517 | external 518 | view 519 | returns(uint256) 520 | { 521 | return checkinStart; 522 | } 523 | 524 | function getEventEndTime() 525 | external 526 | view 527 | returns(uint256) 528 | { 529 | return checkinEnd; 530 | } 531 | 532 | function getDistributionTime() 533 | external 534 | view 535 | returns(uint256) 536 | { 537 | return distributionStart; 538 | } 539 | 540 | function getCurrentPrice() 541 | public 542 | view 543 | returns(uint256) 544 | { 545 | uint256 blocksPassed = block.number - auctionStartBlock; 546 | uint256 currentDiscount = blocksPassed.mul(bidBlockDecrease); 547 | 548 | if (currentDiscount < (initialPrice - minimalPrice)) { 549 | return initialPrice.sub(currentDiscount); 550 | } else { 551 | return minimalPrice; 552 | } 553 | } 554 | 555 | function getEndPrice() 556 | external 557 | view 558 | returns(uint256) 559 | { 560 | return endPrice; 561 | } 562 | 563 | function getMinimalPrice() 564 | external 565 | view 566 | returns(uint256) 567 | { 568 | return minimalPrice; 569 | } 570 | 571 | function getMinimalSpeakerDeposit() 572 | external 573 | view 574 | returns(uint256) 575 | { 576 | return minimalSpeakerDeposit; 577 | } 578 | 579 | function getTotalTickets() 580 | external 581 | view 582 | returns(uint256) 583 | { 584 | return totalTickets; 585 | } 586 | 587 | function getTicketsAmount() 588 | external 589 | view 590 | returns(uint256) 591 | { 592 | return ticketsAmount; 593 | } 594 | 595 | function getSpeakersSlots() 596 | external 597 | view 598 | returns(uint256) 599 | { 600 | return speakersSlots; 601 | } 602 | 603 | function getAvailableSpeaksersSlots() 604 | external 605 | view 606 | returns(uint256) 607 | { 608 | return speakersSlots.sub(acceptedSpeakersSlots); 609 | } 610 | 611 | function getOrganizersShares() 612 | public 613 | view 614 | returns(uint256) 615 | { 616 | uint256 time = auctionEnd; 617 | if (ticketsAmount > 0 && block.timestamp < checkinStart) { // solium-disable-line security/no-block-members 618 | time = block.timestamp; // solium-disable-line security/no-block-members 619 | } 620 | uint256 mul = time.sub(auctionStartTime).mul(100).div(checkinStart.sub(auctionStartTime)); 621 | uint256 shares = SPEAKERS_START_SHARES.sub(SPEAKERS_END_SHARES).mul(mul).div(100); 622 | 623 | return SPEAKERS_END_SHARES.add(shares); 624 | } 625 | 626 | function getSpeakersShares() 627 | public 628 | view 629 | returns(uint256) 630 | { 631 | return uint256(100).sub(getOrganizersShares()); 632 | } 633 | 634 | function getTicketsFunds() 635 | external 636 | view 637 | returns(uint256) 638 | { 639 | return ticketsFunds; 640 | } 641 | 642 | function getPlace() 643 | external 644 | view 645 | returns(string memory) 646 | { 647 | return eventPlace; 648 | } 649 | 650 | function getDescription() 651 | external 652 | view 653 | returns(string memory) 654 | { 655 | return eventDescription; 656 | } 657 | 658 | function getTalksGrid() 659 | external 660 | view 661 | returns(string memory) 662 | { 663 | return talksGrid; 664 | } 665 | 666 | function getWorkshopsGrid() 667 | external 668 | view 669 | returns(string memory) 670 | { 671 | return workshopsGrid; 672 | } 673 | 674 | function getCommunityBuilderMessage(uint256 _messageID) 675 | external 676 | view 677 | returns( 678 | string memory, 679 | string memory, 680 | string memory, 681 | uint256 682 | ) 683 | { 684 | return( 685 | communityBuildersBoard[_messageID].message, 686 | communityBuildersBoard[_messageID].link1, 687 | communityBuildersBoard[_messageID].link2, 688 | communityBuildersBoard[_messageID].donation 689 | ); 690 | } 691 | 692 | function getCommunityBuildersBoardSize() 693 | external 694 | view 695 | returns(uint256) 696 | { 697 | return communityBuildersBoard.length; 698 | } 699 | 700 | function getAmountReturnedOverbids() 701 | external 702 | view 703 | returns(uint256) 704 | { 705 | return amountReturnedBids; 706 | } 707 | } --------------------------------------------------------------------------------