# NOTAS Junio 13
## App architecture
The first app had this architecture.
We storage the questions in a static json.
```sequence
User -> FE: Load page
FE -> FE: Get questions
FE --> User: Page files (static)
User -> FE: Submit responses
FE --> User: Display results
```
Then we add swarm to storage the questions:
```sequence
User -> FE: Load page
FE -> DS: Get questions
DS --> FE: Questions JSON
FE --> User: Page files (static)
User -> FE: Submit responses
FE --> User: Display results
```
## Pre-requisites
* Install swarm.
* Install Metamask add-on in your browser.
* Truffle.
* Install an add-on to enable CORS.
* Node and git.
## Uploading questions on swarm
Run `swarm` in a new terminal
```bash=
swarm
```
Then run:
```bash=
swarm --defaultpath js/questions.json --manifest=true up js/questions.json
8be54696f73724a8d956c35ddb3351a664115ba53c1ccde8cfbb798c2f01205a
```
To see the upload info, copy and paste the hash and make a request using `curl`:
```bash=
curl -s http://localhost:8500/bzz-raw:/8be54696f73724a8d956c35ddb3351a664115ba53c1ccde8cfbb798c2f01205a | jq
{
"entries": [
{
"hash": "9cab4c9198ea0e1ed8bccaa44d3830218ae2e3fa5ba03d828b33f83e6e94532e",
"contentType": "application/json",
"mode": 436,
"size": 1546,
"mod_time": "2020-06-15T20:36:06-05:00"
}
]
}
```
To get the file content, copy and paste the new hash:
```bash=
curl -s http://localhost:8500/bzz-raw:/9cab4c9198ea0e1ed8bccaa44d3830218ae2e3fa5ba03d828b33f83e6e94532e | jq
{
"questions": [
]
...
```
### Getting the questions from swarm
**Important:** You need to get CORS disabled!
Add the new data source in `app.js`:
Before:
```javascript=
mounted: () {
axios.get('js/questions.json')
...
}
```
After:
```javascript=
mounted: () {
axios.get('http://localhost:8500/bzz-raw:/9cab4c9198ea0e1ed8bccaa44d3830218ae2e3fa5ba03d828b33f83e6e94532e')
}
```
---
## Setup the project
You will create the truffle files and folder in the root folder, so you must to say "yes" when truffle warns you
```bash=
truffle init
```
It will create the contract's folder, migrations, and truffle config
## Creating the contract
### Specify the soldity compiler version
In the `truffle-config.js` file, in the `compilers` section, we need to change the solidity compiler version
```javascript=
compilers: {
solc: {
version: "^0.4.14", // Fetch exact version from solc-bin (default: truffle's version)
}
}
```
### Contract boiler plate:
In the `contracts` folder, create a new file named `Quiz.sol`
Copy and paste the following code:
```
pragma solidity ^0.4.14;
contract Quiz {
string[] rightAnswers = ["c", "a", "a", "b"];
address[] public players;
uint8[] public playerHits;
constructor() public {
}
function getPlayerHits() public view returns (uint8[] memory) {
return playerHits;
}
function getPlayers() public view returns (address[] memory) {
return players;
}
function answerQuestions (string playerAnswers) public returns (string memory) {
}
}
```
In the terminal run the `compile` command
```bash=
truffle compile
```
### Before migrate
If you are using `testnet`, `mainnet`, or `regtest` with rskj you should edit the `truffle-config` file to setup the networks:
Also you need to install the `hdwallet provider`:
```bash=
npm install @truffle/hdwallet-provider --save
```
then, add the network data in the `networks` sections in the `truffle-config` file.
### Migration
Add a new file named `2_deploy_contracts.js` in the `migrations` folder and write the following code:
```javascript=
var Quiz = artifacts.require('Quiz');
module.exports = function (deployer) {
deployer.deploy(Quiz)
}
```
### Run migrations
For `ganache`:
Uncomment the `developer` network and edit the port:
```javascript=
networks: {
development: {
host: "127.0.0.1", // Localhost
port: 7545, // Ganache port
network_id: "*", // Any network
}
}
```
Open ganache, and click on the `add workplace`, navigate to your app folder and select the `truffle-config.js` file.
Run the migrations:
```bash=
truffle migrate --network development
```
### Adding web3.js
Add the `web3` CDN in the `index.html` file and the `truffle-contract.js` script.
I copy the `truffle-contract.js` file from the [pet-shop tutorial github](https://github.com/truffle-box/pet-shop-box/blob/master/src/js/truffle-contract.js).
Note: I recommend that the `truffle-contract` be the first script in the 'script' section in `index.html` file.
```htmlembedded=
<script src="js/truffle-contract.js"></script>
<!-- -->
<script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
```
So, at the end, the scripts order will be:
1. `truffle-contract.js` <- truffle contracts library
2. `vue.js` <- Vue CDN library
3. `bulma-steps.min.js` <- Bulma steps (used to render the questions in a step by step form).
4. `axios.min.js` <- The axios library. (used to get the questions data from DS).
5. `web3.min.js` <- Web3 library
6. `app.js` <- our app script
```htmlmixed=
<script src="js/truffle-contract.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/bulma-steps@2.2.1/dist/js/bulma-steps.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
<script src="js/app.js"></script>
```
### Getting the questions Second part.
In the `app.js` file, clear the `mounted` method and add this new lines:
```javascript=
mounted () {
this.initWeb3()
.then(() => {
console.log('App initialized')
})
},
```
Add a new method `initWeb3` in the beggining of the `method` section:
Note: the `initWeb3` method has the word `async`!
> [name=Hiram E. Pérez] You must to have installed the `Metamask` add-on in your browser.
```javascript=
methods: {
async initWeb3() {
await axios.get('http://localhost:8500/bzz-raw:/07f1112168025f4c309b652fb364b9ea126728e7b0959338b0bf9f5187d474d0')
.then(response => {
this.questions = response.data.questions
})
if (window.ethereum) {
this.web3Provider = window.ethereum
try {
// request account access
await window.ethereum.enable()
} catch (error) {
console.error("user denied account access")
}
} else if (window.web3) {
this.web3Provider = window.web3.currentProvider
} else {
this.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545')
}
web3 = new Web3(this.web3Provider)
return this.initContract()
},
// more methods
}
```
### Init the contract
Add the `initContract` method just below of the `initWeb3` function:
> [name=Hiram E. Pérez] It initializes the contract, I mean, load the contract logic (everything in the `Quiz.sol` contract).
> *TruffleContract()* create an instance of our contract, then we set its web3 provider using our `web3Provider`.
```javascript=
initContract: function (params) {
return axios.get('../build/contracts/Quiz.json')
.then(response => {
const QuizArtifact = response.data
this.contracts.Quiz = TruffleContract(QuizArtifact)
// Set the provider for our contract
this.contracts.Quiz.setProvider(this.web3Provider)
})
},
```
### Add sendQuestions method
Add a new method just below the `selectOption` function:
```javascript=
sendQuestions: function () {
// ...
},
```
This method handles the click event on the `ok` button in the modal window. This modal appears when you answer all the questions.
Add the following code in the `sendQuestions` function.
```javascript=
const contract = this.contracts.Quiz
web3.eth.getAccounts((error, accounts) => {
if (error) {
console.error(error)
}
const account = accounts[0]
this.contracts.Quiz.deployed()
.then(instance => {
const quizInstance = instance
return quizInstance.answerQuestions(this.answers.join("."), { from: account })
})
.then(result => {
document.querySelector('div.modal').classList.remove('is-active')
})
.catch(err => {
console.error("=======================")
console.error(err)
console.error(err.message)
console.error(err.stack)
console.error('=======================')
})
})
```
**Important:** The next line calls the `answerQuestions` function on the smart contract Quiz.sol It will open a `Metamask` window asking if you want sign the contract.
```javascript=
return quizInstance.answerQuestions(this.answers.join("."), { from: account })
```
> [name=Hiram E. Pérez] Solidity don't accept arrays of strings as paramenters in functions, so we need the array of answers as string.
>
### Answer question method in contract
Add the following method just below of `getPlayers` function in the `Quiz.sol`
```
function answerQuestions (string playerAnswers) public returns (string memory) {
uint hits = 0;
var s = playerAnswers.toSlice();
var delim = ".".toSlice();
var parts = new string[](s.count(delim) + 1);
for (uint i = 0; i < parts.length; i++) {
parts[i] = s.split(delim).toString();
if (parts[i] == rightAnswers[i]) {
hits++;
}
}
players.push(msg.sender);
hits.push(hits);
return playerAnswers;
}
```
This function compares the rigth answers with the user's ones.
### Add the string solidity contract
Solidity doesn't accept arrays of string as function parameters.
Create a new file named `Strings.sol` in the `contracts` folder.
Open [this repo](https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol), copy and paste it in the `string.sol` file.
In Quiz.sol, please add this line just below of `pragma solidity ^0.4.14;` line.
```
import "./strings.sol";
```
Then add the line `using strings for *;` below of `contract Quiz {` line:
```
pragma solidity ^0.4.14;
import "./strings.sol";
contract Quiz {
using strings for *;
// the rest of the code ...
```
Add [this script](https://github.com/ethereum/dapp-bin/blob/master/library/stringUtils.sol)
Run the migrations:
```bash=
truffle migrate --reset --network development
```