-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
12 additions
and
550 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,194 +1,3 @@ | ||
# Adding a Web Interface | ||
|
||
Last but not least, we'll want to have a user interface for our token.  | ||
|
||
We'll look at a prebuilt template, focusing on how everything fits together and how to wire everything together, rather than on actual advanced tutorials on Web3 UI development. The template includes the code for the Wrapped ERC20 contract we wrote in the previous sections and is based off of the [Vue + fhevmjs template](https://github.com/FhenixProtocol/fhevmjs-vue-template). We use Typescript throughout the example. | ||
|
||
You can find the code for this section in our [github repo](https://github.com/FhenixProtocol/werc20-ui-example). | ||
|
||
#### Starting From a Template | ||
|
||
First, start by cloning the example repository: | ||
|
||
```bash | ||
git clone https://github.com/FhenixProtocol/werc20-ui-example | ||
``` | ||
|
||
#### Install Dependencies | ||
|
||
```bash | ||
cd werc20-ui-example && pnpm install | ||
``` | ||
|
||
#### Get Contracts | ||
|
||
Now, let's bring in our contracts. We're using [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to link the repo containing our contracts and our UI: | ||
|
||
```bash | ||
pnpm get:contracts | ||
``` | ||
|
||
:::tip[Tip] | ||
There are many ways to organize contracts and UI - this is just one pattern, so adapt to what you know & like and don't be afraid to experiment! | ||
::: | ||
|
||
Now you should see the `contracts` folder populated with all the good stuff we created earlier. Feel free to look around and make sure the contract code is updated. If you want to make any changes to the contract code, go ahead. | ||
|
||
#### Compiling Contracts | ||
|
||
At the moment our contracts only exist as solidity files. Let's fix that - | ||
|
||
```bash | ||
pnpm build:contracts | ||
``` | ||
|
||
This will trigger both solidity compilation & building the contracts into typescript files. | ||
|
||
#### Deploying Contracts | ||
|
||
We can also deploy our contract. If LocalFhenix isn't running, you can start it using `pnpm start:localfhenix` from the contracts folder. | ||
|
||
```bash | ||
cd contracts | ||
pnpm deploy:contracts --network localfhenix | ||
``` | ||
|
||
Seeing an error? you might be missing tokens for your deployer address. In this case try `pnpm faucet` to get some tokens for localfhenix. | ||
|
||
#### Connecting the Frontend | ||
|
||
Let's look at why all this is useful to do together with our frontend code. Everything we'll look at is in App.vue. | ||
|
||
First, we initialize fhevmjs: | ||
|
||
```typescript | ||
export default defineComponent({ | ||
name: 'App', | ||
... | ||
setup() { | ||
const instance = ref<FhevmInstance | undefined>(undefined); | ||
return {instance}; | ||
}, | ||
mounted() { | ||
let self = this; | ||
const initInstance = async () => { | ||
await initFhevm(); | ||
const chainIdHex = await window.ethereum.request({method: 'eth_chainId'}); | ||
const thisProvider = new ethers.BrowserProvider(window.ethereum) | ||
|
||
let networkPublicKey = localStorage.getItem('fhepubkey'); | ||
if (!networkPublicKey || networkPublicKey === "0x") { | ||
publicKey = await thisProvider.call({from: null, to: '0x0000000000000000000000000000000000000044'}); | ||
if (publicKey) { | ||
// cache global public key - should change it to be per chain-id | ||
localStorage.setItem('fhepubkey', networkPublicKey); | ||
} | ||
} | ||
const chainId = parseInt(chainIdHex, 16); | ||
return createInstance({chainId, publicKey: networkPublicKey}); | ||
}; | ||
initInstance().then( | ||
(instance) => { | ||
this.loading = false; | ||
this.instance = instance; | ||
this.refreshBalances(); | ||
} | ||
); | ||
}, | ||
... | ||
}); | ||
``` | ||
|
||
Now, interacting with encrypted data in our contracts is pretty straightforward if you've seen EVM Web3 contracts in action - we can simply import the types & deployment data generated by our contracts and use them directly: | ||
|
||
```typescript | ||
// can use this, hard code the address or any other way to map the contract address | ||
import DeployedContract from "../contracts/deployments/localfhenix/WrappingERC20.json"; | ||
import {WrappingERC20__factory} from "../contracts/types"; | ||
|
||
async getEncryptedBalance(): Promise<number> { | ||
const thisProvider = new ethers.BrowserProvider(window.ethereum) | ||
let signer = await thisProvider.getSigner(); | ||
let contractAddress = DeployedContract.address; | ||
// ts-ignore because different ethers versions can cause typescript to think | ||
// there's a type mismatch | ||
// @ts-ignore | ||
const werc20 = WrappingERC20__factory.connect(contractAddress, signer) | ||
|
||
if (this.instance) { | ||
// this is an ehpemeral key used to query encrypted data for the user | ||
// NOT the global network public key from the init | ||
// Here we don't really care about the EIP-712 token & signature, we just want to | ||
// use the public key associated with it | ||
let txPublicKey = this.instance.getTokenSignature(contractAddress)?.publicKey; | ||
if (!txPublicKey) { | ||
txPublicKey = await this.instance.generateToken({verifyingContract: contractAddress}).publicKey; | ||
} | ||
try { | ||
|
||
let encBalance = await werc20.balanceOfEncrypted(txPublicKey); | ||
// instance.decrypt uses the txPublicKey which is loaded internally | ||
// which is why we don't explicitly provide it | ||
this.encryptedBalance = this.instance.decrypt(contractAddress, encBalance); | ||
} catch (e) { | ||
// 0 balance will error here | ||
} | ||
} | ||
|
||
return 0 | ||
} | ||
``` | ||
|
||
Sending a transaction is even easier, since there's less fiddling with keys | ||
|
||
```typescript | ||
async sendToContract (input: number) { | ||
if (!this.instance) { | ||
alert("fhe not initialized"); | ||
return; | ||
} | ||
const thisProvider = new ethers.BrowserProvider(window.ethereum) | ||
|
||
let signer = await thisProvider.getSigner(); | ||
try { | ||
// @ts-ignore | ||
const werc20 = WrappingERC20__factory.connect(DeployedContract.address, signer) | ||
|
||
let encToSend = await this.instance.encrypt32(input); | ||
|
||
// for example purposes just send to the contract | ||
await werc20.transferEncrypted(DeployedContract.address, encToSend); | ||
} catch (e) { | ||
alert(`error: ${e}`) | ||
} | ||
this.refreshBalances(); | ||
}, | ||
``` | ||
|
||
#### Okay, Let's Run this thing! | ||
|
||
```bash | ||
pnpm serve | ||
``` | ||
|
||
In the unlikely scenario that everything worked up to this point you should now see: | ||
|
||
``` | ||
DONE Compiled successfully in 1657ms 1:48:47 PM | ||
App running at: | ||
- Local: http://localhost:8082/ | ||
- Network: http://172.21.20.133:8082/ | ||
Note that the development build is not optimized. | ||
To create a production build, run pnpm run build. | ||
No issues found. | ||
``` | ||
|
||
**GG WP** :tada::tada::tada::tada: | ||
|
||
#### Side Note: Why Go Through all this? | ||
|
||
You can just copy over the compiled .json files from your contracts, hard code deployed addresses (or pass them through environment variables) and be done with it! Yep, that can be a quick-and-dirty solution. However, the more complex the project the more challenging it becomes to cleanly integrate contracts and user interfaces. Frequent changes, debugging and coordinating multiple people are all challenges - but even for personal development, I just like having my UI aware of the contract interfaces and provide typing hints.   | ||
TO BE CREATED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,3 @@ | ||
# Testing & Interacting | ||
|
||
Now let's talk a little bit about testing for Fhenix FHE contracts. If you don't care about this and you just want to hack, we recommend at least skimming the Remix Section to understand how to use encryption/decryption APIs. | ||
|
||
### Manual Testing (Remix) | ||
|
||
Some developers prefer a more manual approach at first - where you just want to quickly verify that something works, play around with a small function or just verify functionality without writing tests or setting up a full developer environment. To do this, we'll show how you can manually test contracts with FHE-encrypted inputs and outputs without using fhevmjs as a helper library. | ||
|
||
Conveniently, LocalFhenix comes with APIs that perform encryption or decryption for you. | ||
|
||
The APIs are: | ||
|
||
<table> | ||
<thead> | ||
<tr> | ||
<th width="138">Function</th> | ||
<th width="411.3333333333333">Endpoint</th> | ||
<th>Description</th> | ||
<th data-hidden>Method</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Encrypt</td> | ||
<td>encrypt?number=integer&int=32/16/8></td> | ||
<td>Encrypt a number. If int size is not provided defaults to 32.</td> | ||
<td>GET</td> | ||
</tr> | ||
<tr> | ||
<td>Decrypt</td> | ||
<td>decrypt?encrypted=hex string</td> | ||
<td>Decrypt a number.</td> | ||
<td>GET</td> | ||
</tr> | ||
<tr> | ||
<td>Decryption Public</td> | ||
<td>decryption_public</td> | ||
<td>Get the public key that the service uses to decrypt data.</td> | ||
<td></td> | ||
</tr> | ||
<tr> | ||
<td>Network Public Key</td> | ||
<td>public</td> | ||
<td>Get the public key of the network.</td> | ||
<td>GET</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
|
||
For example, if I wanted to encrypt the number 259 as a uint16, I would use: | ||
|
||
```bash | ||
curl "http://localhost:5000/encrypt?number=259&int=16" | ||
``` | ||
|
||
To decrypt a number, we use a GET request, with the encrypted string (returned from a `TFHE.reencrypt` function), such as: | ||
|
||
```bash | ||
curl "http://localhost:5000/decrypt&encrypted=0x0dcc7d99ecbb6fc8b8657321b497b39b2bd6017dd4a159b1d1a801e58149422cb5aa4e2d73994bc15e1dc12604a0da1db0" | ||
``` | ||
|
||
> **Note:** At the moment, to perform decryption you must provide the public key of the service for it to be able to decrypt data. That means you need to call the view function of the FHE-enabled contract with the key from the _**`decryption_public`**_ endpoint. | ||
|
||
### Writing Unit Tests | ||
|
||
As we mentioned in previous sections, Fhenix uses extensions to the standard EVM to enable FHE functionality. To write unit tests, we use LocalFhenix instead of hardhat network - this is similar to deploying & testing contracts on Ganache. To see an example of how to write unit tests & create CI workflows you can refer to our [reference wrapped ERC20 contract repo](https://github.com/FhenixProtocol/werc20-example).  | ||
TO BE CREATED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.