Skip to content

Commit

Permalink
Tests/sdk features stage 2 (#11)
Browse files Browse the repository at this point in the history
* tests: enhance tests for existing functions

* tests: add test cases for existing functions

* tests: add test cases for three new functions

* tests: Introduce mock for getFeeRate function

* tests: implement workaround for Jest BigInt assertion issue

* tests: wip test cases getRoute, getAmountTo, runSwap

* tests: mocked functions runSwap, getLatestPrices

* temp commit

* tests: mocked functions getBalances, fetchSwappableCurrency

* tests: add additional tests for error scenarios

* tests: add more coverage to runSwap SDK method

* temp commit

* temp commit

* sync merge

* code linted + documentation update

* tests: SDK getWayPoints method

* tests: add lint fix command

* tests: add getFeeRate with custom route

* test: add external error tests

* fix: minor changes

* conflicts resolved

---------

Co-authored-by: [email protected] <[email protected]>
Co-authored-by: simsbluebox <[email protected]>
Co-authored-by: david weil <[email protected]>
  • Loading branch information
4 people authored Jul 15, 2024
1 parent 4c9ee30 commit fc23587
Show file tree
Hide file tree
Showing 9 changed files with 609 additions and 111 deletions.
111 changes: 19 additions & 92 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,101 +10,28 @@ You can install Alex-SDK using npm:
npm install alex-sdk
```

## Methods
## Functions

### `fetchSwappableCurrency()`
The AlexSDK class includes the following functions:

Fetches the list of currencies that can be swapped on the DEX.

- **Returns**: `Promise<TokenInfo[]>` - A promise that resolves to an array of TokenInfo objects, representing the swappable currencies.

### `getAllPossibleRoutes(from: Currency, to: Currency)`

Retrieves all possible routes for swapping from one currency to another.

- **Parameters**:

- `from: Currency` - The currency to swap from.
- `to: Currency` - The currency to swap to.

- **Returns**: `Promise<AMMRoute[]>` - A promise that resolves to an array of AMMRoute objects, representing all possible routes for the swap.

### `getRoute(from: Currency, to: Currency)`

Retrieves the best route for swapping from one currency to another.

- **Parameters**:

- `from: Currency` - The currency to swap from.
- `to: Currency` - The currency to swap to.

- **Returns**: `Promise<AMMRoute>` - A promise that resolves to an AMMRoute object, representing the best route for the swap.

### `getWayPoints(route: AMMRoute)`

Displays the detailed route information.

- **Parameters**:

- `route: AMMRoute` - The route to display.

- **Returns**: `Promise<TokenInfo[]>` - A promise that resolves to an array of TokenInfo objects, representing the detailed information of the route.

### `getFeeRate(from: Currency, to: Currency, customRoute?: AMMRoute)`

Calculates the fee rate for a swap between two currencies.

- **Parameters**:

- `from: Currency` - The currency to swap from.
- `to: Currency` - The currency to swap to.
- `customRoute?: AMMRoute` - An optional custom route for the swap.

- **Returns**: `Promise<bigint>` - A promise that resolves to a bigint representing the fee rate for the swap.

### `getAmountTo(from: Currency, fromAmount: bigint, to: Currency, customRoute?: AMMRoute)`

Calculates the amount of the destination currency that will be received for a given amount of the source currency.

- **Parameters**:

- `from: Currency` - The currency to swap from.
- `fromAmount: bigint` - The amount of the source currency to swap.
- `to: Currency` - The currency to swap to.
- `customRoute?: AMMRoute` - An optional custom route for the swap.

- **Returns**: `Promise<bigint>` - A promise that resolves to a bigint representing the amount of the destination currency that will be received.

### `runSwap(stxAddress: string, currencyX: Currency, currencyY: Currency, fromAmount: bigint, minDy: bigint, customRoute?: AMMRoute)`

Executes a swap transaction between two currencies.

- **Parameters**:

- `stxAddress: string` - The Stacks (STX) address to execute the swap from.
- `currencyX: Currency` - The currency to swap from.
- `currencyY: Currency` - The currency to swap to.
- `fromAmount: bigint` - The amount of the source currency to swap.
- `minDy: bigint` - The minimum amount of the destination currency to receive.
- `customRoute?: AMMRoute` - An optional custom route for the swap.

- **Returns**: `Promise<TxToBroadCast>` - A promise that resolves to a TxToBroadCast object, representing the transaction to be broadcasted.

### `getLatestPrices()`

Retrieves the latest prices for all supported currencies.

- **Returns**: `Promise<Partial<{ [currency in Currency]: number }>>` - A promise that resolves to an object containing the latest prices for each currency.

### `getBalances(stxAddress: string)`

Retrieves the balances of all supported currencies for a given Stacks (STX) address.

- **Parameters**:

- `stxAddress: string` - The Stacks (STX) address to retrieve the balances for.
```typescript
export declare class AlexSDK {
fetchSwappableCurrency(): Promise<TokenInfo[]>;
getAllPossibleRoutes(from: Currency, to: Currency): Promise<AMMRoute[]>;
getAmountTo(from: Currency, fromAmount: bigint, to: Currency): Promise<bigint>;
getBalances(stxAddress: string): Promise<Partial<{ [currency in Currency]: bigint }>>;
getFeeRate(from: Currency, to: Currency): Promise<bigint>;
getLatestPrices(): Promise<Partial<{ [currency in Currency]: number }>>;
getRoute(from: Currency, to: Currency): Promise<AMMRoute>;
getRouter(from: Currency, to: Currency): Promise<Currency[]>; // deprecated
getWayPoints(route: AMMRoute): Promise<TokenInfo[]>;
runSwap(stxAddress: string, currencyX: Currency,
currencyY: Currency, fromAmount: bigint,
minDy: bigint, customRoute: AMMRoute): Promise<TxToBroadCast>;
}
```

- **Returns**: `Promise<Partial<{ [currency in Currency]: bigint }>>` - A promise that resolves to an object containing the balances of each currency for the given address.
(detailed list [here](./documentation.md)).

## Usage

Expand Down
21 changes: 12 additions & 9 deletions documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ The AlexSDK class includes the following functions:

```typescript
export declare class AlexSDK {
fetchSwappableCurrency(): Promise<TokenInfo[]>;
getAmountTo(from: Currency, fromAmount: bigint, to: Currency): Promise<bigint>;
getBalances(stxAddress: string): Promise<Partial<{ [currency in Currency]: bigint }>>;
getFeeRate(from: Currency, to: Currency): Promise<bigint>;
getLatestPrices(): Promise<Partial<{ [currency in Currency]: number }>>;
getRoute(from: Currency, to: Currency): Promise<Currency[]>;
runSwap(stxAddress: string, currencyX: Currency,
currencyY: Currency, fromAmount: bigint,
minDy: bigint, customRoute: Currency[]): Promise<TxToBroadCast>;
fetchSwappableCurrency(): Promise<TokenInfo[]>;
getAllPossibleRoutes(from: Currency, to: Currency): Promise<AMMRoute[]>;
getAmountTo(from: Currency, fromAmount: bigint, to: Currency): Promise<bigint>;
getBalances(stxAddress: string): Promise<Partial<{ [currency in Currency]: bigint }>>;
getFeeRate(from: Currency, to: Currency): Promise<bigint>;
getLatestPrices(): Promise<Partial<{ [currency in Currency]: number }>>;
getRoute(from: Currency, to: Currency): Promise<AMMRoute>;
getRouter(from: Currency, to: Currency): Promise<Currency[]>; // deprecated
getWayPoints(route: AMMRoute): Promise<TokenInfo[]>;
runSwap(stxAddress: string, currencyX: Currency,
currencyY: Currency, fromAmount: bigint,
minDy: bigint, customRoute: AMMRoute): Promise<TxToBroadCast>;
}
```

Expand Down
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module.exports = {
testEnvironment: 'node',
verbose: true,
maxWorkers: 1
};
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"build": "dts build",
"prepare": "pnpm run build",
"test": "dts test",
"test:coverage": "dts test --coverage",
"lint": "dts lint",
"lint-fix": "dts lint --fix",
"size": "size-limit",
"gen:contract": "rm -rf src/generated/smartContract/* && tsx ./scripts/gen-contract.ts && prettier --write 'src/generated/smartContract'",
"analyze": "size-limit --why",
Expand Down Expand Up @@ -62,6 +64,7 @@
"ajv": "^8.16.0",
"dts-cli": "^2.0.5",
"esm": "^3.2.25",
"fetch-mock": "^10.0.7",
"husky": "^8.0.3",
"prettier": "^2.8.4",
"size-limit": "^8.2.4",
Expand Down
40 changes: 40 additions & 0 deletions test/alexSDK.mock-exceptions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AlexSDK, Currency } from '../src';
import * as ammRouteResolver from '../src/utils/ammRouteResolver';
import { configs } from '../src/config';

const sdk = new AlexSDK();

const tokenAlex = 'age000-governance-token' as Currency;
const tokenWUSDA = 'token-wusda' as Currency;

const dummyRoute = ['TokenA', 'TokenB', 'TokenC', 'TokenD', 'TokenE', 'TokenF'];
jest.mock('../src/utils/ammRouteResolver', () => ({
resolveAmmRoute: jest.fn(async () => dummyRoute),
}));

describe('AlexSDK - mock exceptions', () => {
it('Attempt to Get Fee Rate with more than 4 pools in route', async () => {
expect(jest.isMockFunction(ammRouteResolver.resolveAmmRoute)).toBeTruthy();
await expect(sdk.getFeeRate(tokenAlex, Currency.STX)).rejects.toThrow(
'Too many AMM pools in route'
);
}, 10000);

it('Attempt to getAmountTo with more than 4 pools in route', async () => {
await expect(
sdk.getAmountTo(Currency.STX, BigInt(2) * BigInt(1e8), tokenWUSDA)
).rejects.toThrow('Too many AMM pools in route');
}, 10000);

it('Attempt to run swap with more than 4 pools in route', async () => {
await expect(
sdk.runSwap(
configs.CONTRACT_DEPLOYER,
tokenAlex,
tokenWUSDA,
BigInt(2) * BigInt(1e8),
BigInt(0)
)
).rejects.toThrow('Too many AMM pools in route');
}, 10000);
});
146 changes: 146 additions & 0 deletions test/alexSDK.mock-externals.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { AlexSDK, Currency, TokenInfo } from '../src';
import fetchMock from 'fetch-mock';
import { configs } from '../src/config';
import { fetchBalanceForAccount, getPrices } from '../src/utils/fetchData';
import { transferFactory } from '../src/utils/postConditions';

const sdk = new AlexSDK();

const tokenAlex = 'age000-governance-token' as Currency;
const tokenWUSDA = 'token-wusda' as Currency;

const tokenMappings: TokenInfo[] = [
{
id: 'token-x' as Currency,
name: 'Token x',
icon: 'icon-x',
wrapToken: 'wrap-token-x',
wrapTokenDecimals: 8,
underlyingToken: 'underlying-token-x',
underlyingTokenDecimals: 8,
isRebaseToken: false,
},
];

const stxAddress = 'SM2MARAVW6BEJCD13YV2RHGYHQWT7TDDNMNRB1MVT';

describe('AlexSDK - mock externals', () => {
beforeEach(() => {
fetchMock.get(configs.SDK_API_HOST, 500);
});
afterEach(() => {
fetchMock.restore();
});

it('Attempt to Get Latest Prices with incorrect Alex SDK Data', async () => {
await expect(sdk.getLatestPrices()).rejects.toThrow(
'Failed to fetch token mappings'
);
}, 10000);
it('Attempt to Get Fee with incorrect Alex SDK Data', async () => {
await expect(sdk.getFeeRate(tokenAlex, Currency.STX)).rejects.toThrow(
'Failed to fetch token mappings'
);
}, 10000);

it('Attempt to Get Router with incorrect Alex SDK Data', async () => {
await expect(sdk.getRouter(tokenAlex, Currency.STX)).rejects.toThrow(
'Failed to fetch token mappings'
);
}, 10000);

it('Attempt to Get Amount with incorrect Alex SDK Data', async () => {
await expect(
sdk.getAmountTo(Currency.STX, BigInt(2) * BigInt(1e8), tokenWUSDA)
).rejects.toThrow('Failed to fetch token mappings');
}, 10000);

it('Attempt to Run Swap with incorrect Alex SDK Data', async () => {
await expect(
sdk.runSwap(
configs.CONTRACT_DEPLOYER,
tokenAlex,
tokenWUSDA,
BigInt(2) * BigInt(1e8),
BigInt(0)
)
).rejects.toThrow('Failed to fetch token mappings');
}, 10000);

it('Attempt to Get Latest Prices with incorrect Alex SDK Data', async () => {
await expect(sdk.getLatestPrices()).rejects.toThrow(
'Failed to fetch token mappings'
);
}, 10000);

it('Attempt to Get Balances with incorrect Alex SDK Data', async () => {
const stxAddress = 'SM2MARAVW6BEJCD13YV2RHGYHQWT7TDDNMNRB1MVT';
await expect(sdk.getBalances(stxAddress)).rejects.toThrow(
'Failed to fetch token mappings'
);
}, 10000);

it('Attempt to Fetch Swappable Currency with incorrect Alex SDK Data', async () => {
await expect(sdk.fetchSwappableCurrency()).rejects.toThrow(
'Failed to fetch token mappings'
);
}, 10000);
});

describe('AlexSDK - mock externals - BACKEND_API_HOST', () => {
beforeEach(() => {
fetchMock.get(`${configs.BACKEND_API_HOST}/v2/public/token-prices`, {
status: 500,
body: 'Internal Server Error',
});
});

afterEach(() => {
fetchMock.restore();
});

it('Attempt to get token prices with incorrect data', async () => {
await expect(getPrices(tokenMappings)).rejects.toThrow(
'Failed to fetch token mappings'
);
expect(
fetchMock.calls(`${configs.BACKEND_API_HOST}/v2/public/token-prices`)
.length
).toBe(1);
}, 10000);
});

describe('Transfer Factory', () => {
it('Throws error in Transfer Factory', () => {
const transfer = transferFactory(tokenMappings);
expect(() => transfer(stxAddress, tokenAlex, BigInt(1000))).toThrow(
'Token mapping not found'
);
});
});

describe('AlexSDK - mock externals - STACKS_API_HOST', () => {
beforeEach(() => {
fetchMock.get(
`${configs.STACKS_API_HOST}/extended/v1/address/${stxAddress}/balances`,
{
status: 500,
body: 'Internal Server Error',
}
);
});

afterEach(() => {
fetchMock.restore();
});

it('Attempt to Get Balances with incorrect data', async () => {
await expect(
fetchBalanceForAccount(stxAddress, tokenMappings)
).rejects.toThrow(
new SyntaxError(
'Unexpected token \'I\', "Internal S"... is not valid JSON'
)
);
}, 10000);
});
Loading

0 comments on commit fc23587

Please sign in to comment.