Pendle Hosted SDK
Pendle accommodates a vast array of assets, each characterized by its unique nuances and complexities. While the Pendle protocol remains immutable, the underlying assets don't share this feature, requiring our app and SDK to be updated frequently to align with changes in these assets.
To address this, Pendle has introduced a hosted version of our SDK. It ensures the output remains consistent with Pendle's UI and keeps up-to-date with the latest protocol changes. The API design prioritizes simplicity and stability, with a high rate limit to meet the needs of most users.
Supported functions
- Swap
- Add liquidity
- Add liquidity ZPI
- Remove liquidity
- Mint PT & YT
- Redeem PT & YT
- Transfer liquidity
- Transfer liquidity ZPI
- Roll over PT
- Add liquidity dual
- Remove liquidity dual
- Mint SY
- Redeem SY
All actions above can be accessed via one universal Convert API.
Examples
Swap
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&tokensOut=0xf99985822fb361117fcf3768d34a6353e6022f5f&amountsIn=1000000000&enableAggregator=true&aggregators=kyberswap&additionalData=impliedApy,effectiveApy
In code:
export async function swapTokenToPt() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${USDC_ADDRESS}`,
amountsIn: 1000000000,
tokensOut: `${PT_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
enableAggregator: true,
aggregators: "kyberswap",
additionalData: "impliedApy,effectiveApy",
});
}
Add liquidity
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&tokensOut=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2&amountsIn=1000000000000000000
In code:
export async function addLiquiditySingleToken() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${WSTETH_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${MARKET_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Add liquidity ZPI
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&tokensOut=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2,0xf3abc972a0f537c1119c990d422463b93227cd83&amountsIn=1000000000000000000
In code:
export async function addLiquiditySingleTokenKeepYt() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${WSTETH_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${MARKET_ADDRESS},${YT_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Remove liquidity
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2&tokensOut=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&amountsIn=1000000000000000000&enableAggregator=true
In code:
export async function removeLiquiditySingleToken() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${MARKET_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${WSTETH_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
enableAggregator: true,
});
}
Mint PT & YT
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&tokensOut=0xf99985822fb361117fcf3768d34a6353e6022f5f,0xf3abc972a0f537c1119c990d422463b93227cd83&amountsIn=1000000000000000000
In code:
export async function mintPyFromToken() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${WSTETH_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${PT_ADDRESS},${YT_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Redeem PT & YT
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0xf99985822fb361117fcf3768d34a6353e6022f5f,0xf3abc972a0f537c1119c990d422463b93227cd83&tokensOut=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&amountsIn=1000000000000000000,1000000000000000000
In code:
export async function redeemPyToToken() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${PT_ADDRESS},${YT_ADDRESS}`,
amountsIn: "1000000000000000000,1000000000000000000",
tokensOut: `${WSTETH_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Transfer liquidity
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x9bc2fb257e00468fe921635fe5a73271f385d0eb,0x21aace56a8f21210b7e76d8ef1a77253db85bf0a,0x3787c19c32e727310708c0693aec00fb37a01e7b&tokensOut=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2&amountsIn=1000000000000000000,1000000000000000000,1000000000000000000&enableAggregator=true&aggregators=kyberswap
In code:
export async function transferLiquidity() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${FXSAVE_MARKET_ADDRESS},${FXSAVE_PT_ADDRESS},${FXSAVE_YT_ADDRESS}`,
amountsIn: "1000000000000000000,1000000000000000000,1000000000000000000",
tokensOut: `${MARKET_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
enableAggregator: true,
aggregators: "kyberswap",
});
}
Transfer liquidity ZPI
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x9bc2fb257e00468fe921635fe5a73271f385d0eb,0x21aace56a8f21210b7e76d8ef1a77253db85bf0a,0x3787c19c32e727310708c0693aec00fb37a01e7b&tokensOut=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2,0xf3abc972a0f537c1119c990d422463b93227cd83&amountsIn=1000000000000000000,1000000000000000000,1000000000000000000&enableAggregator=true&aggregators=kyberswap
In code:
export async function transferLiquidityKeepYt() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${FXSAVE_MARKET_ADDRESS},${FXSAVE_PT_ADDRESS},${FXSAVE_YT_ADDRESS}`,
amountsIn: "1000000000000000000,1000000000000000000,1000000000000000000",
tokensOut: `${MARKET_ADDRESS},${YT_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
enableAggregator: true,
aggregators: "kyberswap",
});
}
Roll over PT
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x21aace56a8f21210b7e76d8ef1a77253db85bf0a&tokensOut=0xf99985822fb361117fcf3768d34a6353e6022f5f&amountsIn=1000000000000000000&enableAggregator=true
In code:
export async function rollOverPt() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${FXSAVE_PT_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${PT_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
enableAggregator: true,
});
}
Add liquidity dual
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0xcbc72d92b2dc8187414f6734718563898740c0bc,0xf99985822fb361117fcf3768d34a6353e6022f5f&tokensOut=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2&amountsIn=1000000000000000000,1000000000000000000
In code:
export async function addLiquidityDualSyAndPt() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${SY_ADDRESS},${PT_ADDRESS}`,
amountsIn: "1000000000000000000,1000000000000000000",
tokensOut: `${MARKET_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Remove liquidity dual
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0xc374f7ec85f8c7de3207a10bb1978ba104bda3b2&tokensOut=0xf99985822fb361117fcf3768d34a6353e6022f5f,0xcbc72d92b2dc8187414f6734718563898740c0bc&amountsIn=1000000000000000000
In code:
export async function removeLiquidityDualSyAndPt() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${MARKET_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${PT_ADDRESS},${SY_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Mint SY
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&tokensOut=0xcbc72d92b2dc8187414f6734718563898740c0bc&amountsIn=1000000000000000000
In code:
export async function mintSyFromToken() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${WSTETH_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${SY_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Redeem SY
GET https://api-v2.pendle.finance/core/v2/sdk/1/convert?receiver=<RECEIVER_ADDRESS>&slippage=0.01&tokensIn=0xcbc72d92b2dc8187414f6734718563898740c0bc&tokensOut=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0&amountsIn=1000000000000000000
In code:
export async function redeemSyToToken() {
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${SY_ADDRESS}`,
amountsIn: "1000000000000000000",
tokensOut: `${WSTETH_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
});
}
Send Transaction
After calling any of the Convert API functions above, you can inspect the response and send the transaction:
const res: ConvertResponse;
// Log the action and outputs
console.log("Action: ", res.action);
console.log("Outputs: ", res.routes[0].outputs);
// Send the transaction
getSigner().sendTransaction(res.routes[0].tx);
Please visit our Convert API demo to see more detailed examples.
Inputs
The Convert API accepts the following input parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tokensIn | string | Yes | - | Array of input token addresses (comma-separated) |
amountsIn | string | Yes | - | Array of input token amounts in wei (comma-separated) |
tokensOut | string | Yes | - | Array of expected output token addresses (comma-separated) |
receiver | string | Yes | - | Address to receive the output tokens |
slippage | number | Yes | - | Maximum slippage tolerance (0-1, where 0.01 = 1%) |
enableAggregator | boolean | No | false | Enable swap aggregators for token conversions |
aggregators | string | No | - | Specific aggregators to use (comma-separated), see Routing |
redeemRewards | boolean | No | false | Whether to redeem rewards during the action, applicable to actions: transfer-liquidity |
needScale | boolean | No | false | Aggregator needScale parameter, only set to true when amounts are updated on-chain. When enabled, please make sure to buffer the input amount by about 2%, applicable to actions: swap |
additionalData | string | No | - | Additional data to include in response (comma-separated), see Additional data |
Outputs
The Convert API returns the following response structure:
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | The classified and executed action (e.g., mint-py, swap, add-liquidity) |
inputs | TokenAmountResponse[] | Yes | Input tokens and amounts used in the action |
requiredApprovals | TokenAmountResponse[] | No | Tokens requiring approval before execution |
routes | ConvertResponse[] | Yes | Array of route execution details, sorted by decresing output amount |
TokenAmountResponse
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Token contract address (lowercase) |
amount | string | Yes | Token amount in wei (BigInt string) |
ConvertResponse
| Field | Type | Required | Description |
|---|---|---|---|
contractParamInfo | ContractParamInfo | Yes | Param info for the contract method called |
tx | TransactionDto | Yes | Complete transaction data for execution |
outputs | TokenAmountResponse[] | Yes | Expected output tokens and amounts |
data | ConvertData | Yes | Action-specific data |
ContractParamInfo
| Field | Type | Required | Description |
|---|---|---|---|
method | string | Yes | Contract method name (e.g., mintPyFromToken) |
contractCallParamsName | string[] | Yes | Parameter names array |
contractCallParams | any[] | Yes | Parameter values array |
TransactionDto
| Field | Type | Required | Description |
|---|---|---|---|
data | string | Yes | Encoded transaction data (hex) |
to | string | Yes | Contract address to call |
from | string | Yes | Sender address |
value | string | Yes | Native token amount to send |
ConvertData
| Field | Type | Required | Description |
|---|---|---|---|
aggregatorType | string | Yes | The aggregator used, or VOID if none was used |
priceImpact | number | Yes | Price impact |
impliedApy | ImpliedApy | No | Market APY information for yield actions. (for swap actions) |
effectiveApy | number | No | User's effective APY after fees/slippage. (for swap actions) |
paramsBreakdown | ParamsBreakdown | No | Multi-step action breakdown (for transfer-liquidity) |
ImpliedApy
| Field | Type | Required | Description |
|---|---|---|---|
before | number | Yes | Implied APY before transaction |
after | number | Yes | Implied APY after transaction |
ParamsBreakdown
| Field | Type | Required | Description |
|---|---|---|---|
selfCall1 | ContractParamInfo | Yes | Params info for selfCall1 |
selfCall2 | ContractParamInfo | No | Params info for selfCall2 |
reflectCall | ContractParamInfo | Yes | Params info for reflectCall |
Features
Routing
Routing is a feature of the Pendle SDK that helps users find the most optimal route to interact with the Pendle system. This feature ensures that users can efficiently execute their transactions by identifying the best paths for their specific needs, whether it's swapping assets, adding or removing liquidity, or any other supported function.
To take advantage of the routing feature, users need to set the enableAggregator option to true. When this option is enabled, the system will automatically perform routing to find the most optimal route for the user's transaction. This ensures that users always get the best possible outcome when interacting with the Pendle system.
Select aggregators
When enableAggregator is set to true, you can control which aggregators are used by specifying the aggregators parameter. This parameter accepts a comma-separated list of aggregator names (e.g. "kyberswap,odos"). If not specified, the system will use all available aggregators to find the optimal route.
Using more aggregators generally results in better optimized routes since there are more options to choose from. However, each aggregator adds to the computing unit cost of the request (see rate limiting). You can balance between optimization and cost by selectively enabling only the aggregators you want to use.
Currently supported aggregators (can be fetched from fetch supported aggregators endpoint), this list is subject to change:
| Aggregator | Cost (Computing Units) |
|---|---|
kyberswap | 5 |
odos | 15 |
okx | 10 |
paraswap | 15 |
Example, this request will use KyberSwap and Odos aggregators to find the optimal route, and it cost 25 computing units (5 from kyberswap, 15 from odos, and 5 from the base computing cost):
export async function addLiquiditySingleToken() {
// Use 1 ETH to add liquidity to stETH pool with 1% slippage
const res = await callSDK<ConvertResponse>(`/v2/sdk/${CHAIN_ID}/convert`, {
tokensIn: `${ETH_ADDRESS}`,
amountsIn: '1000000000000000000',
tokensOut: `${STETH_LP_ADDRESS}`,
receiver: RECEIVER_ADDRESS,
slippage: 0.01,
enableAggregator: true,
aggregators: "kyberswap,odos",
});
console.log("Action: ", res.action);
console.log("Outputs: ", res.routes[0].outputs);
console.log("Price impact: ", res.routes[0].data.priceImpact);
// Send tx
getSigner().sendTransaction(res.routes[0].tx);
}
Reduce aggregator computing units
By default, when using aggregators, the system will use public API keys for each aggregator. This means that the computing unit cost for each aggregator will be added to the total cost of the request. To reduce the computing unit cost, you can use your own API key for each aggregator. This way, the computing unit cost for that aggregator will be reduced to 0. For example, if you use your own API key for Odos, the total computing unit cost will be 5 (base cost) + 5 (kyberswap cost) + 0 (odos cost with custom API key) = 10 computing units.
Currently you can use aggregator with your own API key by specifying specific field in the request headers.
| Aggregator | Header Key | Note |
|---|---|---|
kyberswap | KYBERSWAP-API-KEY | |
odos | ODOS-API-KEY | |
okx | OKX-ACCESS-KEY, OKX-ACCESS-SECRET, OKX-PASSPHRASE | Need all three to return a valid response |
paraswap | PARASWAP-API-KEY |
When using custom aggregators, please make sure to include the required headers in your request. If the headers are not provided, the system will default to using the public API for that aggregator.
Additional data
When an endpoint has an additionalData field, users can pass in some fields to receive more data, but it will cost more computing units.
For example, the swap action has additionalData with two available fields: impliedApy and effectiveApy. If the query parameters have additionalData=impliedApy, the response will have the implied APY before and after the swap action.
For additional usage, please refer to our external swagger to explore more.