Skip to content

Commit

Permalink
Feat/Delta support (#183)
Browse files Browse the repository at this point in the history
* update build dependencies

* update tsconfig

* actions/remove Node18

* fix types

* extra gitignore

* update typedoc deps

* update snapshots

* abstract away providers/findPrimaryType

* add TransactionParams.maxFeeGas params

* providers/viem

* export from providers/viem

* DEFAULT_VERSION better type

* add wagmi & viem deps

* SimpleSDK/viem for contractCaller

* examples/viem

* cleanup

* examples/wagmi

* fix dependencies

* tests/fix types

* examples/fix types

* tests/fix types

* viem/fix account usage

* examples/viem/fix account usage

* types/TxHash=Hex

* add hardhat

* legacy/fix types

* ignore cache files

* some tests

* tsconfig/allow BigInt short notation

* providers/viem/reuse account

* viem/fix tests

* tests/viem/remove extra

* tests/viem/Order signing

* rename stuff

* cleanup

* reenable tests

* tests/viem/const acc

* tests/viem/update snapshots

* reexport txParamsToViemTxParams

* tests/viem/update snapshots

* more comments

* cleanup

* remove extra deps

* moved bignumber.js to devDeps

* cleanup

* update tests jpeg

* update README

* update snapshots

* FetcherError/fix types

* viem test/market swap

* fix deps

* update deps

* update snapshots

* remove dummy test

* change default version

* update some deps

* replace ganache with hardhat

* remove ganache

* fix chainId in tests mismatch

* try with tevm

* tevm test

* add hardhat-switch-network plugin

* simplify hardhat helpers

* make tests work with hardhat

* fix method name

* disable web3 tests that break with hardhat

* fix NFT tests

* remove tevm

* hardhat config/explicit gasPrice

* NFT tests/adjust amounts

* cleanup

* remove temp tests

* update some deps

* NFT tests/workaround some errors

* update ethers dep

* update web3 dep(slightly)

* update deps

* override some deps

* update some deps

* hadrhat config/smaller default baseFee

* NFT tests/workaround edge case

* update web3 dep

* update Web3 types

* update web3/constructContractCaller

* legacy/update types

* hardhat config/fixed accounts

* LOrder tests/reenable sign with web3

* NFT Order tests/reenable sign with web3

* getBalance tests workaround

* cleanup

* update some deps

* update snapshots

* update perrDeps

* update required Node v

* NFT tests/account for dust

* hadrhat config/lower initialBaseFeePerGas

* install ethersV6 as alias

* providers/ethersV6

* distinct ethersV5 exports

* legacy/support ethersV6

* simple SDK/support ethersV6

* simpleSDK.tests/add ethersV6

* partialSDK.tests/add ethersV6

* LOrders.tests/add ethersV6

* NFT_Orders.tests/add ethersV6

* update snapshots

* examples/ add ethersV6

* perrDeps/update ethers versions

* update snapshots

* cleanup

* delta/approve

* delta/getContract

* delta/getOrders

* delta/some types

* delta/getPrice

* delta/getPartnerFee

* delta/sanitizeOrderData

* delta/composePermit

* delta/buildOrderData

* delta/buildData

* delta/signOrder

* extend AdaptersContractsResult

* delta/postOrder

* delta/submitOrder & allHandlers

* delta/buildOrder/explicit BuildDeltaOrderDataParams

* add PartnerFeeResponse.takeSurplus

* reexport Delta methods & types

* BuildDeltaOrderDataParams.partner?

* DeltaPriceParams.*decimals: required

* examples/delta

* fullSDK/delta methods

* simpleSDK/delta methods

* producePartnerAndFee |= (takeSuplus << 8)

* examples/delta/manualDeltaFlow

* cleanup

* move ethers types to provider/ethers

* move web3 types to provider/web3

* untie FetchError type from AxiosError

* cleanup

* reexport DeltaPriceParams

* ethers -> ethersV5, ethersV6 -> ethers to fix types when used as lib

* deriveCompactSignature

* signDeltaOrder/deriveCompactSignature

* move ethers types to provider/ethers

* move web3 types to provider/web3

* untie FetchError type from AxiosError

* ethers -> ethersV5, ethersV6 -> ethers to fix types when used as lib

* cleanup

* fetch/remove unnecessary headers

* delta/examples/fix slippage

* reexport more types

* cleanup

* delta/getPrices/fix query params

* type DeltaPrice/add fields

* Partial/delta/approve/fix tx type

* ethersV6/more explicit Contract method calling

* delta/add tests

* delta/getPartnerFee/temp workaround for API response

* README/update version

* README/update version

* less dependency on ethers types

* update README

* cleanup

* remove default partner

* remove temp tests

* ParaswapDeltaAuction.orderHash: string | null

* cleanup

* fix typos

* cleanup

* replace deprecated method

* Delta/signDeltaOrder/skip compact sig when incompatible

* update Delta Order types

* update tests

* fix typo

* Delta/update types

* tests/Delta/update snapshots

* Delta/annotate some types

* Delta/getDEltaOrders/pagination options

* Feat/Quote support (#185)

* quote/getQuote

* reexport from quote

* simpleSDK/add quote

* partialSDK/simplify type

* fullSDK/add quote

* examples/simple/fix wrong Token

* quote/getQuote/more specific types

* examples/quote

* delta/buildDeltaOrders/allow to override partnerFee data

* quote/add tests

* examples/simpleQuote

* examples/quote/ fix amount var

* cleanup

* README/add quote usage

* buildDeltaOrder/partnerAndFee encoding shortcut

* cleanup

* DeltaAuctionOrder.nonce: number -> string

* test/delta/update

* tests/quote/update

* quote/getQuote res/fallback_reason -> fallbackReason

* quote/annotate some types

* tests/quote/update snaps

* quote/getQuote/update response types

* tests/quote/add gas cost error test

* tests/quote/fix purposefully failing tests

* tests/delta/fix orders order

* examples/quote/fix types

* fix linter

* examples/delta/improve flow

* delta/Permit/composeDeltaOrderPermit/special handling

* Readme/DELTA.md

* Readme/update

* cleanup

* add type ParaswapDeltaAuction.deltaVersion

* tests/delta/update snapshots

* fix typo

Co-authored-by: andriy-shymkiv <[email protected]>

---------

Co-authored-by: andriy-shymkiv <[email protected]>

---------

Co-authored-by: andriy-shymkiv <[email protected]>
  • Loading branch information
Velenir and andriy-shymkiv authored Dec 9, 2024
1 parent 3988f11 commit a4a997b
Show file tree
Hide file tree
Showing 36 changed files with 3,059 additions and 25 deletions.
89 changes: 89 additions & 0 deletions DELTA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
**ParaSwap Delta** is an intent-based protocol that enables a ParaSwap user to make gasless swaps where multiple agents compete to execute the trade at the best price possible.
This way the user doesn't need to make a transaction themselve but only to sign a Delta Order.
The easiest way to make use of the Delta Order is to use the SDK following these steps:

### 1. Construct an SDK object

```ts
const account = userAddress;
const paraSwap = constructSimpleSDK(
{chainId: 1, axios},
{
ethersProviderOrSigner: provider, // JsonRpcProvider
EthersContract: ethers.Contract,
account,
});
// for usage with different web3 provider libraries refer to the main [README](./README.md)
```

### 2. Request prices for a Token pair

```ts
const DAI_TOKEN = '0x6b175474e89094c44da98b954eedeac495271d0f';
const PSP_TOKEN = '0xcafe001067cdef266afb7eb5a286dcfd277f3de5';
const amount = '1000000000000'; // in wei

const deltaPrice = await deltaSDK.getDeltaPrice({
srcToken: DAI_TOKEN,
destToken: PSP_TOKEN,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
// partner: "..." // if available
});
```


### 3. Approve srcToken for DeltaContract

```ts
const tx = await deltaSDK.approveTokenForDelta(amount, DAI_TOKEN);
await tx.wait();
```

Alternatively sign Permit (DAI or Permit1) or Permit2 TransferFrom with DeltaContract as the verifyingContract

```ts
const DeltaContract = await deltaSDK.getDeltaContract();

// values depend on the Permit type and the srcToken
const signature = await signer._signTypedData(domain, types, message);
```

See more on accepted Permit variants in [ParaSwap documentation](https://developers.paraswap.network/api/paraswap-delta/build-and-sign-a-delta-order#supported-permits)


### 4. Sign and submit a Delta Order

```ts
// calculate acceptable destAmount
const slippagePercent = 0.5;
const destAmountAfterSlippage = (
+deltaPrice.destAmount *
(1 - slippagePercent / 100)
).toString(10);

const signableOrderData = await deltaSDK.buildDeltaOrder({
deltaPrice,
owner: account,
// beneficiary: anotherAccount, // if need to send destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
srcToken: DAI_TOKEN,
destToken: PSP_TOKEN,
srcAmount: amount,
destAmount: destAmountAfterSlippage, // minimum acceptable destAmount
});
```

### 5. Wait for Delta Order execution

```ts
// poll if necessary
const auction = await deltaSDK.getDeltaOrderById(deltaAuction.id);
if (auction?.status === 'EXECUTED') {
console.log('Auction was executed');
}
```

#### A more detailed example of Delta Order usage can be found in [examples/delta](./src/examples/delta.ts)
114 changes: 110 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,20 @@ Can be created by providing `chainId` and either `axios` or `window.fetch` (or a
If optional `providerOptions` is provided as the second parameter, then the resulting SDK will also be able to approve Tokens for swap.

```ts
// with ethers.js
const providerOptionsEther = {
// with ethers@5
const providerOptionsEtherV5 = {
ethersProviderOrSigner: provider, // JsonRpcProvider
EthersContract: ethers.Contract,
account: senderAddress,
};

// with ethers@6
const providerOptionsEtherV6 = {
ethersV6ProviderOrSigner: provider, // JsonRpcProvider
EthersV6Contract: ethers.Contract,
account: senderAddress,
};

// or with viem (from wagmi or standalone)
const providerOptionsViem = {
viemClient, // made with createWalletClient()
Expand All @@ -101,7 +108,7 @@ If optional `providerOptions` is provided as the second parameter, then the resu
account: senderAddress,
};

const paraSwap = constructSimpleSDK({chainId: 1, axios}, providerOptionsEther);
const paraSwap = constructSimpleSDK({chainId: 1, axios}, providerOptionsEtherV5);

// approve token through sdk
const txHash = await paraSwap.approveToken(amountInWei, DAI);
Expand Down Expand Up @@ -149,6 +156,105 @@ const priceRoute = await minParaSwap.getRate(params);
const allowance = await minParaSwap.getAllowance(userAddress, tokenAddress);
```

### Basic usage

The easiest way to make a trade is to rely on Quote method that communicates with [/quote API endpoint](https://developers.paraswap.network/api/paraswap-delta/retrieve-delta-price-with-fallback-to-market-quote)

```typescript
import axios from 'axios';
import { ethers } from 'ethersV5';
import { constructSimpleSDK } from '@paraswap/sdk';

const ethersProvider = new ethers.providers.Web3Provider(window.ethereum);

const accounts = await ethersProvider.listAccounts();
const account = accounts[0]!;
const signer = ethersProvider.getSigner(account);

const simpleSDK = constructSimpleSDK(
{ chainId: 1, axios },
{
ethersProviderOrSigner: signer,
EthersContract: ethers.Contract,
account,
}
);

const amount = '1000000000000'; // wei
const Token1 = '0x1234...'
const Token2 = '0xabcde...'

const quote = await simpleSDK.quote.getQuote({
srcToken: Token1,
destToken: Token2,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
mode: 'all', // Delta quote if possible, with fallback to Market price
side: 'SELL',
// partner: "..." // if available
});

if ('delta' in quote) {
const deltaPrice = quote.delta;

const DeltaContract = await simpleSDK.delta.getDeltaContract();

// or sign a Permit1 or Permit2 TransferFrom for DeltaContract
await simpleSDK.delta.approveTokenForDelta(amount, Token1);

const slippagePercent = 0.5;
const destAmountAfterSlippage = BigInt(
// get rid of exponential notation

+(+deltaPrice.destAmount * (1 - slippagePercent / 100)).toFixed(0)
// get rid of decimals
).toString(10);

const deltaAuction = await simpleSDK.delta.submitDeltaOrder({
deltaPrice,
owner: account,
// beneficiary: anotherAccount, // if need to send destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
srcToken: Token1,
destToken: Token2,
srcAmount: amount,
destAmount: destAmountAfterSlippage, // minimum acceptable destAmount
});

// poll if necessary
const auction = await simpleSDK.delta.getDeltaOrderById(deltaAuction.id);
if (auction?.status === 'EXECUTED') {
console.log('Auction was executed');
}
} else {
console.log(
`Delta Quote failed: ${quote.fallbackReason.errorType} - ${quote.fallbackReason.details}`
);
const priceRoute = quote.market;

const TokenTransferProxy = await simpleSDK.swap.getSpender();

// or sign a Permit1 or Permit2 TransferFrom for TokenTransferProxy
const approveTxHash = simpleSDK.swap.approveToken(amount, Token1);

const txParams = await simpleSDK.swap.buildTx({
srcToken: Token1,
destToken: Token2,
srcAmount: amount,
slippage: 250, // 2.5%
priceRoute,
userAddress: account,
// partner: '...' // if available
});

const swapTx = await signer.sendTransaction(txParams);
}
```

#### For Delta protocol usage refer to [DELTA.md](./DELTA.md)

### Legacy
The `ParaSwap` class is exposed for backwards compatibility with previous versions of the SDK.

Expand Down Expand Up @@ -205,4 +311,4 @@ Refer to [SDK API documentation](docs/md/modules.md) for detailed documentation
To run `yarn test` it is necessary to provide `PROVIDER_URL=<mainnet_rpc_url>` environment variable.
If it is necessary to run tests against a different API endpoint, provide `API_URL=url_to_API` environment variable.

<img src="./docs/passed_tests.jpg" width=350 />
<img src="./docs/passed_tests.png" width=350 />
Binary file added docs/passed_tests.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: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const config: HardhatUserConfig = {
chainId: 1,
// have to config hardhat with fixed accounts unlocked, otherwise signTx and signTyped data fail when used in RPC calls to Node
// which breaks web3.js
// ethers signs data and txs locally off-chain as long as it has provate key
// ethers signs data and txs locally off-chain as long as it has private key
// web3 is a bit harder to init wallets for locally.
// impersonateAccounts doesn't work, even though it should logically fully unlock accounts,
// but only remore accounts are unlocked, fixed are not,
Expand Down
127 changes: 127 additions & 0 deletions src/examples/delta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from 'axios';
import { ethers, Wallet } from 'ethersV5';
import {
constructPartialSDK,
constructEthersContractCaller,
constructAxiosFetcher,
constructAllDeltaOrdersHandlers,
} from '..';

const fetcher = constructAxiosFetcher(axios);

const provider = ethers.getDefaultProvider(1);
const signer = Wallet.createRandom().connect(provider);
const account = signer.address;
const contractCaller = constructEthersContractCaller({
ethersProviderOrSigner: provider,
EthersContract: ethers.Contract,
});

// type AdaptersFunctions & ApproveTokenFunctions<ethers.ContractTransaction>
const deltaSDK = constructPartialSDK(
{
chainId: 1,
fetcher,
contractCaller,
},
constructAllDeltaOrdersHandlers
);

const DAI_TOKEN = '0x6b175474e89094c44da98b954eedeac495271d0f';
const PSP_TOKEN = '0xcafe001067cdef266afb7eb5a286dcfd277f3de5';

async function simpleDeltaFlow() {
const amount = '1000000000000'; // wei

const deltaPrice = await deltaSDK.getDeltaPrice({
srcToken: DAI_TOKEN,
destToken: PSP_TOKEN,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
// partner: "..." // if available
});

const DeltaContract = await deltaSDK.getDeltaContract();

// or sign a Permit1 or Permit2 TransferFrom for DeltaContract
const tx = await deltaSDK.approveTokenForDelta(amount, DAI_TOKEN);
await tx.wait();

const slippagePercent = 0.5;
const destAmountAfterSlippage = BigInt(
// get rid of exponential notation

+(+deltaPrice.destAmount * (1 - slippagePercent / 100)).toFixed(0)
// get rid of decimals
).toString(10);

const deltaAuction = await deltaSDK.submitDeltaOrder({
deltaPrice,
owner: account,
// beneficiary: anotherAccount, // if need to send destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
srcToken: DAI_TOKEN,
destToken: PSP_TOKEN,
srcAmount: amount,
destAmount: destAmountAfterSlippage, // minimum acceptable destAmount
});

// poll if necessary
const auction = await deltaSDK.getDeltaOrderById(deltaAuction.id);
if (auction?.status === 'EXECUTED') {
console.log('Auction was executed');
}
}
async function manualDeltaFlow() {
const amount = '1000000000000'; // wei

const deltaPrice = await deltaSDK.getDeltaPrice({
srcToken: DAI_TOKEN,
destToken: PSP_TOKEN,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
// partner: "..." // if available
});

const DeltaContract = await deltaSDK.getDeltaContract();

// or sign a Permit1 or Permit2 TransferFrom for DeltaContract
const tx = await deltaSDK.approveTokenForDelta(amount, DAI_TOKEN);
await tx.wait();

const slippagePercent = 0.5;
const destAmountAfterSlippage = (
+deltaPrice.destAmount *
(1 - slippagePercent / 100)
).toString(10);

const signableOrderData = await deltaSDK.buildDeltaOrder({
deltaPrice,
owner: account,
// beneficiary: anotherAccount, // if need to send destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
srcToken: DAI_TOKEN,
destToken: PSP_TOKEN,
srcAmount: amount,
destAmount: destAmountAfterSlippage, // minimum acceptable destAmount
});

const signature = await deltaSDK.signDeltaOrder(signableOrderData);

const deltaAuction = await deltaSDK.postDeltaOrder({
// partner: "..." // if available
order: signableOrderData.data,
signature,
});

// poll if necessary
const auction = await deltaSDK.getDeltaOrderById(deltaAuction.id);
if (auction?.status === 'EXECUTED') {
console.log('Auction was executed');
}
}
Loading

0 comments on commit a4a997b

Please sign in to comment.