Add option to buy or sell eth#730
Conversation
✅ Deploy Preview for pendulum-pay ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
# Conflicts: # apps/api/src/api/services/ramp/quote.service/gross-output.ts # apps/api/src/api/services/ramp/quote.service/index.ts # apps/api/src/api/services/transactions/squidrouter/route.ts # apps/frontend/src/components/AssetNumericInput/index.tsx # apps/frontend/src/components/NumericInput/index.tsx # packages/shared/src/tokens/evm/config.ts # packages/shared/src/tokens/types/evm.ts # packages/shared/src/tokens/utils/helpers.ts
…nd hard minimum output
| } catch (error) { | ||
| logger.error("Error creating quote:", error); | ||
| logger.error( | ||
| `Error creating quote: ${error && typeof error === "object" && "message" in error ? error.message : String(error)}` |
There was a problem hiding this comment.
Can we just use here error instanceof Error ? instead of error && typeof error === "object" && "message" in error ?
gianfra-t
left a comment
There was a problem hiding this comment.
Given that you tested this already on the routes, seems all good!
| } | ||
| } | ||
|
|
||
| export async function getEvmBridgeQuote(request: EvmBridgeQuoteRequest) { |
There was a problem hiding this comment.
Currently, the function handles two distinct responsibilities:
- Preparing the route request (gathering token details, formatting the amount, building params)
- Processing the route response (extracting output amounts, calculating fees)
We could extract them into separate functions to make the context more clear.
For example:
export async function getEvmBridgeQuote(request: EvmBridgeQuoteRequest) {
const routeParams = buildRouteRequest(request);
return await getSquidrouterRouteData(routeResponse);
}
async function getSquidrouterRouteData(routeParams) {
const result = await getSquidrouterRouteData(routeParams);
const outputTokenDecimals = routeResponse.route.estimate.toToken.decimals;
const outputAmountRaw = routeResponse.route.estimate.toAmount;
const outputAmountDecimal = parseContractBalanceResponse(outputTokenDecimals, BigInt(outputAmountRaw)).preciseBigDecimal;
const networkFeeUSD = await calculateSquidrouterNetworkFee(routeResponse);
return {
networkFeeUSD,
outputAmountDecimal,
};
}
function buildRouteRequest(request: EvmBridgeQuoteRequest) {
const token = getTokenDetailsForEvmDestination(request.inputOrOutputCurrency, request.sourceOrDestination);
const amountRaw = multiplyByPowerOfTen(request.amountDecimal, token.decimals).toFixed(0, 0);
return prepareSquidrouterRouteParams(request.rampType, amountRaw, token, request.sourceOrDestination);
}
wdyt?
| ); | ||
|
|
||
| const inputAmountForNablaSwap = new Big(request.inputAmount).minus(preNablaDeductibleFeeInInputCurrency); | ||
| let inputAmountForNablaSwap = new Big(request.inputAmount).minus(preNablaDeductibleFeeInInputCurrency); |
There was a problem hiding this comment.
Thinking of moving the inputAmountForNablaSwap logic into its own function so we can use const instead of let. It should make the code a bit cleaner and safer - wdyt?
async function calculateInputAmountForNablaSwap(request: RequestType, preNablaDeductibleFeeInInputCurrency: Big, preNablaDeductibleFeeAmount: Big) {
if (request.rampType === "off" && request.from !== "assethub") {
// Check squidrouter rate and adjust the input amount accordingly
const bridgeQuote = await getEvmBridgeQuote({
amountDecimal: request.inputAmount,
inputOrOutputCurrency: request.inputCurrency as OnChainToken,
rampType: request.rampType,
sourceOrDestination: request.from
});
return new Big(bridgeQuote.outputAmountDecimal).minus(preNablaDeductibleFeeAmount);
} else {
return new Big(request.inputAmount).minus(preNablaDeductibleFeeInInputCurrency);
}
}
// Usage:
const inputAmountForNablaSwap = await calculateInputAmountForNablaSwap(
request,
preNablaDeductibleFeeInInputCurrency,
preNablaDeductibleFeeAmount
);
| if (!isNetworkEVM(network)) { | ||
| throwInvalidNetworkError(network); | ||
| } | ||
| network = network as EvmNetworks; |
There was a problem hiding this comment.
why do we do the type assertion here?
There was a problem hiding this comment.
Thanks for pointing it out. Made me realize that we have two types for this, EVMNetworks and EvmNetworks. I cleaned it up now.
| })); | ||
| } | ||
|
|
||
| selectedNetwork = selectedNetwork as EvmNetworks; |
There was a problem hiding this comment.
why do we do the type assertion here?
we could just do it like: evmTokenConfig[selectedNetwork as EvmNetworks]
| onChange?: (e: ChangeEvent) => void; | ||
| } | ||
|
|
||
| function trimToMaxDecimals(value: string, decimals: number) { |
There was a problem hiding this comment.
already defined in the NumericInput/helpers.ts
export function trimToMaxDecimals(value: string, maxDecimals: number): string {
const [integer, decimal] = value.split(".");
return decimal ? ${integer}.${decimal.slice(0, maxDecimals)} : value;
}
| } | ||
|
|
||
| function handleOnPaste(e: ClipboardEvent): void { | ||
| function handleOnPaste(e: ClipboardEvent<HTMLInputElement>): void { |
There was a problem hiding this comment.
All these functions are already defined in the NumericInput/helpers.ts
There was a problem hiding this comment.
True, but interesting we weren't even using them before 😅
|
|
||
| // Helper function to format exchange rate strings | ||
| function formatExchangeRateString(rate: number, input: string, output: string) { | ||
| return `1 ${input} ≈ ${rate.toFixed(4)} ${output}`; |
There was a problem hiding this comment.
Instead of using many conditions, could we just use roundDownToSignificantDecimals?
// Round a number to a specified number of decimals
// If the number is small, ensure that the required number of significant
// decimals is retained
// e.g., if decimals = 2, then
// - 12345 -> 12345
// - 12.345 -> 12.34
// - 1.2345 -> 1.23
// - 0.012345 -> 0.012
// - 0.00012345 -> 0.00012
export function roundDownToSignificantDecimals(big: BigNumber, decimals: number) {
return big.prec(Math.max(0, big.e + 1) + decimals, 0);
}
Sharqiewicz
left a comment
There was a problem hiding this comment.
Overall, it looks good 👍 Just in some files I was wondering if we can optimize some things
| return balances[0]; | ||
|
|
||
| // Filter out native tokens since this hook specifically expects OnChainTokenDetails | ||
| const onChainBalance = balances.find( |
There was a problem hiding this comment.
Why do we add these checks if we are sure in the array - there is only one token we requested for?
| import { useVortexAccount } from "./useVortexAccount"; | ||
|
|
||
| // Hook to get EVM native token balance | ||
| export const useEvmNativeBalance = (nativeToken: EvmTokenDetails | undefined): EvmTokenDetailsWithBalance | null => { |
There was a problem hiding this comment.
I am wondering:
Why do we create these two new functions instead of just using useEvmBalances and useAssetHubBalances
Or if we want to have the native functions, why do we support passing tokens if there is always only one native token? wdyt
There was a problem hiding this comment.
I decided to pass the token because the functions itself return XXXTokenDetails and not just a balance. And i didn't want to duplicate the filtering we are already doing in the useOnchainTokenBalances function. I can change it though, so that the functions for the native balances can be called in isolation.
| } | ||
|
|
||
| // This type is used to represent all networks that can be used as a source or destination in the system. | ||
| export type EvmNetworks = |
There was a problem hiding this comment.
We already have this EvmNetworks type but it was not exported from the file:
type EVMNetworks = Exclude<Networks, Networks.AssetHub>;
There was a problem hiding this comment.
True, I missed that. I decided to stick to the other type though where the EvmNetworks are handpicked. This way, we can easily add more other networks to the Networks enum without them accidentally becoming available on the EvmNetworks enum too (which would be the case if we use an Omit for this type).
…ction and simplify onPaste handling
…move nativeToken parameter and derive it internally
ebma
left a comment
There was a problem hiding this comment.
I think I addressed all your comments @Sharqiewicz, please have another look.
| } | ||
|
|
||
| // This type is used to represent all networks that can be used as a source or destination in the system. | ||
| export type EvmNetworks = |
There was a problem hiding this comment.
True, I missed that. I decided to stick to the other type though where the EvmNetworks are handpicked. This way, we can easily add more other networks to the Networks enum without them accidentally becoming available on the EvmNetworks enum too (which would be the case if we use an Omit for this type).
This pull request introduces several updates across controllers, routes, services, and phase handlers to improve error handling, logging, and parameter consistency. The most significant changes include enhancing error messages, updating route definitions, standardizing token details parameters, and improving logging for better debugging and traceability.
Error Handling Improvements:
quote.controller.tsto include detailed error messages when creating quotes.squid-router-pay-phase-handler.tsby replacingconsole.errorwithlogger.errorfor better logging consistency.Route and Controller Updates:
updateRamproute to remove therampIdparameter from the URL and moved it to the request body for consistency. [1] [2]updateRampcontroller to reflect the removal oframpIdfrom the URL, simplifying the request structure.Token Details Standardization:
PendulumDetailswithPendulumTokenDetailsthroughoutoutAmount.tsand related handlers for more precise parameter naming. [1] [2] [3] [4] [5]currencyIdinstead ofpendulumCurrencyId) to align with the new naming convention. [1] [2] [3]Logging Enhancements:
pendulum-moonbeam-phase-handler.tsto trace XCM transfer progress and state transitions.squid-router-pay-phase-handler.tsto provide better insights into Axelar gas funding and route fetching. [1] [2]Miscellaneous Fixes:
didInputTokenArrivedOnPendulumtodidInputTokenArriveOnPendulum) for grammatical correctness and clarity. [1] [2]initial-phase-handler.tsto simplify error conditions.Closes #719.