Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
Guides
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterMPhillips committed Dec 10, 2021
1 parent edea00c commit 7df2f4c
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 53 deletions.
145 changes: 145 additions & 0 deletions docs/guides/create-strategy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
id: create-strategy
title: Create a Strategy
custom_edit_url: https://github.com/EnsoFinance/enso-docs/blob/main/docs/guides/create-strategy.mdx
---

##### Create an Enso strategy and open it up for investment from other Enso users

---

## Getting Started

To create a strategy, a manager must first decide on a number of parameters to initialize their strategy. Every strategy is also an ERC-20, so the manager has to decide on the `name` (e.g. Awesome Successful Strategy) and `symbol` (e.g. ASS). Additionally, the manager will have to determine the intitial state of the strategy. There are a number of values that are used by the strategy to restrict the behaviour of the manager so that users know exactly what risks or costs are associated with investing in the strategy. The `InitialState` object is defined like so:

```typescript
type InitialState = {
timelock: BigNumber
rebalanceThreshold: BigNumber
rebalanceSlippage: BigNumber
restructureSlippage: BigNumber
performanceFee: BigNumber
social: boolean
set: boolean
}
```
The following table gives a description of each value:
| Parameter | Description | Possible values |
| ----------|-------------|----------------:|
| timelock | The amount of time (in seconds) that must pass between a manager initiating a state change (such as changing any of the following values or restructuring the strategy) and finalizing it in the strategy. This gives investors time to exit a strategy in case of objectionable changes. The `timelock` value is ignored in private strategies. | 0+ |
| rebalanceThreshold | The percentage (`rebalanceThreshold/1000`) that a token needs to be out-of-balance from it's intended balance before a manager is able to call `rebalance` on the contract. A low rebalance threshold means that a strategy can be rebalanced when there is very little change in the strategy's token distribution. However, frequent unnecessary rebalances could cause considerable value loss due to slippage. | 0-1000 |
| rebalanceSlippage | The percentage (`rebalanceSlippage/1000`) that the strategy value may slip down to due to a `rebalance` call. The lower the slippage value, the higher risk of value loss to the strategy. However, some slippage is inevitable (such as due to DEX fees and disparity between the oracle price and market spot price) and consequently there always needs to be some room for legitimate slippage | 0-1000 |
| restructureSlippage | Same as `rebalanceSlippage` except it's the slippage value that is checked during a `restructure` call which is expected to have more slippage than a `rebalance` call since restructuring will sometimes involve liquidating all the tokens in a strategy. Consequently, one would expect `restructureSlippage` to be lower than `rebalanceSlippage`. | 0-1000 |
| performanceFee | The fee that is distributed to the manager and Enso stakers based on the increase in the value of the strategy tokens. The earnings are split 70% / 30% in favour of the manager. The fee is received via the inflation of strategy tokens and so issuance of the fee causes a small drop in token value. Performance fees can only be charged on social strategies. | 0-1000 |
| social | A boolean that allows other users to deposit into the strategy. While private strategies don't allow depositing by anyone other than a manager, they always allow token holders to withdraw. So a manger could still mint tokens in a private strategy and then sell them on a secondary market such as Uniswap or Sushi. This value can be changed from `false` to `true` at a later time by calling `openStrategy`, but it cannot be changed back. | `true` or `false` |
| set | A boolean that restricts restructuring of a strategy. If set to true, a manager will be unable to call `restructure` and so they won't be able to change to tokens or the relative token balance of the strategy. This value can be changed from `false` to `true` at a later time by calling `setStrategy`, but it cannot be changed back. | `true` or `false` |
Most importantly, the manager needs to define the composition of the strategy, i.e. what tokens are in the strategy, what percentage of the strategy value should be held in each token, and the trading paths necessary to get into the token position. This is done by passing an array of `StrategyItem` objects:
```typescript
type StrategyItem = {
item: string
percentage: BigNumber
data: TradeData
}

type TradeData = {
adapters: string[]
path: string[]
cache: string
}
```
The following table gives a description of each value:
| Parameter | Description | Possible values |
| ----------|-------------|----------------:|
| item | The address of the ERC-20 token that will be held by the strategy | Ethereum address |
| percentage | The percentage (`percentage/1000`) of the strategy's total value that this token will comprise. | 0-1000 |
| adapters | An array of approved adapter addresses. Each convsersion from one token (e.g. WETH) into another (e.g. DAI) requires a an adapter (e.g. UniswapV3Adapter). Multiple adapters are used for multi-hop trades. | Ethereum address array |
| path | An array of token addresses that represent intermediary steps on a multi-hop trade. If the trade is simply going from WETH to another token on an exchange, it likely doesn't need multiple hops and therfore this array can be empty. | Ethereum address array |
| cache | A flexible bytes value for advanced use cases such as leveraged token positions | Hex string |
There are a few rules in order to successfully define the StrategyItem array:
1. The percentages of all the StrategyItems must add up to 1000, any more or less and call will fail.
2. Address `0x00...00` and `0xFF...FF` are reserved and cannot be passed in the `StrategyItem.item` parameter.
3. In order to cheaply check that there are no duplicate tokens in the contract, we require that the StrategyItems are ordered by address from smallest to largest.
Finally, once a strategy is deployed and initialized, any funds sent during the `createStrategy` call will need to be converted into the strategy's underlying token positions. So the manager will need to pass the address of the `Router` that will handle all the trading logic. If the router used is the `GenericRouter`, the manager will also need to pass the multicall `bytes` data that will handle all the trading logic.
You can see it all come together in the following code:
```typescript
// Define ERC-20 metadata
const name = 'Awesome Successful Strategy'
const symbol = 'ASS' // Too cheeky?

// Define initial state
const timelock = BigNumber.from('604800') // 1 Week
const rebalanceThreshold = 50 // 5%
const rebalanceSlippage = 995 // 99.5%
const restructureSlippage = 990 // 99%
const performanceFee = 50 // 5%
const social = true
const set = false

const initialState = {
timelock,
rebalanceThreshold,
rebalanceSlippage,
restructureSlippage,
performanceFee,
social,
set
}

// Define strategy composition
const daiItem = {
item: dai.address,
percentage: 50,
data: {
adapters: [uniswapV3Adapter.address],
path: [],
cache: '0x'
}
}
const wbtcItem = {
item: wbtc.address,
percentage: 50,
data: {
adapters: [uniswapV3Adapter.address],
path: [],
cache: '0x'
}
}
const strategyItems = [daiItem, wbtcItem].sort((a, b) => {
// Convert addresses to number
const aNum = BigNumber.from(a.item)
const bNum = BigNumber.from(b.item)
return aNum.gt(bNum) ? 1 : -1 // Sort strategy items
})

// Create strategy
const strategyFactoryContract = new ethers.Contract(
strategyFactoryAddress,
StrategyProxyFactory.abi,
signer
)
const tx = await strategyFactoryContract.createStrategy(
managerAddress,
name,
symbol,
initialState,
strategyItems,
loopRouterAddress, // A basic router with on-chain trading logic
'0x' // No data needs to be sent to router
)
const receipt = await tx.wait()

// Get strategy address from events
const strategyAddress = receipt.events.find(
(ev: Event) => ev.event === 'NewStrategy'
).args.strategy
```

Congratulations, you've successfully created an Enso strategy!
78 changes: 78 additions & 0 deletions docs/guides/manage-strategy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
id: manage-strategy
title: Manage a Strategy
custom_edit_url: https://github.com/EnsoFinance/enso-docs/blob/main/docs/guides/manage-strategy.mdx
---

##### Manage your Enso strategy
---

The main responsibility of the manager is keep their strategy balanced as the prices of the underlying tokens changes. One of the primary differences between Enso and AMM's such as Uniswap or Balancer is that it avoids impermanent loss by manually rebalancing at the discretion of the manager. A skilled manager will choose the right times to rebalance, and the right time to *refrain* from rebalancing, in order to maximize their gains.

### Rebalance
Calling rebalance is a relatively simple process. The manager just needs to define what router they would like to use to handle the trading logic. For simple use-cases, they can pass the `LoopRouter` address and no `bytes` data and all the trading logic will be handled on-chain. For advanced users, we provide the `GenericRouter` which allows the manager to define each trade off-chain and then pass a `Call[]` array that has been encoded into `bytes` to the `data` parameter of the `rebalance` function. While use of the `GenericRouter` is often cheaper, it comes with a higher risk of failed transactions due to encoding errors or stale price data.

```typescript
const controllerContract = new ethers.Contract(
controllerAddress,
StrategyController.abi,
signer
)
await controllerContract.rebalance(
strategyAddress,
loopRouterAddress,
'0x'
)
```

### Restructure
A restructure is a restricted action. What this means is that there is a delay between initiating a restructure and finalizing the new structure in the strategy. This delay gives investors a chance to exit the strategy in case they don't like the proposed changes. The first step is to propose a new `StrategyItem[]` array:

```typescript
await controllerContract.restructure(
strategyAddress,
strategyItems
)
```

All data will get cached in the contract and the timelock will be initiated. The manager must wait for `timelock` seconds to pass before they may finalize the structure. During this time investors may exit if the don't like the new structure.

Since the StrategyItems are already cached, the manager just needs to send the router address and any necessary data to the `finalizeStructure` function.

```typescript
await controllerContract.finalizeStructure(
strategyAddress,
loopRouterAddress,
'0x'
)
```

This function will set the new structure in the Strategy contract and then make the necessary trades to reposition the strategy into the new composition.

### Update State
Similar to `restructure`, the `updateValue` function is also restricted by the timelock. This allows a manager to initiate the change of one of the strategy's state variables.

```typescript
enum TIMELOCK_CATEGORY {
RESTRUCTURE,
REBALANCE_THRESHOLD,
REBALANCE_SLIPPAGE,
RESTRUCTURE_SLIPPAGE,
TIMELOCK,
PERFORMANCE
}

await controllerContract.updateValue(
strategyAddress,
TIMELOCK_CATEGORY.REBALANCE_THRESHOLD, // 1
100, // 10%
)
```

The function will fail if the manager passes `0` for the timelock category. This value represent a restructure, which is updated using the `restructure` function.

After the timelock has passed, the manager may call `finalizeValue` to finalize it.

```typescript
await controllerContract.finalizeValue(strategyAddress)
```
92 changes: 66 additions & 26 deletions docs/guides/vampire-attack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,76 @@ custom_edit_url: https://github.com/EnsoFinance/enso-docs/blob/main/docs/guides/
First introduced by [SushiSwap](https://sushi.com/) to liquidity providers of [UniSwap](https://uniswap.org/) to migrate their liquidity over to Sushi through token and community incentivization.


:::note
#### Who is Enso vampire attacking?
| Platform | TVL | Adapter Address |
| ---------|------|----------------:|
| [Tokensets](https://tokensets.com/) | $466.59m* | 0x5F2C716D912Ce400a8A49fB87DB722e8257257A7 |
| [IndexCoop](https://www.indexcoop.com/) | $417.2m* | 0x9c9326C521895c78414BD3C2945e47AFC4Ef16cc |
| [Indexed](https://indexed.finance/) | $11.5m* | 0x8F516c0FB0bcF44CFc8D42D60dAD3E4cdb35Ce26 |
| [PieDAO](https://www.piedao.org/) | $13.72m* | 0xe149B1f2Ef564A289dc094215835f3c8Df1695aa |
| [dHedge](https://www.dhedge.org/) | $21.17m* | 0xaE6859311c341bAc4E0BcEcA0242247c16718FF1 |
| [PowerPool](https://powerpool.finance/) | $18.05m* | 0x0a883a62660328EAd442EB6c2311668ba9c12e57 |

rP: Receiving platform(e.g. Enso, Sushiswap)
sP: Sending platform(E.g. Tokensets, UniSwap)
U : User that provides liquidity
LPt : Liquidity Pool Token

:::
// TODO: full list of pools available

**Steps for migration**
> 1. U stakes sP LPt on rP
2. rP publicly notifies of migration date
3. rP migrates LPt from sP
1. rP uses approval of U LPt
2. burns U LPt from sP
3. obtains underlying tokens from LPt
4. creates 1:1 replication of LPt on rP
5. deposits underlying LPt into rP liquidity pool
6. U gets LPt token from rP
4. rP activates retroactive distribution based upon TVL migrated
> 1. Enso deploys the liquditymigration contract, setting the migration date.
2. User stakes the liqudity pool token from the competing platform in the liquidity-migration contract.
3. Enso migrates the liquidity pool token from the competing platform:
1. An Enso strategy is created that replicates the LP of the competing platform.
2. The 'withdraw' function is called and the LP token is burned.
3. Enso receives the LP's underlying tokens.
4. Deposits underlying tokens into the equivalent Enso strategy.
5. User gets tokens representing their share in the Enso strategy.
4. Enso activates retroactive distribution of Enso tokens based upon TVL migrated

Following this method ensures that platforms continuously have to be at the cutting edge, and build products that their users want. As well as having token incentives aligned with community needs, and governance of the product.

#### Staking

#### Who is Enso vampire attacking?
| Protocol | TVL |
| ------------- |-----:|
| [Tokensets](https://tokensets.com/) | $466.59m* |
| [IndexCoop](https://www.indexcoop.com/) | $417.2m* |
| [Indexed](https://indexed.finance/) | $11.5m* |
| [PieDAO](https://www.piedao.org/) | $13.72m* |
| [dHedge](https://www.dhedge.org/) | $21.17m* |
| [PowerPool](https://powerpool.finance/) | $18.05m* |
The first step for a user to migrate their liquidity from a competing platform is to stake the liquidity pool tokens from the pool they want to migrate from. First they must approve the liquidity-migration contract to transfer their LP tokens:

```typescript
const poolContract = new ethers.Contract(
poolAddress,
ERC20.abi,
signer
)
await poolContract.approve(
liquidityMigrationAddress,
amount
)
```
Then the user calls the `stake` function on the liquidity migration contract:

```typescript
const liquidityMigrationContract = new ethers.Contract(
liquidityMigrationAddress,
LiquidityMigration.abi,
signer
)
await liquidityMigrationContract.stake(
poolAddress,
amount,
platformAdapterAddress
)
```

#### Migration

After the timelock period has passed users will be able to migrate their liquidity to a new Enso strategy. To do this, the user must call the `migrate` function in the liquidity migration contract:

```typescript
const liquidityMigrationContract = new ethers.Contract(
liquidityMigrationAddress,
LiquidityMigration.abi,
signer
)
await liquidityMigrationContract.migrate(
poolAddress,
platformAdapterAddress,
strategyAddress,
slippage
)
```
This will burn the other platform's LP tokens, mint Enso strategy tokens and send them to the user.
6 changes: 3 additions & 3 deletions docs/smart-contracts/token/token-contract.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ ENSO extends the base [ERC-20 standard](https://eips.ethereum.org/EIPS/eip-20) i

### Audited and open source

Contract source code can be found in [Github repository](https://github.com/EnsoFinance/ENSO-ETH-Token).
Contract source code can be found in [GitHub repository](https://github.com/EnsoFinance/ENSO-ETH-Token).
Chainsecurity audit can be found [here](https://github.com/EnsoFinance/ENSO-ETH-Token/blob/main/audits/ChainSecurity_EnsoLabs_Enso_Token_audit.pdf).

![have a look at the architecture](https://raw.githubusercontent.com/EnsoFinance/ENSO-ETH-Token/main/architecture/architecture.png?token=AR3VNP3FS7DKJ7QYEEFWGYLBS7EOS)
![Have a look at the architecture](https://raw.githubusercontent.com/EnsoFinance/ENSO-ETH-Token/main/architecture/architecture.png?token=AR3VNP3FS7DKJ7QYEEFWGYLBS7EOS)



Expand All @@ -40,4 +40,4 @@ Chainsecurity audit can be found [here](https://github.com/EnsoFinance/ENSO-ETH-
| **Contract** | **Network** | **Address** |
| ------------ | -------------- | ------------------------------------------ |
| ENSO(ERC20) | Eth Mainnet | 0x000 |
| ENSO(ERC20) | Rinkeby | 0x000 |
| ENSO(ERC20) | Rinkeby | 0x000 |
2 changes: 1 addition & 1 deletion docs/smart-contracts/token/token-vesting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Enso multisig will approve ERC20 tokens to the Vesting contract, and then `regis

### Audited and open source

Contract source code can be found in [Github repository](https://github.com/EnsoFinance/token-vesting).
Contract source code can be found in [GitHub repository](https://github.com/EnsoFinance/token-vesting).
Chainsecurity audit can be found [here](https://github.com/EnsoFinance/token-vesting/blob/main/audits/ChainSecurity_EnsoLabs_Enso_Vesting_audit.pdf).

![have a look at the architecture](https://raw.githubusercontent.com/EnsoFinance/token-vesting/main/architecture/architecture.png?token=AR3VNP2WDCUIFRYTZVU4QVDBS7FSO)
Expand Down
Loading

0 comments on commit 7df2f4c

Please sign in to comment.