Skip to content

Commit

Permalink
Merge pull request #34 from securesecrets/add-router
Browse files Browse the repository at this point in the history
Add router
  • Loading branch information
AustinWoetzel authored Nov 14, 2023
2 parents f60f9bc + 7820884 commit 3b02c3b
Show file tree
Hide file tree
Showing 25 changed files with 2,506 additions and 214 deletions.
103 changes: 101 additions & 2 deletions docs/calculations/routing.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,102 @@
# ShadeSwap multi-hop routing
# ShadeSwap Multi-Hop Routing

This page contains an example router that determines the optimal path for maximizing the users output tokens received

## Router

### Determine Possible Paths
Returns possible paths through one or multiple pools to complete a trade of two tokens.
```ts
function getPossiblePaths({
inputTokenContractAddress,
outputTokenContractAddress,
maxHops,
pairs,
}:{
inputTokenContractAddress:string,
outputTokenContractAddress:string
maxHops: number // max number of pairs used
pairs: BatchPairsInfo
}): string[][]
```
See [Batch Pairs Query](../queries/swap.html#pairs-info) for input type BatchPairsInfo.

::: warning
Increasing the max hops too much can cause decreased performance due to the large amount of computations required to calculate all routes. The current recommendation is 4 max based on the route paths available, but this is subject to change as more pairs are supported over time.
:::

### Calculate Route
Calculates the output amount received for a given path

```ts
function calculateRoute({
inputTokenAmount,
inputTokenContractAddress,
path,
pairs,
tokens,
}:{
inputTokenAmount: BigNumber,
inputTokenContractAddress: string,
path: string[], // path determined by getPossiblePaths
pairs: BatchPairsInfo,
tokens: TokensConfig, // list of all possible swap tokens
}): Route
```

::: tip
We pass in a list of all possible tokens (TokensConfig) so that we have access to their decimals for uDenom conversions. This is not the most elegant solution as it may be preferrable to reference your own data store for token data. In that case, you can create your own route calculator and use the ShadeJS one as a guide.
:::
```ts
type TokenConfig = {
tokenContractAddress: string,
decimals: number,
}
type TokensConfig = TokenConfig[]
```
**output**

```ts
type Route = {
inputAmount: BigNumber,
quoteOutputAmount: BigNumber,
quoteShadeDaoFee: BigNumber,
quoteLPFee: BigNumber,
priceImpact: BigNumber,
inputTokenContractAddress: string,
outputTokenContractAddress: string,
path: string[],
gasMultiplier: number, // multiplier which is to be applied
// to the base gas cost of a swap
};
```


### Calculate All Possible Routes
Retrieves all potential route options and the outputs of each route.
Returns an array of routes in the order that will give the highest quoted
output amount. This function utilizes both getPossiblePaths and calculateRoute.

```ts
function getRoutes({
inputTokenAmount,
inputTokenContractAddress,
outputTokenContractAddress,
maxHops,
pairs,
tokens,
}: {
inputTokenAmount: BigNumber,
inputTokenContractAddress: string,
outputTokenContractAddress: string,
maxHops: number,
pairs: BatchPairsInfo,
tokens: TokensConfig,
}): Route[]
```

::: warning
This function optimizes for maximizing the output token amount only. It does NOT factor in the value of the gas for the extra hops (i.e. gas multiplier) relative to the value of tokens received. This will especially impact small trades where there is perceived arbitrage opportunities through certain paths, without factoring in the gas cost to perform the arbitrage. It is recommended that you create your own route calculator to handle token values and use the one provided here as a guide.
:::

Coming Soon...
7 changes: 5 additions & 2 deletions docs/calculations/swap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This page demonstrates how to calculate outputs of a single pair swap on ShadeSwap

## BigNumber.js
<a href="https://github.com/MikeMcl/bignumber.js" target="_blank">bignumber.js</a> is used on all computations to prevent loss of precision that normally happens with standard javascript math

## Constant Product (CPMM)

### Forward Swap
Expand Down Expand Up @@ -47,7 +50,7 @@ function constantProductSwapToken1for0({
```

### Reverse Swap
Backwards calcuation for determining a token input amount for a given output amount.
Backwards calculation for determining a token input amount for a given output amount.
```ts
/**
* returns input of a simulated swap from token0 to token1 using the constant
Expand Down Expand Up @@ -162,7 +165,7 @@ function stableSwapToken1for0({
```

### Reverse Swap
Backwards calcuation for determining a token input amount for a given output amount.
Backwards calculation for determining a token input amount for a given output amount.
```ts
/**
* returns input of a simulated swap of token0 for
Expand Down
4 changes: 2 additions & 2 deletions docs/queries/batch-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

This page demonstrates how to query using a smart contract batch query router. The purposes of a query router is to combine multiple queries to one or more contracts into a single http request. This reduces load on the node infrastructure and is faster for retrieving data than would otherwise be obtained by individual contract queries.

The batch query function is generalized to work with any contract queries. Miscellaneous ShadeJS services already implement the batch query router, for example the <a href="/swap.html#pairs-info" target="_blank">Pairs Info Query</a>
The batch query function is generalized to work with any contract queries. Miscellaneous ShadeJS services already implement the batch query router, for example the <a href="./swap.html#pairs-info" target="_blank">Pairs Info Query</a>


## Performance
All of the following results are run with 500 total queries

| Batch Size | RPC Queries | Time | Success |
|------------|-------------|-------|---------|
| N/A | 500 | 5.56s | 26.6% |
| 1 | 500 | 5.56s | 26.6% |
| 5 | 100 | 6.51s | 100% |
| 10 | 50 | 9.24s | 100% |
| 25 | 20 | 7.27s | 100% |
Expand Down
34 changes: 10 additions & 24 deletions docs/queries/swap.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,18 +327,14 @@ type PairInfo = {
isStable: boolean,
token0Amount: string,
token1Amount: string,
priceRatio: string | null,
pairSettings: {
lpFee: number,
daoFee: number,
stableLpFee: number,
stableDaoFee: number,
stableParams: StableParams | null
},
lpFee: number,
daoFee: number,
stableParams: StableParams | null
contractVersion: number,
}
type StableParams = {
priceRatio: string,
alpha: string,
gamma1: string,
gamma2: string,
Expand Down Expand Up @@ -413,14 +409,9 @@ console.log(output)
"token0Amount": "4268251730565",
"token1Amount": "365239579269",
"lpTokenAmount": "487393298891",
"priceRatio": null,
"pairSettings": {
"lpFee": 0.0029,
"daoFee": 0.0001,
"stableLpFee": 0.000483,
"stableDaoFee": 0.000017,
"stableParams": null
},
"lpFee": 0.0029,
"daoFee": 0.0001,
"stableParams": null
"contractVersion": 1
}
},
Expand Down Expand Up @@ -448,14 +439,9 @@ console.log(output)
"token0Amount": "95199329571",
"token1Amount": "377657768",
"lpTokenAmount": "5708789507",
"priceRatio": null,
"pairSettings": {
"lpFee": 0.0029,
"daoFee": 0.0001,
"stableLpFee": 0.000483,
"stableDaoFee": 0.000017,
"stableParams": null
},
"lpFee": 0.0029,
"daoFee": 0.0001,
"stableParams": null
"contractVersion": 1
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// default endpoint to be used when alternative endpoint is not provided
// this is a public endpoint and not guarantees can be provided about the performance
// this is a public endpoint and no guarantees can be provided about the performance
const DEFAULT_SECRET_LCD_ENDPOINT = 'https://lcd.secret.express/';
const SECRET_MAINNET_CHAIN_ID = 'secret-4';

Expand Down
4 changes: 4 additions & 0 deletions src/contracts/definitions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './batchQuery';
export * from './oracle';
export * from './snip20';
export * from './swap';
4 changes: 3 additions & 1 deletion src/contracts/definitions/snip20.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,15 @@ test('it checks the shape of the snip20 increase allowance', () => {

test('it checks the shape of the snip20 viewing', () => {
const viewingKey = 'MOCK_VIEWING_KEY';
const padding = 'PADDING';

const output = {
msg: {
set_viewing_key: {
key: viewingKey,
padding,
},
},
};
expect(snip20.messages.createViewingKey(viewingKey)).toStrictEqual(output);
expect(snip20.messages.createViewingKey(viewingKey, padding)).toStrictEqual(output);
});
11 changes: 6 additions & 5 deletions src/contracts/definitions/snip20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const snip20 = {
recipientCodeHash?: string,
amount: string,
handleMsg: any,
padding: string,
padding?: string,
}): Snip20MessageRequest {
const msg = {
send: {
Expand All @@ -52,7 +52,7 @@ const snip20 = {
}: {
recipient: string,
amount: string,
padding: string,
padding?: string,
}): Snip20MessageRequest {
const msg = {
transfer: {
Expand Down Expand Up @@ -85,7 +85,7 @@ const snip20 = {
}:{
amount: string,
denom: string,
padding: string,
padding?: string,
}): Snip20MessageRequest {
const msg = {
redeem: {
Expand All @@ -108,7 +108,7 @@ const snip20 = {
spender: string,
amount: string,
expiration?: number,
padding: string,
padding?: string,
}): Snip20MessageRequest {
const msg = {
increase_allowance: {
Expand All @@ -123,10 +123,11 @@ const snip20 = {
};
},

createViewingKey(viewingKey: string): Snip20MessageRequest {
createViewingKey(viewingKey: string, padding?: string): Snip20MessageRequest {
const msg = {
set_viewing_key: {
key: viewingKey,
padding,
},
};
return {
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/definitions/swap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ vi.mock('~/contracts/definitions/snip20', () => ({
}));

vi.mock('~/lib/utils', () => ({
randomPadding: vi.fn(() => 'RANDOM_PADDING'),
generatePadding: vi.fn(() => 'RANDOM_PADDING'),
}));

test('it tests the form of the query factory config msg', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/contracts/definitions/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
Path,
Paths,
} from '~/types/contracts/swap/model';
import { randomPadding } from '~/lib/utils';
import { generatePadding } from '~/lib/utils';
import { snip20 } from './snip20';

/**
Expand Down Expand Up @@ -75,7 +75,7 @@ function msgSwap({
recipientCodeHash: routerCodeHash,
amount: sendAmount,
handleMsg: swapParamsMessage,
padding: randomPadding(),
padding: generatePadding(),
}).msg;
}

Expand Down
2 changes: 2 additions & 0 deletions src/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './definitions';
export * from './services';
4 changes: 4 additions & 0 deletions src/contracts/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './batchQuery';
export * from './oracle';
export * from './snip20';
export * from './swap';
62 changes: 30 additions & 32 deletions src/contracts/services/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,39 +187,37 @@ function parsePairInfo(
token0Amount: pairInfo.amount_0,
token1Amount: pairInfo.amount_1,
lpTokenAmount: pairInfo.total_liquidity,
priceRatio: stableInfo ? stableInfo.p : null,
pairSettings: {
lpFee: fees.lp_fee.nom / fees.lp_fee.denom,
daoFee: fees.shade_dao_fee.nom / fees.shade_dao_fee.denom,
stableLpFee: fees.stable_lp_fee.nom / fees.stable_lp_fee.denom,
stableDaoFee: fees.stable_shade_dao_fee.nom / fees.stable_shade_dao_fee.denom,

stableParams: stableInfo ? {
alpha: stableInfo.stable_params.a,
gamma1: stableInfo.stable_params.gamma1,
gamma2: stableInfo.stable_params.gamma2,
oracle: {
address: stableInfo.stable_params.oracle.address,
codeHash: stableInfo.stable_params.oracle.code_hash,
},
token0Data: {
oracleKey: stableInfo.stable_token0_data.oracle_key,
decimals: stableInfo.stable_token0_data.decimals,
},
token1Data: {
oracleKey: stableInfo.stable_token1_data.oracle_key,
decimals: stableInfo.stable_token1_data.decimals,
},
minTradeSizeXForY: stableInfo.stable_params.min_trade_size_x_for_y,
minTradeSizeYForX: stableInfo.stable_params.min_trade_size_y_for_x,
maxPriceImpactAllowed: stableInfo.stable_params.max_price_impact_allowed,
customIterationControls: stableInfo.stable_params.custom_iteration_controls ? {
epsilon: stableInfo.stable_params.custom_iteration_controls.epsilon,
maxIteratorNewton: stableInfo.stable_params.custom_iteration_controls.max_iter_newton,
maxIteratorBisect: stableInfo.stable_params.custom_iteration_controls.max_iter_bisect,
} : null,
lpFee: pairInfo.pair[2] ? fees.stable_lp_fee.nom / fees.stable_lp_fee.denom
: fees.lp_fee.nom / fees.lp_fee.denom,
daoFee: pairInfo.pair[2] ? fees.stable_shade_dao_fee.nom / fees.stable_shade_dao_fee.denom
: fees.shade_dao_fee.nom / fees.shade_dao_fee.denom,
stableParams: stableInfo ? {
priceRatio: stableInfo.p!, // if stable params exist, we know price ratio will be available
alpha: stableInfo.stable_params.a,
gamma1: stableInfo.stable_params.gamma1,
gamma2: stableInfo.stable_params.gamma2,
oracle: {
address: stableInfo.stable_params.oracle.address,
codeHash: stableInfo.stable_params.oracle.code_hash,
},
token0Data: {
oracleKey: stableInfo.stable_token0_data.oracle_key,
decimals: stableInfo.stable_token0_data.decimals,
},
token1Data: {
oracleKey: stableInfo.stable_token1_data.oracle_key,
decimals: stableInfo.stable_token1_data.decimals,
},
minTradeSizeXForY: stableInfo.stable_params.min_trade_size_x_for_y,
minTradeSizeYForX: stableInfo.stable_params.min_trade_size_y_for_x,
maxPriceImpactAllowed: stableInfo.stable_params.max_price_impact_allowed,
customIterationControls: stableInfo.stable_params.custom_iteration_controls ? {
epsilon: stableInfo.stable_params.custom_iteration_controls.epsilon,
maxIteratorNewton: stableInfo.stable_params.custom_iteration_controls.max_iter_newton,
maxIteratorBisect: stableInfo.stable_params.custom_iteration_controls.max_iter_bisect,
} : null,
},
} : null,

contractVersion: pairInfo.contract_version,
};
}
Expand Down
Loading

0 comments on commit 3b02c3b

Please sign in to comment.