Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ async function runTutorialCheckFive(props: CheckProps) {
if (checkMinting) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resMinting);
props.tutorialContractStepFailed("Failed to mint the NFT!");
props.tutorialContractStepFailed(
`
Calling NFT.mintNFT() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${mintRequest}.
`,
);
return false;
}

Expand All @@ -94,7 +99,12 @@ async function runTutorialCheckFive(props: CheckProps) {
if (!checkSecondMinting) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resSecondMinting);
props.tutorialContractStepFailed("NFT has been minted twice!");
props.tutorialContractStepFailed(
`
Calling NFT.mintNFT() the second time did not produce any failed receipts!
The NFT has been minted twice.
`,
);
return false;
}

Expand All @@ -114,7 +124,12 @@ async function runTutorialCheckFive(props: CheckProps) {
if (checkSending) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resSending);
props.tutorialContractStepFailed("Failed to send the NFT!");
props.tutorialContractStepFailed(
`
Calling NFT.sendNFT() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${sendRequest}.
`,
);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ async function runTutorialCheckFour(props: CheckProps) {
if (checkDeploy) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resDeploy);
props.tutorialContractStepFailed("Failed to call Deployer.deploy()!");
props.tutorialContractStepFailed(
`
Calling Deployer.deploy() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${hashDeploy}.
`,
);
return false;
}

Expand All @@ -91,7 +96,12 @@ async function runTutorialCheckFour(props: CheckProps) {
if (checkIncrement) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resIncrement);
props.tutorialContractStepFailed("Failed to call Counter.increment()!");
props.tutorialContractStepFailed(
`
Calling Counter.increment() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${hashDeploy}.
`,
);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,21 @@ async function runTutorialCheckOne(props: CheckProps) {
if (checkCaller) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resCaller);
props.tutorialContractStepFailed("Failed to call Caller.sendValue()!");
props.tutorialContractStepFailed(
`
Calling Caller.sendValue() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${hashCaller}.
`,
);
return false;
}
props.tutorialContractStepPassed("Caller sendValue has been called successfully!");
props.tutorialContractStepPassed("Caller.sendValue() has been called successfully!");

const receiverBalance = await client.getBalance(resultReceiver.address);

if (receiverBalance !== 300_000n) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
props.tutorialContractStepFailed("Receiver failed to receive tokens!");
props.tutorialContractStepFailed("Receiver did not receive 300_000 tokens!");
return false;
}
props.tutorialContractStepPassed("Receiver got 300_000 tokens!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ async function runTutorialCheckThree(props: CheckProps) {
if (checkRequest) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resRequest);
props.tutorialContractStepFailed("Failed to call Requester.requestMultiplication()!");
props.tutorialContractStepFailed(
`
Calling Requester.requestMultiplication() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${hashRequest}.
`,
);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ async function runTutorialCheckTwo(props: CheckProps) {
if (checkMinting) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
console.log(resMinting);
props.tutorialContractStepFailed("Failed to call mintTokenCustom()!");
props.tutorialContractStepFailed(
`
Calling Operator.checkMintToken() produced one or more failed receipts!
Debug this transaction using the Cometa service: ${hashMinting}.
`,
);
return false;
}

Expand Down Expand Up @@ -117,7 +122,12 @@ async function runTutorialCheckTwo(props: CheckProps) {

if (Object.values(customTokenBalanceOperator).at(1) === CUSTOM_TOKEN_AMOUNT) {
props.setTutorialChecksEvent(TutorialChecksStatus.Failed);
props.tutorialContractStepFailed("Operator did not receive tokens from CustomToken!");
props.tutorialContractStepFailed(
`
Calling Operator.checkSendToken() produced one or more failed receipts!
To investigate, debug this transaction using the Cometa service: ${hashSending}.
`,
);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,17 @@ contract NFT is NilTokenBase {
* It must also be protected against repeated minting.
* Hint: use totalSupply to eliminate repeated minting.
*/
function mintNFT() public payable {}
function mintNFT() public payable {
// TODO: complete the function
}

/**
* The function for sending the NFT.
* It must call sendTokenInternal().
* @param to The address where the NFT should be sent.
* Hint: call the deposit() function inside Receiver.
*/
function sendNFT(address to) public {}
function sendNFT(address to) public {
// TODO: complete the function
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ This tutorial is verified once the following checks are passed.
* *NFT* cannot mint tokens after the first mint.
* *NFT* can successfully send the custom token to *Receiver*.


To run these checks:

1. Compile both contacts
2. Click on 'Run Checks'
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ contract Deployer is NilBase {
* The function for deploying the Counter contract.
* @param data The bytecode of the Counter contract.
*/
function deploy(bytes memory data, uint salt) public payable {}
function deploy(bytes memory data, uint salt) public payable {
// TODO: complete the function
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ contract Caller {

// Should send some default tokens to the Receiver contract
// using Nil.asyncCall().
function sendValue(address dst) public payable {}
function sendValue(address dst) public payable {
// TODO: complete the function
}
}

/**
Expand All @@ -32,5 +34,6 @@ contract Caller {
* when the deposit() function is called.
*/
contract Receiver {
// TODO: modify the function so that it can accept default tokens
function deposit() public {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ pragma solidity ^0.8.21;

import "@nilfoundation/smart-contracts/contracts/Nil.sol";

/**
* @title Requester
* @author =nil; Foundation
* @notice A contract for requesting the multiplication of two numbers.
*/
contract Requester is NilBase {
using Nil for address;
uint256 private num1 = 5;
uint256 private num2 = 10;
boolean private result;

function requestMultiplication(address dst) public payable {
// TODO: create valid context and callData
bytes memory context = ;
bytes memory callData = ;

Expand All @@ -29,13 +35,19 @@ contract Requester is NilBase {
onlyResponse
{
require(success, "Request failed!");
// TODO: complete the function
}

function getResult() public view returns (bool) {
return result;
}
}

/**
* @title RequestedContract
* @author =nil; Foundation
* @notice A contract for multiplying two numbers and returning the result.
*/
contract RequestedContract {
function multiply(uint256 num1, uint256 num2) public pure returns (uint256) {
return num1 * num2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ This tutorial includes two contracts:

To complete this tutorial:

* Complete both contracts so that *Requester* sends a valid request to *RequestedContract*.
* Make it so that *Requester* sends a valid request to *RequestedContract*.
* On receiving a response, *Requester* must return a boolean value signifying whether the result received from *RequestedContract* is a valid multiplication product.

## Checks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,23 @@ contract CustomToken is NilTokenBase {
* The constructor must set operatorAddress to _operatorAddress.
* @param _operatorAddress The address of the Operator contract.
*/
constructor(address _operatorAddress) {}
constructor(address _operatorAddress) {
// TODO: complete the constructor
}

/**
* A custom wrapper for mintTokenInternal().
* @param amount The amount of the custom token to mint.
*/
function mintTokenCustom(uint256 amount) public payable {}
function mintTokenCustom(uint256 amount) public payable {
// TODO: complete the function
}

/**
* A custom wrapper for sendTokenInternal().
* @param amount The amount of the custom token to send.
*/
function sendTokenCustom(uint256 amount) public payable {}
function sendTokenCustom(uint256 amount) public payable {
// TODO: complete the function
}
}
42 changes: 40 additions & 2 deletions explorer_frontend/src/features/tutorial/init.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { sample } from "effector";
import { combine, sample } from "effector";
import { persist } from "effector-storage/local";
import { changeCode, loadedTutorialPage } from "../code/model";
import { $contracts } from "../contracts/models/base";
import { notFoundRoute } from "../routing/routes/routes";
import { tutorialWithUrlStringRoute } from "../routing/routes/tutorialRoute";
import {
$completedTutorials,
$tutorial,
$tutorialUserSolutions,
$tutorials,
fetchAllTutorialsFx,
fetchTutorialEvent,
Expand All @@ -21,6 +23,11 @@ persist({
store: $completedTutorials,
});

persist({
key: "userSolutions",
store: $tutorialUserSolutions,
});

sample({
clock: [loadedTutorialPage, tutorialWithUrlStringRoute.$params],
source: tutorialWithUrlStringRoute.$params,
Expand All @@ -43,7 +50,26 @@ sample({

sample({
clock: fetchTutorialFx.doneData,
fn: (tutorial) => tutorial.contracts,
source: combine($tutorial, $tutorialUserSolutions),
filter: ([tutorial, userSolutions]) => {
const res = Object.keys(userSolutions).includes(tutorial.urlSlug);
return !res;
},
fn: ([tutorial]) => tutorial.contracts,
target: changeCode,
});

sample({
clock: fetchTutorialFx.doneData,
source: combine($tutorial, $tutorialUserSolutions),
filter: ([tutorial, userSolutions]) => {
const res = Object.keys(userSolutions).includes(tutorial.urlSlug);
return res;
},
fn: ([tutorial, userSolutions]) => {
const solutions = userSolutions[tutorial.urlSlug];
return solutions;
},
target: changeCode,
});

Expand All @@ -63,3 +89,15 @@ sample({
},
target: $completedTutorials,
});

sample({
clock: setCompletedTutorial,
source: combine($contracts, tutorialWithUrlStringRoute.$params, $tutorialUserSolutions),
fn: ([userSolutions, urlSlug, currentSolutions]) => {
return {
...currentSolutions,
[urlSlug.urlSlug]: userSolutions.at(0)?.sourcecode || "",
};
},
target: $tutorialUserSolutions,
Comment thread
khannanov-nil marked this conversation as resolved.
});
5 changes: 5 additions & 0 deletions explorer_frontend/src/features/tutorial/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const $tutorials = tutorialDomain.createStore<Tutorial[]>([]);

export const $completedTutorials = tutorialDomain.createStore<number[]>([]);

export const $tutorialUserSolutions = tutorialDomain.createStore<Record<string, string>>({});

export const $compiledTutorialContracts = tutorialDomain.createStore<App[]>([]);
export const $tutorialContracts = $tutorial.map((tutorial) => (tutorial ? tutorial.contracts : ""));

Expand All @@ -52,12 +54,15 @@ export const notFoundTutorial = tutorialDomain.createEvent();

export const setCompletedTutorial = tutorialDomain.createEvent<number>();

export const setTutorialUserSolutions = tutorialDomain.createEvent<string>();

fetchTutorialFx.use(async (urlSlug) => {
const tutorials = await loadTutorials();
const tutorial = tutorials.find((tutorial) => tutorial.urlSlug === urlSlug);
if (!tutorial) {
throw new Error(`Tutorial for URL ${urlSlug} not found`);
}

return tutorial;
});

Expand Down
Loading