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

Feat/ether v6 support #182

Merged
merged 24 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Refer to the documentation of the ParaSwap API: https://developers.paraswap.netw

**Canonical**: bring only the functions you actually need

**Lightweight**: 400B Gzipped for the minimal variant
**Lightweight**: 10KB Gzipped for the minimal variant

## Installing ParaSwap SDK

Expand All @@ -29,7 +29,7 @@ You can see some examples in [/src/examples](src/examples) directory.

### Simple SDK

Can be created by providing `chainId` and either `axios` or `window.fetch` (or alternative `fetch` implementation), and an optional `version` (`'5'` or `'6.1'`) parameter that corresponds to the API version SDK will be making requests to. The resulting SDK will be able to use all methods that query the API.
Can be created by providing `chainId` and either `axios` or `window.fetch` (or alternative `fetch` implementation), and an optional `version` (`'5'` or `'6.2'`) parameter that corresponds to the API version SDK will be making requests to. The resulting SDK will be able to use all methods that query the API.

```ts
import { constructSimpleSDK } from '@paraswap/sdk';
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
"bignumber.js": "^9.1.2",
"dotenv": "^16.4.5",
"dts-cli": "^2.0.5",
"ethers": "^5.7.2",
"ethers": "^6.13.4",
"ethersV5": "npm:ethers@5",
"hardhat": "^2.22.15",
"hardhat-switch-network": "^1.1.1",
"husky": "^9.1.6",
Expand All @@ -72,9 +73,9 @@
},
"peerDependencies": {
"axios": ">=0.25.0 <2.0.0",
"ethers": "^5.5.0",
"web3": "^4.14.0",
"viem": "^2.21.0"
"ethers": "^5.5.0 || ^6.0.0",
"viem": "^2.21.0",
"web3": "^4.14.0"
},
"peerDependenciesMeta": {
"axios": {
Expand Down
38 changes: 38 additions & 0 deletions src/examples/ethersV6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import { ethers } from 'ethers';
import {
constructPartialSDK,
constructFullSDK,
constructGetAdapters,
constructEthersV6ContractCaller,
constructAxiosFetcher,
} from '..';

const fetcher = constructAxiosFetcher(axios);

const provider = ethers.getDefaultProvider(1);
const contractCaller = constructEthersV6ContractCaller({
ethersV6ProviderOrSigner: provider,
EthersV6Contract: ethers.Contract,
});

const paraswap = constructFullSDK({
chainId: 1,
fetcher,
contractCaller,
});

const res = paraswap.swap.getAdapters();

// type Promise<ContractTransaction>
const txResponse = paraswap.swap.approveToken('1', '0x...');
// type Promise<ContractTransaction[]>
const txResponses = paraswap.swap.approveTokenBulk('1', ['0x...']);

const partial = constructPartialSDK(
{ apiURL: '', chainId: 1, fetcher },
constructGetAdapters
);

const res1 = partial.getAdapters();
2 changes: 1 addition & 1 deletion src/examples/limitOrders_all.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { ethers } from 'ethersV5';
import { assert } from 'ts-essentials';
import {
// swap methods
Expand Down
2 changes: 1 addition & 1 deletion src/examples/limitOrders_partial.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { ethers } from 'ethersV5';
import { assert } from 'ts-essentials';
import {
// swap methods
Expand Down
2 changes: 1 addition & 1 deletion src/examples/limitOrders_postOrder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import { ethers } from 'ethers';
import { ethers } from 'ethersV5';

import {
// swap methods
Expand Down
2 changes: 1 addition & 1 deletion src/examples/partial.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import { ethers } from 'ethers';
import { ethers } from 'ethersV5';
import {
constructPartialSDK,
constructGetAdapters,
Expand Down
6 changes: 3 additions & 3 deletions src/examples/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import { ethers } from 'ethers';
import { ethers } from 'ethersV5';
import {
constructPartialSDK,
constructFullSDK,
constructGetAdapters,
constructEthersContractCaller,
constructEthersV5ContractCaller,
constructAxiosFetcher,
} from '..';

const fetcher = constructAxiosFetcher(axios);

const provider = ethers.getDefaultProvider(1);
const contractCaller = constructEthersContractCaller({
const contractCaller = constructEthersV5ContractCaller({
ethersProviderOrSigner: provider,
EthersContract: ethers.Contract,
});
Expand Down
2 changes: 1 addition & 1 deletion src/examples/simple.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import { ethers, Wallet } from 'ethers';
import { ethers, Wallet } from 'ethersV5';
import {
constructSimpleSDK,
ContractMethod,
Expand Down
13 changes: 11 additions & 2 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { EthersV5ProviderDeps } from './providers/ethers';
import { EthersV6ProviderDeps } from './providers/ethersV6';
export { constructFetcher as constructAxiosFetcher } from './fetchers/axios';
export { constructFetcher as constructFetchFetcher } from './fetchers/fetch';
export {
constructContractCaller as constructEthersContractCaller,
EthersProviderDeps,
constructEthersV5ContractCaller as constructEthersContractCaller,
constructEthersV5ContractCaller,
EthersV5ProviderDeps,
} from './providers/ethers';
export {
constructContractCaller as constructEthersV6ContractCaller,
EthersV6ProviderDeps,
} from './providers/ethersV6';
export type EthersProviderDeps = EthersV5ProviderDeps | EthersV6ProviderDeps;

export {
constructContractCaller as constructWeb3ContractCaller,
Web3UnpromiEvent,
Expand Down
96 changes: 18 additions & 78 deletions src/helpers/misc.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,4 @@
import type {
Contract as EthersContract,
ContractFunction as EthersContractFunction,
PopulatedTransaction as EthersPopulatedTransaction,
BigNumber as EthersBigNumber,
} from 'ethers';
import { assert, Primitive } from 'ts-essentials';

import type { AxiosError, AxiosResponse } from 'axios';
import type { ContractAbi, Contract as Web3Contract } from 'web3';

export type Web3ContractSendMethod =
Web3Contract<ContractAbi>['methods'][string];

export type EthersContractWithMethod<T extends string> = EthersContract & {
readonly [method in T]: EthersContractFunction;
} & {
readonly functions: { [method in T]: EthersContractFunction };

readonly callStatic: { [method in T]: EthersContractFunction };
readonly estimateGas: {
[method in T]: EthersContractFunction<EthersBigNumber>;
};
readonly populateTransaction: {
[method in T]: EthersContractFunction<EthersPopulatedTransaction>;
};
};

export function ethersContractHasMethods<T extends string>(
contract: EthersContract,
...methods: T[]
): contract is EthersContractWithMethod<T> {
return methods.every((method) => typeof contract[method] === 'function');
}

export function assertEthersContractHasMethods<T extends string>(
contract: EthersContract,
...methods: T[]
): asserts contract is EthersContractWithMethod<T> {
assert(
ethersContractHasMethods(contract, ...methods),
`Contract must have methods: ${methods.join(', ')}`
);
}

export type Web3ContractWithMethod<T extends string> =
Web3Contract<ContractAbi> & {
methods: { [method in T]: Web3ContractSendMethod };
};

export function web3ContractHasMethods<T extends string>(
contract: Web3Contract<ContractAbi>,
...methods: T[]
): contract is Web3ContractWithMethod<T> {
return methods.every(
(method) => typeof contract.methods[method] === 'function'
);
}

export function assertWeb3ContractHasMethods<T extends string>(
contract: Web3Contract<ContractAbi>,
...methods: T[]
): asserts contract is Web3ContractWithMethod<T> {
assert(
web3ContractHasMethods(contract, ...methods),
`Contract must have methods: ${methods.join(', ')}`
);
}
import type { Primitive } from 'ts-essentials';

export const objectToFilledEntries = <T extends Record<string, unknown>>(
object: T
Expand All @@ -92,16 +25,23 @@ export const constructSearchString = <
return queryString && `?${queryString}`;
};

type FetcherErrorConstructorInput = Pick<
AxiosError,
'code' | 'request' | 'isAxiosError' | 'message'
> & {
response?: Pick<
AxiosResponse,
'data' | 'status' | 'statusText' | 'headers'
> & {
config: { url?: string; method?: string };
};
type MinAxiosError = {
code?: string;
request?: any;
isAxiosError: boolean;
message: string;
};

type MinAxiosResponse = {
data: any;
status: number;
statusText: string;
headers: Record<string, any>;
config: { url?: string; method?: string };
};

type FetcherErrorConstructorInput = MinAxiosError & {
response?: MinAxiosResponse;
};

export interface FetcherErrorInterface extends FetcherErrorConstructorInput {
Expand Down
52 changes: 44 additions & 8 deletions src/helpers/providers/ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,36 @@ import type {
} from '@ethersproject/providers';
import type { Signer } from '@ethersproject/abstract-signer';
import type {
Contract as EthersContract,
Contract as EthersV5Contract,
ContractFunction as EthersContractFunctionV5,
PopulatedTransaction as EthersPopulatedTransactionV5,
PayableOverrides,
CallOverrides,
ContractTransaction,
} from '@ethersproject/contracts';
import { assertEthersContractHasMethods } from '../misc';

import type { BigNumber as EthersBigNumberV5 } from '@ethersproject/bignumber';

import { assert } from 'ts-essentials';

export interface EthersProviderDeps {
export interface EthersV5ProviderDeps {
ethersProviderOrSigner: BaseProvider | Signer;
EthersContract: typeof EthersContract; // passing Contract in allows not to include ethers as dependency even when using legacy ParaSwap class
EthersContract: typeof EthersV5Contract; // passing Contract in allows not to include ethers as dependency even when using legacy ParaSwap class
}

export const constructContractCaller = (
export const constructEthersV5ContractCaller = (
{
ethersProviderOrSigner: providerOrSigner,
EthersContract: Contract,
}: EthersProviderDeps,
}: EthersV5ProviderDeps,
account?: Address
): ContractCallerFunctions<ContractTransaction> => {
const staticCall: StaticContractCallerFn = async (params) => {
const { address, abi, contractMethod, args, overrides } = params;

const contract = new Contract(address, abi, providerOrSigner);

assertEthersContractHasMethods(contract, contractMethod);
assertEthersContractHasMethodsV5(contract, contractMethod);
// drop keys not in CallOverrides
const { block, gas, ...restOverrides } = overrides;
// reassign values to keys in CallOverrides
Expand Down Expand Up @@ -80,7 +84,7 @@ export const constructContractCaller = (

const contract = new Contract(address, abi, signer);

assertEthersContractHasMethods(contract, contractMethod);
assertEthersContractHasMethodsV5(contract, contractMethod);
// drop keys not in PayableOverrides
const { gas, from, ...restOverrides } = overrides;
// reassign values to keys in PayableOverrides
Expand Down Expand Up @@ -154,3 +158,35 @@ function isTypedDataCapableSigner(
): signer is Signer & Pick<JsonRpcSigner, '_signTypedData'> {
return '_signTypedData' in signer;
}

/// ethers v5
type EthersContractWithMethodV5<T extends string> = EthersV5Contract & {
readonly [method in T]: EthersContractFunctionV5;
} & {
readonly functions: { [method in T]: EthersContractFunctionV5 };

readonly callStatic: { [method in T]: EthersContractFunctionV5 };
readonly estimateGas: {
[method in T]: EthersContractFunctionV5<EthersBigNumberV5>;
};
readonly populateTransaction: {
[method in T]: EthersContractFunctionV5<EthersPopulatedTransactionV5>;
};
};

function ethersContractHasMethodsV5<T extends string>(
contract: EthersV5Contract,
...methods: T[]
): contract is EthersContractWithMethodV5<T> {
return methods.every((method) => typeof contract[method] === 'function');
}

function assertEthersContractHasMethodsV5<T extends string>(
contract: EthersV5Contract,
...methods: T[]
): asserts contract is EthersContractWithMethodV5<T> {
assert(
ethersContractHasMethodsV5(contract, ...methods),
`Contract must have methods: ${methods.join(', ')}`
);
}
Loading
Loading