-
Notifications
You must be signed in to change notification settings - Fork 113
Feat/external orders #228
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
Open
0xNazarii
wants to merge
21
commits into
feat/deltaSlippageParam
Choose a base branch
from
feat/externalOrders
base: feat/deltaSlippageParam
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,365
−245
Open
Feat/external orders #228
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
8455df8
add external delta orders support
0xNazarii 6dc7492
add example for external delta orders
0xNazarii 50c439d
Merge branch 'feat/deltaSlippageParam' of github.com:VeloraDEX/sdk in…
0xNazarii 99ba949
add `slippage` for external order build/submit
0xNazarii 9e12958
presignature abi dedup
0xNazarii 4e37d83
`DELTA_DEFAULT_EXPIRY` dedup
0xNazarii 4874d05
fix external order response typed with wrong order shape
0xNazarii 4be8b35
add external orders type to getter
0xNazarii c98efb5
duplicate partner and slippage resolution logic
0xNazarii 08c0bb4
fix SwapSideUnion import
0xNazarii 57676a0
merge with base branch
0xNazarii e590bc7
use OrderKind
0xNazarii 84829b7
fix example delta scripts
0xNazarii 7ccbef5
strong comparison
0xNazarii 4943d15
consistent truthfulness check
0xNazarii ef7d7b8
merge with base branch
0xNazarii 6569d3c
add `onChainOrderType` filter
0xNazarii 0969469
generic `DeltaAuction` type
0xNazarii a6fa4bf
infer order type from filter
0xNazarii d0b78e8
correctly convert fee to bps
0xNazarii fc8bc08
ExternalOrders docs
0xNazarii File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| ## External Delta Orders | ||
|
|
||
| External orders delegate token handling to an external **handler** contract, enabling complex DeFi strategies that go beyond simple token swaps. The handler manages the actual token logic (e.g. Aave flash loan + collateral/debt swap), while the Delta protocol handles the auction, settlement, and signature verification. | ||
|
|
||
| ### Handler Contract | ||
|
|
||
| The handler contract must be deployed by the integrator and implement the `IExternalProtocolHandler` interface: | ||
|
|
||
| ```solidity | ||
| interface IExternalProtocolHandler { | ||
| function execute( | ||
| address user, | ||
| address tokenIn, | ||
| uint256 amountIn, | ||
| address tokenOut, | ||
| uint256 amountOut, | ||
| bytes calldata data | ||
| ) external; | ||
| } | ||
| ``` | ||
|
|
||
| The `data` field carries protocol-specific parameters (e.g. an Aave operation type). A reference implementation is the [Aave handler deployed on Ethereum](https://etherscan.io/address/0xb4a2c36668cf8b19fe08f263e3685a5e16e82912#code). | ||
|
|
||
| ### Key Differences from Standard Delta Orders | ||
|
|
||
| | Feature | Standard Order | External Order | | ||
| |---|---|---| | ||
| | `beneficiary` field | yes | no (output goes to owner) | | ||
| | `bridge` field | yes | no | | ||
| | `handler` field | no | yes (required) | | ||
| | `data` field | no | yes (required) | | ||
| | Cross-chain support | yes | no | | ||
|
|
||
|
|
||
| --- | ||
|
|
||
| ### 1. Construct an SDK object | ||
|
|
||
| ```ts | ||
| const account = userAddress; | ||
| const deltaSDK = constructSimpleSDK( | ||
| { chainId: 1, axios }, // Ethereum | ||
| { | ||
| ethersProviderOrSigner: signer, | ||
| EthersContract: ethers.Contract, | ||
| account, | ||
| } | ||
| ); | ||
| // for usage with different web3 provider libraries refer to the main README | ||
| ``` | ||
|
|
||
| ### 2. Request prices for a Token pair | ||
|
|
||
| ```ts | ||
| const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; | ||
| const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; | ||
| const AAVE_HANDLER = '0xb4a2c36668cf8b19fe08f263e3685a5e16e82912'; | ||
| const amount = ethers.utils.parseUnits('1', 6).toString(); // 1 USDC | ||
|
|
||
| const deltaPrice = await deltaSDK.getDeltaPrice({ | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| amount, | ||
| userAddress: account, | ||
| srcDecimals: 6, | ||
| destDecimals: 18, | ||
| side: SwapSide.SELL, | ||
| }); | ||
| ``` | ||
|
|
||
| ### 3. Perform any prerequisites required by the handler | ||
|
|
||
| Each handler may have its own prerequisites (token approvals, delegation, position setup, etc.). Refer to your handler's documentation for details. | ||
|
|
||
| For example, the [Aave handler](https://etherscan.io/address/0xb4a2c36668cf8b19fe08f263e3685a5e16e82912#code) requires aToken approval before a collateral swap: | ||
|
|
||
| ```ts | ||
| const aUSDC = new ethers.Contract(USDC, ['function approve(address,uint256)'], signer); | ||
| await aUSDC.approve(AAVE_HANDLER, amount); | ||
| ``` | ||
|
|
||
| ### 4. Build, sign, and submit the External Order | ||
|
|
||
| #### Using `submitExternalDeltaOrder` (recommended) | ||
|
|
||
| The simplest approach — builds, signs, and posts the order in one call: | ||
|
|
||
| ```ts | ||
| const deltaAuction = await deltaSDK.submitExternalDeltaOrder({ | ||
| deltaPrice, | ||
| owner: account, | ||
| handler: AAVE_HANDLER, // your deployed handler contract | ||
| data: '0x0000000000000000000000000000000000000000000000000000000000000000', // protocol-specific bytes | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| srcAmount: amount, | ||
| slippage: 50, // 0.5% in bps | ||
| }); | ||
| ``` | ||
|
|
||
| #### Using individual steps | ||
|
|
||
| For more control, you can build, sign, and post separately: | ||
|
|
||
| ```ts | ||
| const signableOrderData = await deltaSDK.buildExternalDeltaOrder({ | ||
| deltaPrice, | ||
| owner: account, | ||
| handler: AAVE_HANDLER, | ||
| data: '0x0000000000000000000000000000000000000000000000000000000000000000', | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| srcAmount: amount, | ||
| slippage: 50, // 0.5% in bps | ||
| }); | ||
|
|
||
| const signature = await deltaSDK.signExternalDeltaOrder(signableOrderData); | ||
|
|
||
| const deltaAuction = await deltaSDK.postExternalDeltaOrder({ | ||
| order: signableOrderData.data, | ||
| signature, | ||
| }); | ||
| ``` | ||
|
|
||
| ### 5. Wait for execution | ||
|
|
||
| ```ts | ||
| const intervalId = setInterval(async () => { | ||
| const auction = await deltaSDK.getDeltaOrderById(deltaAuction.id); | ||
| console.log('Status:', auction.status); | ||
|
|
||
| if (auction.status === 'EXECUTED' || auction.status === 'FAILED') { | ||
| clearInterval(intervalId); | ||
| } | ||
| }, 3000); | ||
| ``` | ||
|
|
||
| ### 6. Query External Orders | ||
|
|
||
| ```ts | ||
| // fetch a specific external order | ||
| const order = await deltaSDK.getDeltaOrderById(orderId); | ||
|
|
||
| // list external orders only | ||
| const orders = await deltaSDK.getDeltaOrders({ | ||
| owner: account, | ||
| onChainOrderType: 'ExternalOrder', | ||
| }); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Specifying Amounts | ||
|
|
||
| There are two ways to specify amounts: | ||
|
|
||
| **With `slippage` (recommended)** — the SDK computes the slippage-adjusted amount from `deltaPrice` automatically: | ||
|
|
||
| ```ts | ||
| // SELL: provide srcAmount + slippage → destAmount auto-computed | ||
| await deltaSDK.buildExternalDeltaOrder({ | ||
| // ... | ||
| srcAmount: amount, | ||
| slippage: 50, // 0.5% in bps | ||
| }); | ||
|
|
||
| // BUY: provide destAmount + slippage → srcAmount auto-computed | ||
| await deltaSDK.buildExternalDeltaOrder({ | ||
| // ... | ||
| destAmount: amount, | ||
| slippage: 50, | ||
| }); | ||
| ``` | ||
|
|
||
| **With explicit amounts** — you compute both amounts yourself: | ||
|
|
||
| ```ts | ||
| await deltaSDK.buildExternalDeltaOrder({ | ||
| // ... | ||
| srcAmount: amount, | ||
| destAmount: destAmountAfterSlippage, | ||
| side: SwapSide.SELL, // or SwapSide.BUY | ||
| }); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Pre-signing External Orders | ||
|
|
||
| For smart contract wallets or other cases where EIP-712 signatures are not available, you can pre-sign an external order on-chain: | ||
|
|
||
| ```ts | ||
| const signableOrderData = await deltaSDK.buildExternalDeltaOrder({ | ||
| deltaPrice, | ||
| owner: account, | ||
| handler: AAVE_HANDLER, | ||
| data: '0x0000000000000000000000000000000000000000000000000000000000000000', | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| srcAmount: amount, | ||
| slippage: 50, | ||
| }); | ||
|
|
||
| // on-chain pre-sign transaction | ||
| const tx = await deltaSDK.preSignExternalDeltaOrder(signableOrderData); | ||
| await tx.wait(); | ||
|
|
||
| // post with empty signature | ||
| const deltaAuction = await deltaSDK.postExternalDeltaOrder({ | ||
| order: signableOrderData.data, | ||
| signature: '0x', | ||
| }); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| #### A more detailed example can be found in [examples/externalDelta](../src/examples/externalDelta.ts) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||
| import axios from 'axios'; | ||
| import { ethers, Wallet } from 'ethersV5'; | ||
| import { | ||
| constructPartialSDK, | ||
| constructEthersContractCaller, | ||
| constructAxiosFetcher, | ||
| constructAllDeltaOrdersHandlers, | ||
| SwapSide, | ||
| } from '..'; | ||
| import { startStatusCheck } from './helpers/delta'; | ||
|
|
||
| const fetcher = constructAxiosFetcher(axios); | ||
|
|
||
| const provider = ethers.getDefaultProvider(1); // Ethereum | ||
| const signer = Wallet.createRandom().connect(provider); | ||
| const account = signer.address; | ||
| const contractCaller = constructEthersContractCaller( | ||
| { | ||
| ethersProviderOrSigner: signer, | ||
| EthersContract: ethers.Contract, | ||
| }, | ||
| account | ||
| ); | ||
|
|
||
| const deltaSDK = constructPartialSDK( | ||
| { | ||
| chainId: 1, // Ethereum | ||
| fetcher, | ||
| contractCaller, | ||
| }, | ||
| constructAllDeltaOrdersHandlers | ||
| ); | ||
|
|
||
| // Ethereum tokens | ||
| const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; | ||
| const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; | ||
|
|
||
| // Aave external handler on Ethereum (https://etherscan.io/address/0xb4a2c36668cf8b19fe08f263e3685a5e16e82912#code) | ||
| // The handler contract is provided by the integrator and must implement IExternalProtocolHandler. | ||
| // Different handlers serve different purposes and may have different prerequisites | ||
| // (e.g. token approvals, credit delegation, position setup). | ||
| const AAVE_HANDLER = '0xb4a2c36668cf8b19fe08f263e3685a5e16e82912'; | ||
|
|
||
| // Aave-specific order types passed as `data` field. | ||
| // The `data` encoding is handler-specific — each handler defines its own format. | ||
| const AaveOrderTypes = { | ||
| COLLATERAL_SWAP: | ||
| '0x0000000000000000000000000000000000000000000000000000000000000000', | ||
| DEBT_SWAP: | ||
| '0x0000000000000000000000000000000000000000000000000000000000000001', | ||
| REPAY_WITH_COLLATERAL: | ||
| '0x0000000000000000000000000000000000000000000000000000000000000002', | ||
| }; | ||
|
|
||
| // Aave Collateral Swap: swap one collateral asset for another (SELL side) | ||
| // Prerequisites: user must approve the source aToken to the handler | ||
| async function collateralSwapFlow() { | ||
| const amount = ethers.utils.parseUnits('1', 6).toString(); // 1 USDC | ||
|
|
||
| const deltaPrice = await deltaSDK.getDeltaPrice({ | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| amount, | ||
| userAddress: account, | ||
| srcDecimals: 6, | ||
| destDecimals: 18, | ||
| side: SwapSide.SELL, | ||
| }); | ||
|
|
||
| const signableOrderData = await deltaSDK.buildExternalDeltaOrder({ | ||
| deltaPrice, | ||
| owner: account, | ||
| handler: AAVE_HANDLER, | ||
| data: AaveOrderTypes.COLLATERAL_SWAP, | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| srcAmount: amount, | ||
| slippage: 50, // 0.5% slippage in bps | ||
| }); | ||
|
|
||
| const signature = await deltaSDK.signExternalDeltaOrder(signableOrderData); | ||
|
|
||
| const deltaAuction = await deltaSDK.postExternalDeltaOrder({ | ||
| order: signableOrderData.data, | ||
| signature, | ||
| }); | ||
|
|
||
| startStatusCheck(() => deltaSDK.getDeltaOrderById(deltaAuction.id)); | ||
| } | ||
|
|
||
| // Aave Debt Swap: swap one debt for another (BUY side) | ||
| // Prerequisites: user must grant borrowAllowance on the source variable debt token to the handler | ||
| async function debtSwapFlow() { | ||
| const debtAmount = ethers.utils.parseUnits('1', 6).toString(); // amount of debt to swap | ||
|
|
||
| const deltaPrice = await deltaSDK.getDeltaPrice({ | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| amount: debtAmount, | ||
| userAddress: account, | ||
| srcDecimals: 6, | ||
| destDecimals: 18, | ||
| side: SwapSide.BUY, | ||
| }); | ||
|
|
||
| const signableOrderData = await deltaSDK.buildExternalDeltaOrder({ | ||
| deltaPrice, | ||
| owner: account, | ||
| handler: AAVE_HANDLER, | ||
| data: AaveOrderTypes.DEBT_SWAP, | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| destAmount: debtAmount, | ||
| slippage: 50, // 0.5% slippage in bps | ||
| }); | ||
|
|
||
| const signature = await deltaSDK.signExternalDeltaOrder(signableOrderData); | ||
|
|
||
| const deltaAuction = await deltaSDK.postExternalDeltaOrder({ | ||
| order: signableOrderData.data, | ||
| signature, | ||
| }); | ||
|
|
||
| startStatusCheck(() => deltaSDK.getDeltaOrderById(deltaAuction.id)); | ||
| } | ||
|
|
||
| // Aave Repay with Collateral: use collateral to repay debt (BUY side) | ||
| // Prerequisites: user must approve the source aToken to the handler | ||
| async function repayWithCollateralFlow() { | ||
| const collateralAmount = ethers.utils.parseUnits('1', 6).toString(); | ||
|
|
||
| const deltaPrice = await deltaSDK.getDeltaPrice({ | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| amount: collateralAmount, | ||
| userAddress: account, | ||
| srcDecimals: 6, | ||
| destDecimals: 18, | ||
| side: SwapSide.BUY, | ||
| }); | ||
|
|
||
| const signableOrderData = await deltaSDK.buildExternalDeltaOrder({ | ||
| deltaPrice, | ||
| owner: account, | ||
| handler: AAVE_HANDLER, | ||
| data: AaveOrderTypes.REPAY_WITH_COLLATERAL, | ||
| srcToken: USDC, | ||
| destToken: WETH, | ||
| destAmount: collateralAmount, | ||
| slippage: 50, // 0.5% slippage in bps | ||
| }); | ||
|
|
||
| const signature = await deltaSDK.signExternalDeltaOrder(signableOrderData); | ||
|
|
||
| const deltaAuction = await deltaSDK.postExternalDeltaOrder({ | ||
| order: signableOrderData.data, | ||
| signature, | ||
| }); | ||
|
|
||
| startStatusCheck(() => deltaSDK.getDeltaOrderById(deltaAuction.id)); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.