This commit is contained in:
Richard Ramos 2018-07-11 08:56:16 -04:00
parent 79893be65e
commit d865e28f86
2 changed files with 19 additions and 14 deletions

View File

@ -2,7 +2,7 @@
Before starting, it's important to have setup correctly our workspace. You can follow the steps described in the [documentation](https://embark.status.im/docs/), since it will always be updated to reflect latest changes in the framework. If you already have Embark and its dependencies installed you can skip this step.
At the moment of writing the tutorial, the commands I used on Ubuntu 18.04 were these. Some steps change depending on the operative system used (i.e. [node-gyp](https://github.com/nodejs/node-gyp)), as well as the versions of files. Please refer to Embark or any of the installed tools' documentation website for troubleshooting. Also a docker image is being built with Embark and all its dependencies, and it may be used to follow this tutorial.
At the moment of writing the tutorial, the commands I used on Ubuntu 18.04 were these. Some steps change depending on the operative system used (i.e. [node-gyp](https://github.com/nodejs/node-gyp)), as well as the versions of files. Please refer to Embark or any of the installed tools docs for troubleshooting. Also a [docker image](https://github.com/embark-framework/embark-docker/) is being built with Embark and all its dependencies, and it may be used to follow this tutorial.
## Ubuntu Installation

View File

@ -4,15 +4,15 @@ The DApp will be running in http://localhost:8000 . This URL will present a simp
[IMAGE_HERE]
## Minting tokens
Each spaceship in this dapp is an ERC721 token, and as such, they have their own characteristics. Since they represent an spaceship (which could be used in a game), the attributes they will have are: lasers, shields, and energy, as well as an image.
Each spaceship in this dapp is an ERC721 token, and as such, they have their own characteristics. Since these tokens represent spaceships (that could be used in a game), the attributes they will have are: lasers, shields, and energy, as well as an image.
The contract we will use to represent the spaceships is `SpaceshipToken` and it is located in the file: `contracts\SpaceshipToken.sol`.
> Embark will attempt to deploy all the contracts it finds in the `contracts` folder and their dependencies. For this project, we already pre-configured the contracts we will and will not deploy. You can read more about this in the [documentation](https://embark.status.im/docs/contracts.html#Specify-contract-file).
For minting the tokens we will use a combination of IPFS to store the image and attributes, and the function `mint(bytes _metadataHash, uint8 _energy, uint8 _lasers, uint8 _shield, uint _price) public onlyOwner`.
For minting the tokens we will use IPFS to store the image and attributes, and the function `mint(bytes _metadataHash, uint8 _energy, uint8 _lasers, uint8 _shield, uint _price)` of the `SpaceshipToken` contract.
Let's edit the file `app/js/components/addToken.js` which contains the form to add tokens. You can use the spaceship images from the folder `resources`
Let's edit the file `app/js/components/addToken.js` which contains the form to add tokens. You can use the spaceship images from the folder `resources` for testing.
#### Importing Embark and contracts
Before being able to interact with the EVM, and with IPFS, you need to import both EmbarkJS and the contract file:
@ -24,10 +24,10 @@ import SpaceshipToken from 'Embark/contracts/SpaceshipToken';
### Adding functionality to the Create button
[IMAGE_HERE]
When you click on the 'Create' button, it will invoke the `handleClick(e)` method which doesn't have a useful implementation at the moment. Let's start by loading the image into IPFS.
When you click on the 'Create' button, it will invoke the `handleClick(e)` method which doesn't have a useful implementation at the moment. Let's start by loading the ship's attributes and image into IPFS.
#### Creating attributes
Each ERC721 may have an optional list of attributes or metadata that follows the "ERC721 Metadata JSON Schema". We'll start by creating a json object that contains this metadata using the values from the input fields:
Each ERC721 may have an optional list of attributes or metadata that follows the "ERC721 Metadata JSON Schema". Start by creating a JSON object that contains this metadata using the values from the input fields:
```
let attributes = {
@ -40,10 +40,10 @@ let attributes = {
}
}
```
Notice that the image attribute is empty because we still don't know the url of the image, we need to upload it to IPFS first.
Notice the image attribute is empty because we still don't know the url of the image yet - it needs to be uploaded to IPFS first.
#### Uploading image to IPFS
To upload the image file, we'll use `uploadFile(inputFile[])` function of Embark.Storage which returns a promise, and after the promise resolves, we'll add the image hash to our attributes object. We also will add a `catch` block to display any error we receive on the browser console, as well as a `finally` block for enabling the `Create` button after the file upload.
To upload the image file, use `uploadFile(inputFile[])` function of `Embark.Storage` which returns a promise, and after the promise resolves, add the image hash to the attributes object. We also will add a `catch` block to display any error we receive on the browser console, as well as a `finally` block for enabling the `Create` button after the file upload.
```
EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
@ -61,7 +61,11 @@ EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
```
#### Uploading attributes to IPFS
An ERC721 token that supports the metadata standard needs to return an URI with all the attribute info of the token when the `tokenURI(uint256 _tokenId)` function of the contract is invoked. Since IPFS already provides an URL for all the resources it has, we will proceed to store the attributes object in IPFS an return its URL when we invoke that function. We will start by modifying our then() implementation by calling the `saveText(text)` method of Embark.Storage. This also returns a promise, and we will deal with it in the next step.
An ERC721 token that supports the metadata standard has a `tokenURI(uint256 _tokenId)` function that returns an URI with all the attribute info of the token. Since IPFS already provides an URL for all the resources it has, we will store the attributes object in IPFS an return its URL when we invoke that contract function.
Modify our `then()` implementation by calling the `saveText(text)` method of `Embark.Storage`. This also returns a promise with the hash of the stored attributes object.
This hash will be stored in the `metadataHash` attribute of the `Spaceship` struct in the contract and will be returned along with an IPFS gateway url.
```
EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
@ -83,7 +87,8 @@ EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
```
#### Using our contract to mint the new token
Finally, once we have both the Image and the Attributes stored in IPFS it's time to mint our new token. This is done in two steps: first, we estimate the gas cost to invoke the `mint(bytes _metadataHash, uint8 _energy, uint8 _lasers, uint8 _shield, uint _price) public onlyOwner` (it's a good practice, in order to avoid running into Out of Gas exceptions), and then, we will create the transaction. To estimate gas, we use the estimateGas() function of the contract:
Finally, once we have both the Image and the Attributes stored in IPFS it's time to mint our new token. This is done in two steps: first, we estimate the gas cost to invoke `mint(bytes _metadataHash, uint8 _energy, uint8 _lasers, uint8 _shield, uint _price)` (it's a good practice, in order to avoid running into Out of Gas exceptions), and then, we will create the transaction. To estimate gas, use `estimateGas()`.
```
const { mint } = SpaceshipToken.methods;
@ -113,11 +118,11 @@ EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
});
```
Notice that we stored the function call in the `toSend` variable, however it does not mean that we're creating a transaction here. With this `toSend` variable we're able to send a transaction or estimating gas (also, calling a value if the contract function were a `constant`/`view`/`pure` function).
Notice that we stored the function call in the `toSend` variable. however, we're not creating a transaction here. With `toSend` we're able to send a transaction or estimate gas (also, calling a value if the contract function were a `constant`/`view`/`pure` function).
> We introduced the use of two `web3.utils` functions: `toHex`, to convert any given value to a HEX string, and also, `toWei`, to convert from ether to wei, since in the UI we're introducing the values as ether, and the smart contract uses wei.
> Two `web3.utils` functions are being used here: `toHex`, to convert any given value to a HEX string, and `toWei`, to convert from ether to wei, since in the UI the values are introduced as ether, and the smart contract uses wei.
After estimating gas, we proceed to create the transaction by returning the `send` method of our contract with the estimated gas plus an additional wei amount (estimateGas() is not always precise).
After estimating gas, proceed to create the transaction by returning the `send` method of our contract with the estimated gas plus an additional wei amount (estimateGas() is not always precise due to gas refunds or logic that may depend of the contract state).
```
...
@ -142,7 +147,7 @@ After estimating gas, we proceed to create the transaction by returning the `sen
...
...
```
Notice that `send()` returns a promise, and when it resolves, you can access a receipt object with all the information about the transaction as well as logs generated. Here we also clean the form, and invoke `this.props.loadShipsForSale()` to reload the list of ships. It doesn't do anything at the moment since we haven't implemented it yet.
Notice `send()` returns a promise that resolves to a receipt object with all the information about the transaction as well as any event logs that were emitted during the function exectuib. Here we also clean the form, and invoke `this.props.loadShipsForSale()` to reload the list of ships. It doesn't do anything at the moment since we haven't implemented it yet.
We did not implement success/error alert messages or warnings to the user. These can be added inside the promise resolution and catch blocks.