Skip to content

Commit 1c35120

Browse files
authored
Merge pull request #69 from ten-protocol/tudor/update_dev_qs
improve dev quick start
2 parents 64855c1 + 45c958c commit 1c35120

File tree

1 file changed

+84
-83
lines changed

1 file changed

+84
-83
lines changed

docs/introduction/developer-quickstart.md

Lines changed: 84 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,39 @@
22
sidebar_position: 4
33
---
44

5-
# Migrate your dApp to Ten
6-
Migrating to Ten is a straightforward process that immediately unlocks "Programmable Encryption".
5+
# Migrate your dApp to TEN
76

8-
There are a couple of changes you need to make:
7+
Migrating to TEN enables your dApp to leverage "Programmable Encryption". Below are steps to help you transition smoothly.
98

10-
1. Change your hardhat deployment script so that you can use `--network ten`.
11-
2. Add logic to your view functions to protect data (if needed).
12-
3. Configure event log visibility (if needed).
13-
4. Add a widget to your javascript UI to onboard Ten users.
9+
### Key Migration Steps
10+
11+
1. Update your Hardhat deployment to support the `--network ten` option.
12+
2. Add data protection logic to your view functions (if applicable).
13+
3. Configure visibility rules for event logs and internal storage.
14+
4. Add the TEN onboarding widget to your JavaScript UI.
1415

1516
## 1. Configuring Hardhat
1617

17-
To begin building on Ten, you can start by setting up a Hardhat project as usual.
18+
First, set up a Hardhat project if you haven't already.
1819

19-
### 1.1 Installing the Ten Hardhat Plugin
20+
### 1.1 Installing the TEN Hardhat Plugin
2021

21-
To integrate the Ten Network into your Hardhat project, install the ten-hardhat-plugin:
22+
To add TEN Network compatibility, install the `ten-hardhat-plugin`:
2223

2324
```bash
2425
npm install ten-hardhat-plugin
2526
```
2627

27-
Note: Plugins can be installed using `npm` or `yarn`.
28+
_You can use `npm` or `yarn` to install plugins._
2829

29-
### 1.2 Configuring `hardhat.config.js` for the Ten Testnet
30+
### 1.2 Configuring `hardhat.config.js`
3031

31-
Open `hardhat.config.js` in your project's root directory and configure it in the following way:
32+
Modify `hardhat.config.js` in your projects root directory as follows:
3233

3334
```javascript
34-
import {HardhatUserConfig} from "hardhat/config";
35+
import { HardhatUserConfig } from "hardhat/config";
3536
import "@nomiclabs/hardhat-waffle";
36-
import 'ten-hardhat-plugin'
37+
import "ten-hardhat-plugin";
3738

3839
module.exports = {
3940
solidity: "0.8.10",
@@ -42,35 +43,37 @@ module.exports = {
4243
// Configuration for the Hardhat Network
4344
},
4445
ten: {
45-
url: "https://testnet.ten.xyz/v1/",
46-
chainId: 443,
46+
url: "https://testnet.ten.xyz/v1/",
47+
chainId: 443,
4748
accounts: ["your-private-key"],
4849
},
4950
},
5051
};
5152

5253
export default config;
5354
```
54-
Now, you can start writing or migrating the smart contracts.
5555

56-
# 2. Writing Smart Contracts
56+
Once configured, you can start writing or migrating your smart contracts.
5757

58-
Ten performs bytecode execution in the EVM identically to Ethereum, allowing developers to leverage their existing codebase and tools.
58+
## 2. Writing Smart Contracts for TEN
5959

60-
The main difference is that, during execution, private variables and the internal state of the contract are hidden from everyone, including node operators and the sequencer.
61-
This is a major advantage that represents "Programmable Privacy".
60+
TEN executes smart contracts within the EVM similarly to Ethereum, so you can reuse your existing code.
61+
However, the execution and the internal state are hidden from everyone, including node operators and the sequencer.
6262

6363
:::info
64-
In Ten, the internal node database is encrypted, and the execution itself is also encrypted inside the TEE.
64+
TEN encrypts both the execution and its internal database using Trusted Execution Environments (TEEs).
6565
:::
6666

67-
The calls to [getStorageAt](https://docs.alchemy.com/reference/eth-getstorageat) are disabled by default, so all data access will be performed through view functions which are under the control of the smart contract developer. Note that public variables are accessible to everyone because Solidity automatically generates a getter function for them.
67+
The [getStorageAt](https://docs.alchemy.com/reference/eth-getstorageat) method is disabled by default on TEN, so data access relies on view functions that you define.
68+
Public variables remain accessible as Solidity automatically creates getters for them.
69+
70+
Let's illustrate with a basic storage dApp example where users can store and retrieve a number.
6871

69-
We'll illustrate how this works by creating a simple data storage example. In this dApp, users can store a number and retrieve it later.
72+
At every step, we'll add a new feature and explain the difference between `TEN` and `Ethereum`.
7073

71-
## Step 1: Declaring a Public Variable
74+
## Step 1: Basic contract with a Public Variable
7275

73-
### Code:
76+
### Code
7477

7578
```solidity
7679
// SPDX-License-Identifier: MIT
@@ -85,13 +88,17 @@ contract StorageExample {
8588
}
8689
```
8790

88-
### Explanation:
91+
### Explanation
92+
93+
In this step, we created a public variable `storedValues` that maps the provided value to the address of the user who called the `storeValue` function.
8994

90-
In this step, we've declared a public variable `storedValues`. Solidity automatically generates a public getter view function for it, so on both Ethereum and Ten, you can call this view function without any restrictions. We also created a function that allows users to store a value against their address.
95+
Because the variable is public, Solidity will provide a default public getter for it.
9196

92-
## Step 2: Transitioning to a Private Variable with a Getter Function
97+
Since there are no data access restrictions, on both Ethereum and TEN, everyone will be able to read the values of all users.
9398

94-
### Code:
99+
## Step 2: Converting to a Private Variable with an explicit Getter Function
100+
101+
### Code
95102

96103
```solidity
97104
contract StorageExample {
@@ -107,19 +114,18 @@ contract StorageExample {
107114
}
108115
```
109116

110-
### Explanation:
117+
### Explanation
111118

112-
We've now made our data variable private, meaning it can't be accessed directly from outside the contract. To fetch its value, we've provided a custom public view function `getValue` where the user provides the address. On both Ethereum and Ten, if you call this function you will retrieve the number stored by that address.
119+
The `storedValues` variable is now private, and we added a basic `getValue` function for users to retrieve their value.
113120

114-
:::caution
115-
In Ethereum, the `_storedValues` variable can also be accessed directly using the `getStorageAt` method, but not in Ten.
116-
:::
121+
On both Ethereum and TEN, anyone can call `getValue` to retrieve any value.
122+
On Ethereum, `_storedValues` can also be accessed directly with `getStorageAt`
117123

118-
## Step 3: Implementing Data Access Control
124+
## Step 3: Data Access Control
119125

120-
In this step, we aim to restrict users to only access their own value. This feature can only be implemented in Ten because as mentioned above, `_storedValues` is not hidden in Ethereum.
126+
In this step, we'll add restrictions so users can only access their own data.
121127

122-
### Code:
128+
### Code
123129

124130
```solidity
125131
contract StorageExample {
@@ -136,21 +142,23 @@ contract StorageExample {
136142
}
137143
```
138144

139-
### Explanation:
145+
### Explanation
140146

141-
Since `getValue` is the only function which exposes the values, we can add a check like this: `require(tx.origin == account, "Not authorized!");` If anyone, other than the original account, asks for the value, they will get an error.
147+
The key line is: ``require(tx.origin == account, "Not authorized!");``, which ensures that the caller of the view function is the owner of the data.
142148

143149
:::info
144-
In Ethereum, since all data is accessible anyway, there is no need to sign calls to view functions, so `tx.origin` can be spoofed.
150+
TEN uses "Viewing Keys" to authenticate view function calls.
145151
:::
146152

147-
In Ten, the platform ensures that calls to view functions are authenticated, which means that behind the scenes, there is a "Viewing Key" signature of the `tx.origin` address.
153+
**When deployed on TEN, this code guarantees that all users can only access their own values, and nobody can read the `_storedValues`.**
154+
155+
## Step 4: Emitting Events - Default Visibility
148156

149-
## Step 4: Emitting Events - Default visibility
157+
Event logs notify UIs about state changes in smart contracts.
150158

151-
Events in Ethereum are crucial for UIs to react to smart contract state changes. In this step, we'll emit an event when a user stores a value. We'll also gauge the popularity of our contract by emitting an event when certain milestones are reached.
159+
To improve our smart contract, well emit an event when a user stores a value and milestone events when a specific size threshold is met.
152160

153-
### Code:
161+
### Code
154162

155163
```solidity
156164
contract StorageExample {
@@ -176,50 +184,38 @@ contract StorageExample {
176184
}
177185
```
178186

179-
### Explanation:
187+
### Explanation
180188

181-
On Ethereum, events are visible to anyone. For example, you can subscribe to the `DataChanged` event and receive notifications in real time about the data of everyone else.
189+
Notice how we defined the two events: `DataChanged` and `MilestoneReached`, and are emitting them in the `storeValue` function.
182190

183-
The programmable encryption of Ten allows you full control over visibility but also has sensible defaults.
184-
Event logs can be queried using `eth_getLogs` or subscribed to using the `logs` endpoint. Both these calls are authenticated, and the platform makes sure to return only visible logs.
191+
In Ethereum, everyone can query and subscribe to these events. This obviously can't be the case for TEN because it would completely break the functionality.
185192

186-
In our case, the requirements are very simple and common sense:
193+
Notice how in this version, we have no configuration for event log visibility, so we are relying on the default rules.
187194

188-
- The `DataChanged` event is specific to an account, so it should only be received by that user.
189-
- `MilestoneReached`, on the other hand, is intended for everyone, as we want to show how popular our contract is.
195+
Rule 1: Event logs that contain EOAs as indexed fields (topics) are only visible to those EOAs.
196+
Rule 2: Event logs that don't contain any EOA are visible to everyone.
190197

191-
The behaviour you desire is to restrict the visibility of `DataChanged`, but not that of `MilestoneReached`. **Which is exactly how it works by default!**
198+
In our case, the default rules ensure that:
199+
- `DataChanged` is visible only to the address that is storing the value.
200+
- `MilestoneReached` is publicly visible.
192201

193-
Default behaviour:
194202

195-
- `DataChanged` - has an address as a topic (an indexed field), which instructs the platform that the event log is only visible to that address.
196-
- `MilestoneReached` - has no address topic which by default means it is visible to everyone.
203+
## Step 5: Customizing Event Visibility
197204

198-
All you have to do is emit events as usual, and the platform applies common-sense visibility rules.
205+
The default visibility rules are a good starting point, but complex dApps require greater flexibility.
199206

207+
TEN give you explicit control over event visibility.
200208

201-
## Step 5: Emitting Events - Configuring visibility
202-
203-
Once you prepare your application for production, you will want explicit control over the event visibility.
204-
205-
### Code:
209+
### Code
206210

207211
```solidity
208212
interface ContractTransparencyConfig {
209-
enum Field {
210-
TOPIC1, TOPIC2, TOPIC3,
211-
SENDER, // tx.origin - msg.sender
212-
EVERYONE // the event is public - visible to everyone
213-
}
214-
215-
enum ContractCfg {
216-
TRANSPARENT, // internal state via getStorageAt is accessible to everyone, all events are public
217-
PRIVATE // internal state is hidden, and events can be configured individually
218-
}
213+
enum Field { TOPIC1, TOPIC2, TOPIC3, SENDER, EVERYONE }
214+
enum ContractCfg { TRANSPARENT, PRIVATE }
219215
220216
struct EventLogConfig {
221-
bytes32 eventSignature; // the event signature hash
222-
Field[] visibleTo; // list of fields denoting who can see the event when private
217+
bytes32 eventSignature;
218+
Field[] visibleTo;
223219
}
224220
225221
struct VisibilityConfig {
@@ -230,7 +226,7 @@ interface ContractTransparencyConfig {
230226
function visibilityRules() external pure returns (VisibilityConfig memory);
231227
}
232228
233-
contract StorageExample is ContractTransparencyConfig{
229+
contract StorageExample is ContractTransparencyConfig {
234230
mapping(address => uint256) private _storedValues;
235231
uint256 private totalCalls = 0;
236232
@@ -254,14 +250,14 @@ contract StorageExample is ContractTransparencyConfig{
254250
function visibilityRules() external pure override returns (VisibilityConfig memory) {
255251
EventLogConfig[] memory eventLogConfigs = new EventLogConfig[](2);
256252
257-
// the signagure of "event DataChanged(address indexed account, uint256 newValue);"
253+
// the signature of "event DataChanged(address indexed account, uint256 newValue);"
258254
bytes32 dataChangedEventSig = hex"0xec851d5c322f7f1dd5581f7432e9f6683a8709a4b1ca754ccb164742b82a7d2f";
259255
Field[] memory relevantTo = new Field[](2);
260256
relevantTo[0] = Field.TOPIC1;
261257
relevantTo[1] = Field.SENDER;
262258
eventLogConfigs[0] = EventLogConfig(dataChangedEventSig, relevantTo);
263259
264-
// the signagure of "event MilestoneReached(uint256 noStoredValues);"
260+
// the signature of "event MilestoneReached(uint256 noStoredValues);"
265261
bytes32 milestoneReachedEventSig = hex"0xd41033274424d56dd572e7196fb4230cf4141d546b91fc00555cab8403965924";
266262
Field[] memory relevantTo = new Field[](1);
267263
relevantTo[0] = Field.EVERYONE;
@@ -272,11 +268,16 @@ contract StorageExample is ContractTransparencyConfig{
272268
}
273269
```
274270

275-
### Explanation:
271+
### Explanation
272+
273+
The `ContractTransparencyConfig` interface is known by the TEN platform.
274+
When a contract is deployed, the platform will call the `visibilityRules` function, and store the `VisibilityConfig`.
276275

277-
By implementing the `ContractTransparencyConfig.visibilityRules` method you can configure the visibility concerns of the current contract.
276+
For each event type, you can configure which fields can access it.
277+
This allows the developer to configure an event to be public even if it has EOAs or to allow the sender of the transaction to access events emitted even if the address is not in the event.
278278

279-
A `ContractCfg.PUBLIC` contract behaves exactly like a contract deployed on Ethereum. The storage slots are exposed, and all contracts are public.
279+
Notice how in the `visibilityRules` above, we configure the `DataChanged` event to be visible to the first field and the sender, and the `MilestoneReached` to be visible to everyone.
280280

281-
For private contracts, you can configure the visibility of each individual event type you're emitting by specifying the "fields" that can receive it.
282-
`Field.EVERYONE` means that this is a public event.
281+
The other configuration: `VisibilityConfig.contractCfg` applies to the entire contract:
282+
- `ContractCfg.TRANSPARENT`: The contracts will have public storage and events, behaving exactly like Ethereum.
283+
- `ContractCfg.PRIVATE`: The default TEN behaviour, where the storage is not accessible and the events are individually configurable.

0 commit comments

Comments
 (0)