Skip to content

Commit d179cc6

Browse files
authored
Merge pull request #506 from curvefi/feat/add-swapCalldata-for-router
feat: add swapCalldata for router
2 parents 8f60e07 + a760c78 commit d179cc6

File tree

6 files changed

+97
-5
lines changed

6 files changed

+97
-5
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,17 @@ import curve from "@curvefi/api";
181181
// '0xb0cada2a2983dc0ed85a26916d32b9caefe45fecde47640bd7d0e214ff22aed3',
182182
// '0x00ea7d827b3ad50ce933e96c579810cd7e70d66a034a86ec4e1e10005634d041'
183183
// ]
184+
185+
// Get populated approve transactions (without executing)
186+
const approveTxs = await curve.populateApprove(["DAI", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], ['1000', '1000'], spender);
187+
// OR with custom user address (for API):
188+
// await curve.populateApprove(["DAI", "USDC"], ['1000', '1000'], spender, false, '0x...userAddress');
189+
// Returns array of TransactionLike objects (may include reset to 0 if needed for some tokens)
190+
console.log(approveTxs);
191+
// [
192+
// { to: '0x6B17...', data: '0x095ea7b3...', hash: '0x...', ... },
193+
// { to: '0xA0b8...', data: '0x095ea7b3...', hash: '0x...', ... }
194+
// ]
184195
})()
185196
```
186197
@@ -1191,6 +1202,13 @@ import curve from "@curvefi/api";
11911202
// [
11921203
// '0xc111e471715ae6f5437e12d3b94868a5b6542cd7304efca18b5782d315760ae5'
11931204
// ]
1205+
1206+
// Get populated transactions for approve (without executing)
1207+
const approveTxs = await curve.router.populateApprove('DAI', 1000, false, userAddress);
1208+
// OR const approveTxs = await curve.router.populateApprove('0x6B175474E89094C44Da98b954EedeAC495271d0F', 1000, false, userAddress);
1209+
console.log(approveTxs);
1210+
// [{ to: '0x6B17...', data: '0x...', ... }]
1211+
// Returns array of TransactionLike objects
11941212
const swapTx = await curve.router.swap('DAI', 'CRV', '1000');
11951213
// OR const swapTx = await curve.router.swap('0x6B175474E89094C44Da98b954EedeAC495271d0F', '0xD533a949740bb3306d119CC777fa900bA034cd52', '1000');
11961214
console.log(swapTx.hash);
@@ -1200,6 +1218,16 @@ import curve from "@curvefi/api";
12001218
12011219
await curve.getBalances(['DAI', 'CRV']);
12021220
// [ '8900.0', '100428.626463428100672494' ]
1221+
1222+
// Get calldata for swap (without executing transaction)
1223+
// First, you need to call getBestRouteAndOutput to cache the route
1224+
await curve.router.getBestRouteAndOutput('DAI', 'CRV', '1000');
1225+
1226+
// Then get calldata
1227+
const { data, to, from, amount } = await curve.router.populateSwap('DAI', 'CRV', '1000', 0.5);
1228+
// OR const tx = await curve.router.populateSwap('0x6B175474E89094C44Da98b954EedeAC495271d0F', '0xD533a949740bb3306d119CC777fa900bA034cd52', '1000', 0.5);
1229+
console.log(data);
1230+
// 0x8f726f1c000000000000000000000000...
12031231
})()
12041232
```
12051233

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@curvefi/api",
3-
"version": "2.68.17",
3+
"version": "2.68.18",
44
"description": "JavaScript library for curve.finance",
55
"main": "lib/index.js",
66
"author": "Macket",

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import {
1616
swapIsApproved,
1717
swapApproveEstimateGas,
1818
swapApprove,
19+
swapPopulateApprove,
1920
swapEstimateGas,
2021
swap,
22+
populateSwap,
2123
getSwappedAmount,
2224
} from "./router.js";
2325
import { Curve } from "./curve.js";
@@ -62,6 +64,7 @@ import {
6264
hasAllowance,
6365
ensureAllowanceEstimateGas,
6466
ensureAllowance,
67+
populateApprove,
6568
getUsdRate,
6669
getGasPriceFromL1,
6770
getGasPriceFromL2,
@@ -175,6 +178,7 @@ export const createCurve = () => {
175178
getAllowance: getAllowance.bind(_curve),
176179
hasAllowance: hasAllowance.bind(_curve),
177180
ensureAllowance: ensureAllowance.bind(_curve),
181+
populateApprove: populateApprove.bind(_curve),
178182
getCoinsData: getCoinsData.bind(_curve),
179183
getVolume: getVolume.bind(_curve),
180184
hasDepositAndStake: hasDepositAndStake.bind(_curve),
@@ -352,7 +356,9 @@ export const createCurve = () => {
352356
priceImpact: swapPriceImpact.bind(_curve),
353357
isApproved: swapIsApproved.bind(_curve),
354358
approve: swapApprove.bind(_curve),
359+
populateApprove: swapPopulateApprove.bind(_curve),
355360
swap: swap.bind(_curve),
361+
populateSwap: populateSwap.bind(_curve),
356362
getSwappedAmount: getSwappedAmount.bind(_curve),
357363
estimateGas: {
358364
approve: swapApproveEstimateGas.bind(_curve),

src/router.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import BigNumber from "bignumber.js";
2-
import {ethers} from "ethers";
2+
import {ethers, TransactionLike} from "ethers";
33
import {type Curve, OLD_CHAINS} from "./curve.js";
44
import {IChainId, IDict, IRoute, IRouteOutputAndCost, IRouteStep} from "./interfaces";
55
import {
@@ -8,6 +8,7 @@ import {
88
_get_small_x,
99
_getCoinAddresses,
1010
_getCoinDecimals,
11+
_getAllowance,
1112
_getUsdRate,
1213
BN,
1314
DIGas,
@@ -20,6 +21,7 @@ import {
2021
hasAllowance,
2122
isEth,
2223
parseUnits,
24+
populateApprove,
2325
runWorker,
2426
smartNumber,
2527
toBN,
@@ -406,6 +408,10 @@ export async function swapApprove(this: Curve, inputCoin: string, amount: number
406408
return await ensureAllowance.call(this, [inputCoin], [amount], this.constants.ALIASES.router);
407409
}
408410

411+
export async function swapPopulateApprove(this: Curve, inputCoin: string, amount: number | string, isMax = true, userAddress: string): Promise<TransactionLike[]> {
412+
return await populateApprove.call(this, [inputCoin], [amount], this.constants.ALIASES.router, isMax, userAddress);
413+
}
414+
409415
export async function swapEstimateGas(this: Curve, inputCoin: string, outputCoin: string, amount: number | string): Promise<number | number[]> {
410416
const [inputCoinAddress, outputCoinAddress] = _getCoinAddresses.call(this, inputCoin, outputCoin);
411417
const [inputCoinDecimals] = _getCoinDecimals.call(this, inputCoinAddress, outputCoinAddress);
@@ -459,6 +465,30 @@ export async function swap(this: Curve, inputCoin: string, outputCoin: string, a
459465
}
460466
}
461467

468+
export async function populateSwap(this: Curve, inputCoin: string, outputCoin: string, amount: number | string, slippage = 0.5): Promise<TransactionLike> {
469+
console.log(inputCoin, outputCoin, amount, slippage);
470+
const [inputCoinAddress, outputCoinAddress] = _getCoinAddresses.call(this, inputCoin, outputCoin);
471+
const [inputCoinDecimals, outputCoinDecimals] = _getCoinDecimals.call(this, inputCoinAddress, outputCoinAddress);
472+
473+
const { route, output } = _getBestRouteAndOutput.call(this, inputCoinAddress, outputCoinAddress, amount);
474+
475+
if (route.length === 0) {
476+
throw new Error("This pair can't be exchanged");
477+
}
478+
479+
const { _route, _swapParams, _pools } = _getExchangeArgs.call(this, route);
480+
const _amount = parseUnits(amount, inputCoinDecimals);
481+
const minRecvAmountBN: BigNumber = BN(output).times(100 - slippage).div(100);
482+
const _minRecvAmount = fromBN(minRecvAmountBN, outputCoinDecimals);
483+
484+
const contract = this.contracts[this.constants.ALIASES.router].contract;
485+
return await contract.exchange.populateTransaction(...[
486+
_route, _swapParams, _amount, _minRecvAmount,
487+
..._pools ? [_pools] : [],
488+
{ value: isEth(inputCoinAddress) ? _amount : this.parseUnits("0") },
489+
])
490+
}
491+
462492
export async function getSwappedAmount(this: Curve, tx: ethers.ContractTransactionResponse, outputCoin: string): Promise<string> {
463493
const [outputCoinAddress] = _getCoinAddresses.call(this, outputCoin);
464494
const [outputCoinDecimals] = _getCoinDecimals.call(this, outputCoinAddress);

src/utils.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Contract, ethers} from 'ethers';
1+
import {Contract, ethers, TransactionLike} from 'ethers';
22
import {Contract as MulticallContract} from "@curvefi/ethcall";
33
import BigNumber from 'bignumber.js';
44
import {
@@ -310,6 +310,34 @@ export async function ensureAllowance(this: Curve, coins: string[], amounts: (nu
310310
return await _ensureAllowance.call(this, coinAddresses, _amounts, spender, isMax)
311311
}
312312

313+
export async function populateApprove(this: Curve, coins: string[], amounts: (number | string)[], spender: string, isMax = true, userAddress?: string): Promise<TransactionLike[]> {
314+
const coinAddresses = _getCoinAddresses.call(this, coins);
315+
const decimals = _getCoinDecimals.call(this, coinAddresses);
316+
const _amounts = amounts.map((a, i) => parseUnits(a, decimals[i]));
317+
318+
const address = userAddress || this.signerAddress;
319+
if (!address) throw Error("User address is not defined. Pass userAddress parameter.");
320+
321+
const allowance = await _getAllowance.call(this, coinAddresses, address, spender);
322+
323+
const transactions: TransactionLike[] = [];
324+
325+
for (let i = 0; i < allowance.length; i++) {
326+
if (allowance[i] < _amounts[i]) {
327+
const contract = this.contracts[coinAddresses[i]].contract;
328+
const _approveAmount = isMax ? MAX_ALLOWANCE : _amounts[i];
329+
330+
if (allowance[i] > parseUnits("0")) {
331+
transactions.push(await contract.approve.populateTransaction(spender, parseUnits("0")));
332+
}
333+
334+
transactions.push(await contract.approve.populateTransaction(spender, _approveAmount));
335+
}
336+
}
337+
338+
return transactions;
339+
}
340+
313341
export function getPoolIdBySwapAddress(this: Curve, swapAddress: string): string {
314342
const poolsData = this.getPoolsData();
315343
const poolIds = Object.entries(poolsData).filter(([, poolData]) => poolData.swap_address.toLowerCase() === swapAddress.toLowerCase());

0 commit comments

Comments
 (0)