├── .gitignore ├── static ├── btc.gif ├── lib │ ├── app.js │ └── contract-abi.json ├── css │ └── app.css └── index.html ├── index.js ├── bower.json ├── package.json ├── contracts └── training.sol └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /bower_components -------------------------------------------------------------------------------- /static/btc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msusur/solidity-training-dapp/HEAD/static/btc.gif -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var express = require("express"), 2 | app = new express(), 3 | minify = require('express-minify'); 4 | 5 | app.set('port', (process.env.PORT || 5000)); 6 | 7 | if (!process.env.DEBUG) { 8 | app.use(minify({ 9 | cache: true 10 | })); 11 | } 12 | app.use("/", express.static("static")); 13 | app.use("/vendor", express.static("bower_components")); 14 | 15 | app.listen(app.get('port'), function() { 16 | console.log('Node app is running on port', app.get('port')); 17 | }); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-training-contract", 3 | "description": "", 4 | "main": "index.js", 5 | "authors": [ 6 | "msusur " 7 | ], 8 | "license": "ISC", 9 | "homepage": "https://github.com/msusur/solidity-training-contract", 10 | "private": true, 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "web3": "^0.19.0", 20 | "bootstrap": "^3.3.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-training-contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "prestart": "bower install", 8 | "start": "node index.js", 9 | "start:debug": "DEBUG=1 node index.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/msusur/solidity-training-contract.git" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/msusur/solidity-training-contract/issues" 20 | }, 21 | "homepage": "https://github.com/msusur/solidity-training-contract#readme", 22 | "dependencies": { 23 | "bower": "^1.8.2", 24 | "express": "^4.16.2", 25 | "express-minify": "^1.0.0", 26 | "grunt": "^1.0.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/training.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Training { 4 | 5 | address owner; 6 | string participationUrl; 7 | uint studentLimit; 8 | uint registeredUserCount; 9 | bool isActive; 10 | mapping(address => Student) students; 11 | mapping(uint => address) studentsIndex; 12 | 13 | struct Student { 14 | string email; 15 | bool validated; 16 | address addr; 17 | bool deleted; 18 | } 19 | 20 | event StudentRegistered(address addr, string email); 21 | 22 | modifier onlyOwner() { 23 | require(owner == msg.sender); 24 | _; 25 | } 26 | 27 | modifier onlyActive() { 28 | require(isActive); 29 | _; 30 | } 31 | 32 | function Training(uint _studentLimit) public { 33 | owner = msg.sender; 34 | isActive = true; 35 | 36 | if (_studentLimit != 0) { 37 | studentLimit = _studentLimit; 38 | } else { 39 | studentLimit = 30; 40 | } 41 | } 42 | 43 | /* Student Functions */ 44 | 45 | function registerToEvent(string email) onlyActive public { 46 | require(!isRegistered(msg.sender)); 47 | require(registeredUserCount < studentLimit); 48 | 49 | registeredUserCount++; 50 | students[msg.sender] = Student(email, false, msg.sender, false); 51 | studentsIndex[registeredUserCount] = msg.sender; 52 | 53 | StudentRegistered(msg.sender, email); 54 | } 55 | 56 | function isRegistered(address _addr) public constant returns (bool) { 57 | return students[_addr].addr != address(0) && !students[_addr].deleted; 58 | } 59 | 60 | function getMeParticipationUrl() public constant returns (string) { 61 | require(isRegistered(msg.sender)); 62 | Student storage student = students[msg.sender]; 63 | require(student.validated); 64 | return participationUrl; 65 | } 66 | 67 | function getTotalCount() public constant returns (uint) { 68 | return registeredUserCount; 69 | } 70 | 71 | function getStudentLimit() public constant returns (uint) { 72 | return studentLimit; 73 | } 74 | 75 | /* Admin Functions */ 76 | 77 | function getStudent(uint idx) public constant onlyOwner returns (uint index, string email, address addr) { 78 | address studentAddr = studentsIndex[idx]; 79 | Student storage student = students[studentAddr]; 80 | return (idx, student.email, student.addr); 81 | } 82 | 83 | function deleteStudent(uint idx) public onlyOwner { 84 | address studentAddr = studentsIndex[idx]; 85 | Student storage student = students[studentAddr]; 86 | student.deleted = true; 87 | registeredUserCount--; 88 | } 89 | 90 | function validateStudentStatus(uint idx, bool isValidated) public onlyOwner { 91 | address studentAddr = studentsIndex[idx]; 92 | Student storage student = students[studentAddr]; 93 | student.validated = isValidated; 94 | } 95 | 96 | function setParticipationLink(string url) public onlyOwner { 97 | participationUrl = url; 98 | } 99 | 100 | function closeParticipation() public onlyOwner { 101 | isActive = false; 102 | } 103 | 104 | function openParticipation() public onlyOwner { 105 | isActive = true; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /static/lib/app.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | var activeDude, 3 | isEthActive = typeof web3 !== 'undefined', 4 | studentCount = 0, 5 | contractAddress = '0xC4C0e5Ea64Bd2172ACD901500aecA7B2DB0F6f85'; 6 | 7 | // Set contract address on FAQ 8 | $('#smartContract').attr('href', 'https://rinkeby.etherscan.io/address/' + contractAddress).text(contractAddress); 9 | 10 | // FAQ items open/close 11 | $('.faq-item').click(function() { 12 | if (activeDude) { 13 | activeDude.hide(); 14 | } 15 | var key = $(this).attr('for'); 16 | activeDude = $('#' + key).toggle(); 17 | }); 18 | 19 | var loadEthInformation = function(w3, address) { 20 | // Check the network 21 | w3.version.getNetwork(function(err, netId) { 22 | // Get network id 23 | if (netId !== '4') { 24 | $('#metamask-alert').text('Sadece Rinkeby ile bu siteyi kullanabilirsiniz!').show(); 25 | throw new Error('Not Rinkeby.'); 26 | } 27 | 28 | // Get contract ABI. 29 | $.get('/lib/contract-abi.json').then(function(abi) { 30 | 31 | // Registration button 32 | $('#btn').click(function() { 33 | var email = $('#email').val(); 34 | var re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; 35 | if (!re.test(email)) { 36 | return $('#metamask-alert').text('Email adresi gecersiz.').show(); 37 | } 38 | contract.StudentRegistered(function(error, result) { 39 | if (error) { 40 | return $('#metamask-alert').text('Bir hata olustu: ' + error).show(); 41 | } 42 | $('#information-alert') 43 | .html('Kayıt işleminiz tamamlandı. Bizden haber bekleyin!') 44 | .show(); 45 | }); 46 | 47 | contract.registerToEvent.sendTransaction(email, { gas: 4700000, gasPrice: 80000000000 }, 48 | function(error, result) { 49 | $('#information-alert') 50 | .html('İşleminiz alındı. Detaylar için buraya tıklayın ' + 51 | '' + result + '.
Eğer transaction başarılı görünüyorsa kaydınız alınmıştır. Lütfen mail beklemeyin.') 52 | .show(); 53 | }); 54 | }); 55 | 56 | $('#adminButton').click(function() { 57 | var queue = w3.createBatch(); 58 | // admin button to list the students. 59 | for (var idx = 0; idx < studentCount; idx += 1) { 60 | queue.add(function(id) { 61 | return contract.getStudent.call(id, function(error, result) { 62 | if (error) { 63 | return console.log('Yok canim daha neler...'); 64 | } 65 | if (result[1]) { 66 | $('.super-secret-admin-stuff').show().append('

' + id + '.' + result[1] + '

'); 67 | } 68 | }); 69 | }(idx)); 70 | } 71 | 72 | queue.execute(); 73 | 74 | }); 75 | var contract = w3.eth.contract(abi).at(address); 76 | 77 | var batch = w3.createBatch(); 78 | batch.add(contract.getTotalCount.call(function(error, result) { 79 | $('#studentCount').text(result); 80 | })); 81 | batch.add(contract.getStudentLimit.call(function(error, result) { 82 | studentCount = result.c; 83 | $('#studentLimit').text(studentCount); 84 | })); 85 | batch.execute(); 86 | }); 87 | }); 88 | }; 89 | 90 | 91 | if (isEthActive) { 92 | $('#metamask-alert').hide(); 93 | loadEthInformation(new Web3(web3.currentProvider), contractAddress); 94 | } else { 95 | $('.eth-info').remove(); 96 | } 97 | }); -------------------------------------------------------------------------------- /static/css/app.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | 5 | .faq-item { 6 | cursor: pointer; 7 | } 8 | 9 | .faq-detail p { 10 | text-align: left; 11 | padding-top: 10px; 12 | padding-left: 10px; 13 | } 14 | 15 | .faq-detail { 16 | display: none; 17 | } 18 | 19 | .super-secret-admin-stuff { 20 | display: none; 21 | } 22 | 23 | .form { 24 | width: 320px; 25 | height: auto; 26 | top: calc(50vh - 232px); 27 | text-align: center; 28 | padding: 0; 29 | font-family: 'Open Sans', sans-serif; 30 | letter-spacing: 2px; 31 | margin: auto; 32 | } 33 | 34 | .form .top { 35 | text-align: center; 36 | background-color: rgb(45, 48, 230); 37 | text-transform: uppercase; 38 | color: #fff; 39 | margin: 0; 40 | padding: 15px 20px; 41 | border-top-right-radius: 5px; 42 | border-top-left-radius: 5px; 43 | } 44 | 45 | .form form { 46 | margin: 0; 47 | position: relative; 48 | bottom: 10px; 49 | padding: 15px 20px; 50 | background-color: #fff; 51 | border-bottom-right-radius: 5px; 52 | border-bottom-left-radius: 5px; 53 | } 54 | 55 | .form form input, 56 | .form form textarea { 57 | display: block; 58 | padding: 5px; 59 | border: 1px solid #DDDBDB; 60 | margin-bottom: 20px; 61 | width: 100%; 62 | max-width: 280px; 63 | border-radius: 3px; 64 | letter-spacing: 1px 65 | } 66 | 67 | .form form textarea { 68 | height: 110px; 69 | resize: none; 70 | } 71 | 72 | .form form input[type="button"] { 73 | width: 100px; 74 | background-color: rgb(45, 48, 230); 75 | margin: auto; 76 | margin-bottom: 0; 77 | color: #fff; 78 | text-transform: uppercase; 79 | border: none; 80 | padding: 10px; 81 | cursor: pointer 82 | } 83 | 84 | .input:focus, 85 | .input:hover { 86 | -webkit-box-shadow: 0 0 8px rgb(3, 41, 63); 87 | -moz-box-shadow: 0 0 8px rgb(3, 41, 63); 88 | -ms-box-shadow: 0 0 8px rgb(3, 41, 63); 89 | -o-box-shadow: 0 0 8px rgb(3, 41, 63); 90 | box-shadow: 0 0 8px rgb(3, 41, 63); 91 | border: none; 92 | outline: none; 93 | } 94 | 95 | 96 | /* Space out content a bit */ 97 | 98 | body { 99 | padding-top: 1.5rem; 100 | padding-bottom: 1.5rem; 101 | } 102 | 103 | 104 | /* Everything but the jumbotron gets side spacing for mobile first views */ 105 | 106 | .header, 107 | .marketing, 108 | .footer { 109 | padding-right: 1rem; 110 | padding-left: 1rem; 111 | } 112 | 113 | 114 | /* Custom page header */ 115 | 116 | .header { 117 | padding-bottom: 1rem; 118 | border-bottom: .05rem solid #e5e5e5; 119 | } 120 | 121 | 122 | /* Make the masthead heading the same height as the navigation */ 123 | 124 | .header h3 { 125 | margin-top: 0; 126 | margin-bottom: 0; 127 | line-height: 3rem; 128 | } 129 | 130 | 131 | /* Custom page footer */ 132 | 133 | .footer { 134 | padding-top: 1.5rem; 135 | color: #777; 136 | border-top: .05rem solid #e5e5e5; 137 | } 138 | 139 | 140 | /* Customize container */ 141 | 142 | @media (min-width: 48em) { 143 | .container { 144 | max-width: 46rem; 145 | } 146 | } 147 | 148 | .container-narrow>hr { 149 | margin: 2rem 0; 150 | } 151 | 152 | 153 | /* Main marketing message and sign up button */ 154 | 155 | .jumbotron { 156 | text-align: center; 157 | border-bottom: .05rem solid #e5e5e5; 158 | } 159 | 160 | .jumbotron .btn { 161 | padding: .75rem 1.5rem; 162 | font-size: 1.5rem; 163 | } 164 | 165 | 166 | /* Supporting marketing content */ 167 | 168 | .marketing { 169 | margin: 3rem 0; 170 | } 171 | 172 | .marketing p+h4 { 173 | margin-top: 1.5rem; 174 | } 175 | 176 | 177 | /* Responsive: Portrait tablets and up */ 178 | 179 | @media screen and (min-width: 48em) { 180 | /* Remove the padding we set earlier */ 181 | .header, 182 | .marketing, 183 | .footer { 184 | padding-right: 0; 185 | padding-left: 0; 186 | } 187 | /* Space out the masthead */ 188 | .header { 189 | margin-bottom: 2rem; 190 | } 191 | /* Remove the bottom border on the jumbotron for visual effect */ 192 | .jumbotron { 193 | border-bottom: 0; 194 | } 195 | } -------------------------------------------------------------------------------- /static/lib/contract-abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "getStudentLimit", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "uint256" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": true, 18 | "inputs": [], 19 | "name": "getMeParticipationUrl", 20 | "outputs": [ 21 | { 22 | "name": "", 23 | "type": "string" 24 | } 25 | ], 26 | "payable": false, 27 | "stateMutability": "view", 28 | "type": "function" 29 | }, 30 | { 31 | "constant": true, 32 | "inputs": [ 33 | { 34 | "name": "idx", 35 | "type": "uint256" 36 | } 37 | ], 38 | "name": "getStudent", 39 | "outputs": [ 40 | { 41 | "name": "index", 42 | "type": "uint256" 43 | }, 44 | { 45 | "name": "email", 46 | "type": "string" 47 | }, 48 | { 49 | "name": "addr", 50 | "type": "address" 51 | } 52 | ], 53 | "payable": false, 54 | "stateMutability": "view", 55 | "type": "function" 56 | }, 57 | { 58 | "constant": true, 59 | "inputs": [ 60 | { 61 | "name": "_addr", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "isRegistered", 66 | "outputs": [ 67 | { 68 | "name": "", 69 | "type": "bool" 70 | } 71 | ], 72 | "payable": false, 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "constant": true, 78 | "inputs": [], 79 | "name": "getTotalCount", 80 | "outputs": [ 81 | { 82 | "name": "", 83 | "type": "uint256" 84 | } 85 | ], 86 | "payable": false, 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [ 92 | { 93 | "name": "_studentLimit", 94 | "type": "uint256" 95 | } 96 | ], 97 | "payable": false, 98 | "stateMutability": "nonpayable", 99 | "type": "constructor" 100 | }, 101 | { 102 | "constant": false, 103 | "inputs": [ 104 | { 105 | "name": "idx", 106 | "type": "uint256" 107 | }, 108 | { 109 | "name": "isValidated", 110 | "type": "bool" 111 | } 112 | ], 113 | "name": "validateStudentStatus", 114 | "outputs": [], 115 | "payable": false, 116 | "stateMutability": "nonpayable", 117 | "type": "function" 118 | }, 119 | { 120 | "anonymous": false, 121 | "inputs": [ 122 | { 123 | "indexed": false, 124 | "name": "addr", 125 | "type": "address" 126 | }, 127 | { 128 | "indexed": false, 129 | "name": "email", 130 | "type": "string" 131 | } 132 | ], 133 | "name": "StudentRegistered", 134 | "type": "event" 135 | }, 136 | { 137 | "constant": false, 138 | "inputs": [], 139 | "name": "closeParticipation", 140 | "outputs": [], 141 | "payable": false, 142 | "stateMutability": "nonpayable", 143 | "type": "function" 144 | }, 145 | { 146 | "constant": false, 147 | "inputs": [ 148 | { 149 | "name": "idx", 150 | "type": "uint256" 151 | } 152 | ], 153 | "name": "deleteStudent", 154 | "outputs": [], 155 | "payable": false, 156 | "stateMutability": "nonpayable", 157 | "type": "function" 158 | }, 159 | { 160 | "constant": false, 161 | "inputs": [ 162 | { 163 | "name": "email", 164 | "type": "string" 165 | } 166 | ], 167 | "name": "registerToEvent", 168 | "outputs": [], 169 | "payable": false, 170 | "stateMutability": "nonpayable", 171 | "type": "function" 172 | }, 173 | { 174 | "constant": false, 175 | "inputs": [], 176 | "name": "openParticipation", 177 | "outputs": [], 178 | "payable": false, 179 | "stateMutability": "nonpayable", 180 | "type": "function" 181 | }, 182 | { 183 | "constant": false, 184 | "inputs": [ 185 | { 186 | "name": "url", 187 | "type": "string" 188 | } 189 | ], 190 | "name": "setParticipationLink", 191 | "outputs": [], 192 | "payable": false, 193 | "stateMutability": "nonpayable", 194 | "type": "function" 195 | } 196 | ] 197 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 25 |

Ethereum'a Giriş Eğitimi

26 |
27 |
28 | Dikkat! Formu çalıştırmak için Metamask kurulu olmalı! Ancak kurulu gibi görünmüyor. 29 |
30 | 33 |
34 |
35 | 36 |

Kayıt Formu

37 |
38 |
39 |

Toplam -1/-1 öğrenci kayitli.

40 | 41 | 42 |
43 |
44 | 45 |
46 |

Soruların mı var? Şuraya bir göz at istersen.

47 |

...ya da kestirmeden şuraya giderek adımları takip edebilirsin.

48 |
    49 |
  • Sadece akıllı kontrat ile mi kayıt alıyorsunuz?
  • 50 |
    51 |

    Evet, hem akıllı kontratların uygulama alanlarını herkese göstermek hem de işimizi kolaylaştırmak için bir akıllı kontrat yazdık. Detaylarına 52 | adresindeki kontrat üzerinden ulaşabilirsiniz. 53 |

    54 |
    55 |
  • Her kayıt olan eğitime katılabilecek mi?
  • 56 |
    57 |

    Evet eğitime her kayıt olan katılabilecek. Ancak eğitim tamamen başlangıç seviyesinde olacağı için daha önceden bu konuyu bilenlerin katılmamasını rica ederim.

    58 |
    59 |
  • Kayıt formu çalışmıyor ne yapmalıyım?
  • 60 |
    61 |

    Eğitime katılım sırasında bir ön eleme yöntemi belirlemek istedik, böylece sadece 'gerçekten' hevesli ve ilgili kişileri eğitime dahil edebileceğiz. Ancak üzülmeyin, formu çalıştırabilmeniz için gerekli adımları 62 | github repository'mizde anlattık. Dolayısıyla ne yapacağınızı oradan okuyabilirsiniz. 63 |

    64 |
    65 |
  • Neden böyle garip işlerle uğraştırıyorsunuz bizi?
  • 66 |
    67 |

    Çünkü her gün bize 'paramı ethereum'a mı yatırayım yoksa bitcoin'e mi yatırayım?' diye eposta gönderen kişilerden bıktık. Bu yüzden zamanımızı gerçekten hevesli kişilere ayırıp onlara en fazla faydayı sağlamayı istiyoruz. 68 |

    69 |
    70 |
  • Neler öğreneceğim?
  • 71 |
    72 |

    Ethereum'un nasıl çalıştığını, akıllı kontrat deploy etmek için gerekenleri ve akıllı kontrat tasarımını öğreneceksiniz.

    73 |
    74 |
  • Paramı Ethereum'a mı yoksa Bitcoin'e mi yatırayım?
  • 75 |
    76 |

    77 | 78 |

    79 |
    80 |
81 |
82 | 83 | 84 |
85 |

86 | Admin 87 |

88 |
    89 | 90 |
91 |
92 | 93 | 96 | 97 |
98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ethereum Eğitim Kayıt Uygulaması 2 | 3 | Bu uygulama ile, Ethereum eğitimimize katılacak olan lise ve üniversite öğrencilerini kayıt altına almayı ve biraz da sadece gerçekten Blockchain ve Ethereum'a ilgisi olanları ayırt etmeyi hedefliyoruz. 4 | 5 | `Ethereum Eğitim Kayıt Uygulaması` ile, bir Dapp uygulamasının nasıl kullanıldığını deneyimlemiş ve öğrenmiş olacaksınız. Bu sebeple kayıt işlemine geçmeden önce bu dökümanı okumanız gerekmektedir. 6 | 7 | Yapmanız gerekenler gerçekten çok basit. Hiç merak etmeyin, bir problemle karşılaşırsanız [Mert Susur](https://www.twitter.com/mertsusur)'a ulaşıp destek isteyebilirsiniz. 8 | 9 | Kolları sıvadıysak başlayabiliriz. 10 | 11 | ## Nasıl kayıt olurum? 12 | 13 | Yapılması gereken ilk şey [Metamask](https://metamask.io/)'ı Chrome'unuza kurmak. Metamask basitçe, Dapp ile Ethereum cüzdanınız arasında köprü görevi gören bir Chrome Extension'udur. Bizim örneğimizde Dapp, bu repository'de gördüğünüz ve sizin için hazırladığımız kayıt formu uygulamasıdır. 14 | 15 | Extension'u indirdikten sonra bir şifre belirleyip Metamask'ı kullanmaya başlayabilirsiniz. Kayıt uygulamamız Rinkeby ağında çalıştığı için extension'un sol üstünden Rinkeby ağına geçmeniz gerekmektedir. Geçiş yaptığınızda `Àccount 1` adında bir hesabın yaratılmış olduğunu göreceksiniz. 16 | 17 | Hemen bir parantez açarak uygulamanın nasıl çalıştığına değinelim. Uygulamamızda girilen e-mail adresleri bir database'de tutulmamaktadır. Tüm veriler Ethereum network'ü üzerinde tutulmaktadır. Tabii yukarda da bahsettiğimiz gibi gerçek ağda değil, Rinkeby ağında yani bir test ortamında tutulmaktadır. 18 | 19 | Bu noktada e-mail adresiniz ile Ethereum adresiniz eşleştirilerek tutulmaktadır. Bu da, Rinkeby ağında bir adrese ihtiyacınız olduğu anlamına geliyor ki yukarda Metamask'ı kurup Rinkeby'e geçtiğinizde bu adrese sahip olacağınızı belirtmiştik. Tabii her şey bununla bitmiyor. 20 | 21 | Gerçek Ethereum ağına bir veri yazmak için belirli bir miktar ücret ödemeniz gerekmektedir. Buna `Transaction fee` adı verilmekte. Gerçek Ethereum ağında olduğu gibi Rinkeby ağında da bu ücretin ödenmesi gerekmektedir. Fakat bu, gerçek ağdaki gibi USD, Euro, TL gibi gerçek para karşılığı olan bir şey değildir. 22 | 23 | Rinkeby, Robsten gibi ortamlar tamamen test ortamları oldukları için ve aslen geliştirme zamanında kullanıldıkları için gerçek para ile yapılmaz buradaki işler. Bunun yerine bazı servisler kullanarak hesabınızda para yaratırsınız. Rinkeby ağı için bu servis şudur: https://faucet.rinkeby.io/ 24 | 25 | Şimdi hesabımıza nasıl test ether'i aktaracağımıza değinelim. Öncelikle Metamask'ı açıp `Account 1`'in sağındaki üç noktadan Copy Address to clipboard diyoruz. Bu adresimizi, Facebook, Twitter ya da hiçbir arkadaşımızın ekli olmadığı Google+'a post olarak giriyoruz. Fakat önemli olan, postta sadece bu adresimiz oluyor. Başka bir yazı yazmıyoruz. Şurada bir örneği mevcut: https://plus.google.com/109087235038332478392/posts/EcfdTxCmrw8?hl=tr 26 | 27 | Sonrasında bu postumuzun linkini alıp faucet'e yapıştırıyoruz ve sağ taraftaki `Give me Ether`'den istediğimizi seçiyoruz. Birkaç saniye sonrasında Metamask'tan aldığımız adresimizi https://rinkeby.etherscan.io adresinde aratıp, bakiyemizi görebiliriz. Şimdi Rinkeby ağına e-mail adresimizi yazdırmak için gerekli olan `Transaction fee`'yi karşılayabiliriz. 28 | 29 | Her şey hazır görünüyor. Şimdi [kayıt uygulamamıza](https://solidity-egitimi.herokuapp.com/) girebiliriz. Hala kontenjan varsa e-mail adresinizi doğru bir şekilde yazalım ve `KAYIT OL` butonuna tıklayalım. (Kayıt sistemimizin, Metamask'ta Rinkeby ağı seçili durumdayken çalıştığını tekrar hatırlatalım.) Sonrasında, karşınıza şu şekilde bir ekran gelecek: 30 | 31 | ![Onay ekranı](http://oi66.tinypic.com/8y7wp4.jpg) 32 | 33 | Şimdi `Submit` butonuna tıklayıp Rinkeby network'ünde yeni bir transaction yaratılmasını sağlayabilirsiniz. 34 | 35 | Bundan sonrasında ise, kontenjan doldugunda sizlere bir mail gonderecegiz ve bu mail'de nasil katilacaginizin detaylarini bulabileceksiniz. 36 | 37 | > Onay mail'inin gelmesi biraz vakit alabilir. Şu an biz de kestiremiyoruz ama baktınız beş günü-on günü geçiyor onay mail'inin gelmesi, yine bizimle iletişime geçin lütfen. 38 | 39 | > Bir adres ile sadece bir kayıt işlemi yapabilirsiniz. 40 | 41 | ## Ne yaptım ben şimdi? 42 | 43 | Kayıt oldunuz! Ve daha da önemlisi bir Dapp uygulaması kullanırken, mevcutta genel kabul görmüş olan yöntemi (Metamask) uyguladınız. Bu sayede Ethereum private key'inizi veya şifrenizi paylaşmadan transaction gerçekleştirdiniz! 44 | 45 | ## Daha fazla istiyorum, daha fazla! 46 | 47 | Öncelikle `contracts` klasörü altında, kayıt ol işleminde çalışan `Smart Contract`'ı görebilirsiniz. İsmi `training.sol`. Kayıt olduğunuzda `contract` üzerinde tutulan bilgileriniz sadece bizim tarafımızdan (owner) görüntülenebilir. 48 | 49 | Ama kafa karışıklığına neden olmamak adına daha fazla detay vermiyoruz. Eğitimde bu konuların üzerinden zaten geçeceğiz. 50 | 51 | ## Eğitimdeki konu başlıkları 52 | 53 | Yazilimin temelleri 54 | Blockchain Temelleri 55 | Smart contract temelleri (Remix nasıl kullanılır) 56 | Metamask ve Rinkeby 57 | Smart contract deployment 58 | 59 | ## Ben de kullanabilir miyim bu uygulamayı? 60 | 61 | Tabi ki! Basic Smart Contract ve Ethereum bilginiz varsa çok kolay bir şekilde uygulamayı kendiniz için çalıştırabilirsiniz. Öncelikle contract'ı kendi cüzdan adresiniz ile deploy edin ve owner olun, sonrasında uygulamayı indirip `./static/lib/app.js` dosyasındaki contractAddress alanını, kendi yarattığınız contract'ın adresi ile değiştirin ve uygulamayı deploy edin. İşte bu kadar. 62 | 63 | Tabi ki şunları çalıştırmayı unutmayın: 64 | ``` 65 | npm install 66 | npm prestart 67 | ``` 68 | 69 | ## Son 70 | 71 | Herhangi bir sorunuz olursa bize ulaşmak konusunda çekinmeyin lütfen. Görüşmek üzere! 72 | 73 | * [Mert Susur](https://www.twitter.com/mertsusur) 74 | 75 | ## Destek olanlar 76 | * [Mert Susur](https://www.twitter.com/mertsusur) 77 | * [Onur Aykaç](https://github.com/onuar) 78 | * [Tarık Yurtlu](https://github.com/tarikyurtlu) 79 | * [Emir Ercan Ayar](https://github.com/eercanayar) 80 | 81 | --------------------------------------------------------------------------------