Lab 2
This commit is contained in:
424
exam/index.html
Normal file
424
exam/index.html
Normal file
@@ -0,0 +1,424 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
||||
>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.colors.min.css"
|
||||
>
|
||||
<style>
|
||||
[data-theme="dark"] {
|
||||
--pico-background-color: #282a36; /* Custom color */
|
||||
}
|
||||
|
||||
.app-header {
|
||||
border-bottom: 1px solid var(--pico-muted-border-color);
|
||||
box-shadow: 0 1px 0 var(--pico-muted-border-color);
|
||||
}
|
||||
|
||||
[data-theme="light"] .app-header {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .app-header {
|
||||
background-color: rgba(9, 21, 21, 0.6);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .sidebar {
|
||||
border-right: 3px solid rgba(9, 21, 21, 0.6);
|
||||
}
|
||||
|
||||
.app-header nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
.app-header hgroup {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.app-header #theme-icon {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.app-root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.app-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sidebar {
|
||||
width: 315px;
|
||||
flex-shrink: 0;
|
||||
padding: 1.5rem;
|
||||
border-right: 1px solid var(--pico-muted-border-color);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.sidebar nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-button {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem 0.75rem 0;
|
||||
border-bottom: 1px solid var(--pico-muted-border-color);
|
||||
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
[data-theme="dark"] hgroup>:not(:first-child):last-child {
|
||||
--pico-color: revert;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.main-content section.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.main-content section p.mt-3 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.main-content section .theory-question:first-of-type {
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
|
||||
.main-content section .theory-question {
|
||||
border-top: 1px solid var(--pico-muted-border-color);
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.main-content section .theory-question p.question {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.main-content section hgroup p {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
/* Mobile: hide sidebar, show desktop-only message */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
.main-content {
|
||||
display: none;
|
||||
}
|
||||
.mobile-message {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
.mobile-message {
|
||||
display: none;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
.mobile-message article {
|
||||
max-width: 28rem;
|
||||
}
|
||||
</style>
|
||||
<title>Lab 2</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-root">
|
||||
<header class="app-header">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<hgroup>
|
||||
<h2>Lab 2</h2>
|
||||
<p>Distributed Systems and Blockchain</p>
|
||||
</hgroup>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<details class="dropdown">
|
||||
<summary>
|
||||
Documentation
|
||||
</summary>
|
||||
<ul>
|
||||
<li><a href="https://api.next.code-camp.org/cdn/viem-docs.pdf" target="_blank">Viem</a></li>
|
||||
<li><a href="https://api.next.code-camp.org/cdn/solidity-docs.pdf" target="_blank">Solidity</a></li>
|
||||
<li><a href="https://api.next.code-camp.org/cdn/crowdfunding-contract.html" target="_blank">Contract example</a></li>
|
||||
<li><a href="https://api.next.code-camp.org/cdn/crowdfunding-tests.html" target="_blank">Tests example</a></li>
|
||||
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<span id="theme-icon" aria-hidden="true" onclick="cycleTheme()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 32 32" fill="currentColor" class="icon-theme-toggle "><clipPath id="theme-toggle-cutout"><path d="M0-11h25a1 1 0 0017 13v30H0Z"></path></clipPath><g clip-path="url(#theme-toggle-cutout)"><circle cx="16" cy="16" r="8.4"></circle><path d="M18.3 3.2c0 1.3-1 2.3-2.3 2.3s-2.3-1-2.3-2.3S14.7.9 16 .9s2.3 1 2.3 2.3zm-4.6 25.6c0-1.3 1-2.3 2.3-2.3s2.3 1 2.3 2.3-1 2.3-2.3 2.3-2.3-1-2.3-2.3zm15.1-10.5c-1.3 0-2.3-1-2.3-2.3s1-2.3 2.3-2.3 2.3 1 2.3 2.3-1 2.3-2.3 2.3zM3.2 13.7c1.3 0 2.3 1 2.3 2.3s-1 2.3-2.3 2.3S.9 17.3.9 16s1-2.3 2.3-2.3zm5.8-7C9 7.9 7.9 9 6.7 9S4.4 8 4.4 6.7s1-2.3 2.3-2.3S9 5.4 9 6.7zm16.3 21c-1.3 0-2.3-1-2.3-2.3s1-2.3 2.3-2.3 2.3 1 2.3 2.3-1 2.3-2.3 2.3zm2.4-21c0 1.3-1 2.3-2.3 2.3S23 7.9 23 6.7s1-2.3 2.3-2.3 2.4 1 2.4 2.3zM6.7 23C8 23 9 24 9 25.3s-1 2.3-2.3 2.3-2.3-1-2.3-2.3 1-2.3 2.3-2.3z"></path></g></svg>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="app-body">
|
||||
<aside class="sidebar">
|
||||
<nav>
|
||||
<a href="#theory" class="sidebar-button">Theory</a>
|
||||
<a href="#viem" class="sidebar-button">Coding - Viem</a>
|
||||
<a href="#hardhat" class="sidebar-button">Coding - Hardhat</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<main class="main-content container" id="main-content">
|
||||
<section id="theory">
|
||||
<hgroup>
|
||||
<h2>Theory</h1>
|
||||
<p>Answer the following questions to the best of your ability. Each question has exactly one correct answer. There are no negative marks for incorrect answers.</p>
|
||||
</hgroup>
|
||||
|
||||
|
||||
<div id="theory-questions"></div>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="viem" class="hidden">
|
||||
<h2>Coding - Viem: Contract Explorer</h1>
|
||||
|
||||
<p>In the repository you downloaded, there is a directory called <strong>/scripts</strong>. Inside, there is a file called <strong>contract-explorer.ts</strong>, which is used to analyze an ERC-20 contract (for fungible tokens) on the Next Testnet network, and to get the balance of a specific Ethereum address within that contract. Your job is to complete the script so that it prints the following information for the contract:</p>
|
||||
|
||||
<ul>
|
||||
<li>The name</li>
|
||||
<li>The symbol</li>
|
||||
<li>The decimals (for example, for PAXG it's 18 decimals, like Wei and Ether)</li>
|
||||
<li>The balance (in that contract), for a specific address</li>
|
||||
</ul>
|
||||
|
||||
<p class="mt-3">You can test the script by running the following command:</p>
|
||||
<pre><code>npx tsx scripts/contract-explorer.ts</code></pre>
|
||||
|
||||
<p class="mt-3">To understand what options are available for ERC-20 tokens, you should start by clicking the "Read contract" tab here: <a href="https://etherscan.code-camp.org/address/0x5FbDB2315678afecb367f032d93F642f64180aa3" target="_blank">view contract</a>.</p>
|
||||
|
||||
<p class="mt-3">For testing, you can use the following information:</p>
|
||||
<div class="overflow-auto">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="word-break: break-all;">Contract address</td>
|
||||
<td>0x5FbDB2315678afecb367f032d93F642f64180aa3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="word-break: break-all;">Account address</td>
|
||||
<td>0xa24d14702cef666131dd8EFC0670318CDCA4Baf3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p class="mt-3">Hint: you only need to use the readErc20Contract function, which is already provided for you in the starter template.</p>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="hardhat" class="hidden">
|
||||
<h2>Coding - Hardhat: Gradebook</h1>
|
||||
|
||||
<p>In the repository you downloaded, there is a directory called <strong>/contracts</strong> (for storing smart contracts), and a directory called <strong>/test</strong> (for test files). In the first directory, there is a file called <strong>Gradebook.sol</strong> (where you need to write a smart contract in Solidity); while in the second directory, there is a file called <strong>Gradebook.ts</strong> (where you need to write tests for the contract).</p>
|
||||
<p>The contract is meant to be used by a teacher as a public Gradebook, for storing points and calculating grades for students. It should support 3 functions:</p>
|
||||
|
||||
<ul>
|
||||
<li>addStudent(addr, name) - for adding a new student (with their Ethereum address and name).</li>
|
||||
<li>submitScore(addr, points) - for storing the number of points earned by the student (a number between 0 and 100). Points can only be set once for a student and can't be overridden. An event should be fired with the address and the number of points.</li>
|
||||
<li>getGrade(addr) - a view function that calculates a grade based on the points and returns a number between 5 and 10.</li>
|
||||
</ul>
|
||||
|
||||
<p class="mt-3">The contract should validate the inputs, and return errors if something is not correct. For calculating a grade based on the number of points, you can use the standard scoring system used at Brainster Next (91-100 is a 10, 81-90 is a 9, ..., and anything below 51 is a 5).</p>
|
||||
|
||||
<p class="mt-3">You can build the contracts by running the following command:</p>
|
||||
<pre><code>npx hardhat build</code></pre>
|
||||
|
||||
<p class="mt-3">After you code the contract, you can start adding tests in the test/Gradebook.ts file (it already has a list of tests you need to add). To run them, you can use the following command:</p>
|
||||
<pre><code>npx hardhat test</code></pre>
|
||||
|
||||
<p class="mt-3">Hint: you have an example contract and tests (see Documentation links on the top) to help you get started.</p>
|
||||
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="mobile-message">
|
||||
<article>
|
||||
<header>
|
||||
<strong>Desktop required</strong>
|
||||
</header>
|
||||
<p>This exam layout needs a larger screen. Please open it on a desktop computer.</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
function setupTheme() {
|
||||
var THEME_KEY = 'dsb-exam-theme';
|
||||
var html = document.documentElement;
|
||||
var icon = document.getElementById('theme-icon');
|
||||
|
||||
function applyTheme(theme) {
|
||||
html.setAttribute('data-theme', theme);
|
||||
try { localStorage.setItem(THEME_KEY, theme); } catch (e) {}
|
||||
}
|
||||
|
||||
function cycleTheme(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
||||
applyTheme(next);
|
||||
}
|
||||
|
||||
try {
|
||||
var saved = localStorage.getItem(THEME_KEY);
|
||||
if (saved === 'light' || saved === 'dark') applyTheme(saved);
|
||||
} catch (e) {}
|
||||
|
||||
if (icon) icon.addEventListener('click', cycleTheme);
|
||||
}
|
||||
|
||||
function setupSidebar() {
|
||||
var sidebar = document.querySelector('.sidebar');
|
||||
var sidebarButtons = sidebar.querySelectorAll('.sidebar-button');
|
||||
sidebarButtons.forEach(function(button) {
|
||||
button.addEventListener('click', function() {
|
||||
var target = button.getAttribute('href');
|
||||
var targetSection = document.querySelector(target);
|
||||
|
||||
var sections = document.querySelectorAll('.main-content section');
|
||||
|
||||
if (targetSection) {
|
||||
document.body.style.opacity = 0;
|
||||
sections.forEach(function(section) {
|
||||
section.classList.add('hidden');
|
||||
});
|
||||
|
||||
targetSection.classList.remove('hidden');
|
||||
setTimeout(function() {
|
||||
window.scrollTo(0, 0);
|
||||
document.body.style.opacity = 1;
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupTheory() {
|
||||
|
||||
function setupTheoryCheckboxes() {
|
||||
var theoryCheckboxes = document.querySelectorAll('.theory-question input[type="checkbox"]');
|
||||
theoryCheckboxes.forEach(function(checkbox) {
|
||||
checkbox.addEventListener('change', function() {
|
||||
var question = checkbox.closest('.theory-question');
|
||||
var questionId = question.getAttribute('id').split('-')[1];
|
||||
var answer = checkbox.getAttribute('name').split('-')[2];
|
||||
var otherCheckboxes = question.querySelectorAll('input[type="checkbox"]');
|
||||
otherCheckboxes.forEach(function(otherCheckbox) {
|
||||
const otherAnswer = otherCheckbox.getAttribute('name').split('-')[2];
|
||||
if (otherAnswer !== answer) {
|
||||
otherCheckbox.checked = false;
|
||||
}
|
||||
});
|
||||
|
||||
checkbox.checked = true;
|
||||
fetch('/theory-answer', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
questionId: Number(questionId),
|
||||
answer: answer
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(function(response) {
|
||||
if (!response.ok) {
|
||||
alert('Failed to submit answer.');
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
alert('Failed to submit answer.');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
fetch('/theory')
|
||||
.then(response => response.json())
|
||||
.then(function(data) {
|
||||
var theory = document.querySelector('#theory');
|
||||
|
||||
var html = '';
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var questionId = data[i].id;
|
||||
var question = data[i].question;
|
||||
var options = data[i].options;
|
||||
var answer = data[i].answer;
|
||||
|
||||
html += '<div class="theory-question" id="question-' + questionId + '">';
|
||||
html += '<p class="question">' + (i + 1) + '. ' + question + "</p>";
|
||||
const sortedOptionKeys = Object.keys(options).sort();
|
||||
|
||||
html += '<fieldset>';
|
||||
sortedOptionKeys.forEach(function(optionKey) {
|
||||
html += '<label>';
|
||||
html += '<input type="checkbox" name="question-' + questionId + '-' + optionKey + '"' + (answer === optionKey ? ' checked' : '') +'>';
|
||||
html += ' ' + options[optionKey];
|
||||
html += '</label>';
|
||||
});
|
||||
html += '</fieldset>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
document.querySelector('#theory-questions').innerHTML = html;
|
||||
setupTheoryCheckboxes();
|
||||
});
|
||||
}
|
||||
|
||||
setupTheme();
|
||||
setupSidebar();
|
||||
setupTheory();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user