Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TeddySwap Integration #69

Merged
merged 14 commits into from
Dec 20, 2023
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

All notable changes to Dexter will be documented in this file.

## [UNRELEASED]
- TeddySwap integration

## [v4.2.0]
- Fix WR price impact formula for 0 decimals
- Rename Asset identifier function
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<img src="https://raw.githubusercontent.com/IndigoProtocol/dexter/master/src/dex/logo/muesliswap.png" width="30" />
<img src="https://raw.githubusercontent.com/IndigoProtocol/dexter/master/src/dex/logo/wingriders.png" width="30" />
<img src="https://raw.githubusercontent.com/IndigoProtocol/dexter/master/src/dex/logo/vyfinance.png" width="30" />
<img src="https://raw.githubusercontent.com/IndigoProtocol/dexter/master/src/dex/logo/teddyswap.png" width="30" />
</div>

### What You Can Do
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@
"axios-retry": "^3.5.1",
"bottleneck": "^2.19.5",
"crypto-js": "^4.1.1",
"lodash": "^4.17.21",
"lucid-cardano": "^0.8.7"
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/preset-env": "^7.21.4",
"@babel/preset-typescript": "^7.21.4",
"@types/jest": "^29.5.0",
"@types/lodash": "^4.14.202",
"babel-jest": "^29.5.0",
"jest": "^29.5.0",
"prettier": "^2.8.8",
Expand Down
6 changes: 5 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export enum DatumParameterKey {
*/
PoolIdentifier = 'PoolIdentifier',
TotalLpTokens = 'TotalLpTokens',
LpTokenPolicyId = 'LpTokenPolicyId',
LpTokenAssetName = 'LpTokenAssetName',
LpFee = 'LpFee',
LpFeeNumerator = 'LpFeeNumerator',
LpFeeDenominator = 'LpFeeDenominator',
Expand All @@ -54,6 +56,8 @@ export enum DatumParameterKey {
RootKLast = 'RootKLast',
LastInteraction = 'LastInteraction',
RequestScriptHash = 'RequestScriptHash',
StakeAdminPolicy = 'StakeAdminPolicy',
LqBound = 'LqBound',
}

export enum TransactionStatus {
Expand All @@ -68,4 +72,4 @@ export enum AddressType {
Contract,
Base,
Enterprise,
}
}
25 changes: 24 additions & 1 deletion src/definition-builder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DatumParameters, DefinitionConstr, DefinitionField } from './types';
import { datumJsonToCbor } from 'lucid-cardano';
import { DatumParameterKey } from './constants';
import _ from 'lodash';

export class DefinitionBuilder {

Expand All @@ -10,7 +11,11 @@ export class DefinitionBuilder {
* Load a DEX definition file as a template for this builder.
*/
public async loadDefinition(definition: DefinitionConstr): Promise<DefinitionBuilder> {
this._definition = structuredClone(definition);
this._definition = _.cloneDeepWith(definition, (value: any) => {
if (value instanceof Function) {
return value;
}
})

return this;
}
Expand Down Expand Up @@ -54,6 +59,10 @@ export class DefinitionBuilder {
* Recursively set specified parameters.
*/
private applyParameters(field: DefinitionField, mappedParameters: DatumParameters): DefinitionConstr {
if (field instanceof Function) {
return field(field, mappedParameters, false);
}

if ('fields' in field) {
if (typeof field.constructor === 'string') {
const parameterValue: any = mappedParameters[field.constructor as keyof typeof DatumParameterKey];
Expand Down Expand Up @@ -100,6 +109,20 @@ export class DefinitionBuilder {
* Recursively pull parameters from datum using definition template.
*/
private extractParameters(definedDefinition: DefinitionField, templateDefinition: DefinitionField, foundParameters: DatumParameters = {}): DatumParameters {
if (templateDefinition instanceof Function) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds support for datum mappings to include dynamic pulling/pushing of parameters. One example would be an order datum not including a stake key, which will result in a different datum. Not all DEXs support this atm, but will push a fix after this PR is in

templateDefinition(definedDefinition, foundParameters);

return foundParameters;
}

if (templateDefinition instanceof Array) {
templateDefinition.map((fieldParameter: DefinitionField, index: number) => {
return this.extractParameters(fieldParameter, templateDefinition[index], foundParameters);
}).forEach((parameters: DatumParameters) => {
foundParameters = {...foundParameters, ...parameters};
})
}

if ('fields' in definedDefinition) {
if (! ('fields' in templateDefinition)) {
throw new Error("Template definition does not match with 'fields'");
Expand Down
61 changes: 61 additions & 0 deletions src/dex/api/teddyswap-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { BaseApi } from './base-api';
import { Asset, Token } from '../models/asset';
import { LiquidityPool } from '../models/liquidity-pool';
import axios, { AxiosInstance } from 'axios';
import { RequestConfig } from '@app/types';
import { appendSlash, tokensMatch } from '@app/utils';
import { TeddySwap } from '@dex/teddyswap';

export class TeddyswapApi extends BaseApi {

protected readonly api: AxiosInstance;
protected readonly dex: TeddySwap;

constructor(dex: TeddySwap, requestConfig: RequestConfig) {
super();

this.dex = dex;
this.api = axios.create({
timeout: requestConfig.timeout,
baseURL: `${appendSlash(requestConfig.proxyUrl)}https://analytics.teddyswap.org/v1`,
headers: {
'Content-Type': 'application/json',
}
});
}

liquidityPools(assetA: Token, assetB?: Token): Promise<LiquidityPool[]> {
return this.api.get('/front/pools', ).then((response: any) => {
return response.data.map((poolResponse: any) => {
const tokenA: Token = poolResponse.lockedX.asset.currencySymbol !== ''
? new Asset(poolResponse.lockedX.asset.currencySymbol, Buffer.from(poolResponse.lockedX.asset.tokenName, 'utf8').toString('hex'))
: 'lovelace';
const tokenB: Token = poolResponse.lockedY.asset.currencySymbol !== ''
? new Asset(poolResponse.lockedY.asset.currencySymbol, Buffer.from(poolResponse.lockedY.asset.tokenName, 'utf8').toString('hex'))
: 'lovelace';

if (! tokensMatch(tokenA, assetA) || (assetB && ! tokensMatch(tokenB, assetB))) {
return undefined;
}

let liquidityPool: LiquidityPool = new LiquidityPool(
TeddySwap.identifier,
tokenA,
tokenB,
BigInt(poolResponse.lockedX.amount),
BigInt(poolResponse.lockedY.amount),
'', // Not supplied
this.dex.orderAddress,
this.dex.orderAddress,
);

liquidityPool.lpToken = new Asset(poolResponse.lockedLQ.asset.currencySymbol, Buffer.from(poolResponse.lockedLQ.asset.tokenName, 'utf8').toString('hex'));
liquidityPool.poolFeePercent = (1 - (poolResponse.poolFeeNum / poolResponse.poolFeeDenum)) * 10;
liquidityPool.identifier = liquidityPool.lpToken.identifier();

return liquidityPool;
}).filter((pool: LiquidityPool | undefined) => pool !== undefined) as LiquidityPool[];
});
}

}
85 changes: 85 additions & 0 deletions src/dex/definitions/teddyswap/order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { DatumParameterKey } from '@app/constants';
import { DatumParameters, DefinitionField } from '@app/types';

export default {
constructor: 0,
fields: [
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.SwapInTokenPolicyId
},
{
bytes: DatumParameterKey.SwapInTokenAssetName
}
],
},
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.SwapOutTokenPolicyId
},
{
bytes: DatumParameterKey.SwapOutTokenAssetName
}
],
},
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.TokenPolicyId // Pool NFT
},
{
bytes: DatumParameterKey.TokenAssetName
}
],
},
{
int: DatumParameterKey.LpFee
},
{
int: DatumParameterKey.LpFeeNumerator // Execution fee numerator
},
{
int: DatumParameterKey.LpFeeDenominator // Execution fee denominator
},
{
bytes: DatumParameterKey.SenderPubKeyHash
},
(field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => {
if (! shouldExtract) {
const stakeKeyHash: string = parameters[DatumParameterKey.SenderStakingKeyHash] as string ?? null;

if (! stakeKeyHash) return;

return {
constructor: 0,
fields: [
{
bytes: stakeKeyHash,
}
],
};
}

if ('fields' in field) {
if (field.constructor === 1) return;

if (field.fields.length > 0 && 'bytes' in field.fields[0]) {
parameters[DatumParameterKey.SenderStakingKeyHash] = field.fields[0].bytes;
}
}

return;
},
{
int: DatumParameterKey.SwapInAmount
},
{
int: DatumParameterKey.MinReceive
}
],
}
62 changes: 62 additions & 0 deletions src/dex/definitions/teddyswap/pool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { DatumParameterKey } from '@app/constants';

export default {
constructor: 0,
fields: [
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.TokenPolicyId // Pool NFT
},
{
bytes: DatumParameterKey.TokenAssetName
}
]
},
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.PoolAssetAPolicyId
},
{
bytes: DatumParameterKey.PoolAssetAAssetName
}
]
},
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.PoolAssetBPolicyId
},
{
bytes: DatumParameterKey.PoolAssetBAssetName
}
]
},
{
constructor: 0,
fields: [
{
bytes: DatumParameterKey.LpTokenPolicyId
},
{
bytes: DatumParameterKey.LpTokenAssetName
}
]
},
{
int: DatumParameterKey.LpFee
},
[
{
bytes: DatumParameterKey.StakeAdminPolicy
}
],
{
int: DatumParameterKey.LqBound
}
]
}
Binary file added src/dex/logo/teddyswap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/dex/minswap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ export class Minswap extends BaseDex {
},
],
datum: datumBuilder.getCbor(),
isInlineDatum: false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice approach!

spendUtxos: spendUtxos,
}
)
Expand All @@ -230,6 +231,7 @@ export class Minswap extends BaseDex {
address: returnAddress,
addressType: AddressType.Base,
assetBalances: relevantUtxo.assetBalances,
isInlineDatum: false,
spendUtxos: [relevantUtxo],
}
];
Expand Down
2 changes: 2 additions & 0 deletions src/dex/muesliswap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export class MuesliSwap extends BaseDex {
},
],
datum: datumBuilder.getCbor(),
isInlineDatum: false,
spendUtxos: spendUtxos,
}
)
Expand All @@ -222,6 +223,7 @@ export class MuesliSwap extends BaseDex {
address: returnAddress,
addressType: AddressType.Base,
assetBalances: relevantUtxo.assetBalances,
isInlineDatum: false,
spendUtxos: [relevantUtxo],
}
];
Expand Down
Loading