Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions apps/api/src/api/services/moonbeam/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@ export class BalanceCheckError extends Error {
}
}

interface GetBalanceParams {
tokenAddress: `0x${string}`;
ownerAddress: `0x${string}`;
chain: any;
}

export async function getEvmTokenBalance({ tokenAddress, ownerAddress, chain }: GetBalanceParams): Promise<Big> {
try {
const publicClient = createPublicClient({
chain,
transport: http(),
});

const balanceResult = (await publicClient.readContract({
address: tokenAddress,
abi: erc20ABI,
functionName: 'balanceOf',
args: [ownerAddress],
})) as string;

return new Big(balanceResult);
} catch (err: any) {
throw new Error(`Failed to read token balance: ${err.message ?? err}`);
}
}

export function checkEvmBalancePeriodically(
tokenAddress: string,
brlaEvmAddress: string,
Expand Down
21 changes: 21 additions & 0 deletions apps/api/src/api/services/phases/handlers/nabla-approve-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { RampPhase, decodeSubmittableExtrinsic } from '@packages/shared';
import { NABLA_ROUTER } from '@packages/shared';
import { ExecuteMessageResult, createExecuteMessageExtrinsic, submitExtrinsic } from '@pendulum-chain/api-solang';
import { Abi } from '@polkadot/api-contract';
import Big from 'big.js';
import logger from '../../../../config/logger';
import { erc20WrapperAbi } from '../../../../contracts/ERC20Wrapper';
import RampState from '../../../../models/rampState.model';
Expand All @@ -17,6 +19,25 @@ export class NablaApprovePhaseHandler extends BasePhaseHandler {
const networkName = 'pendulum';
const pendulumNode = await apiManager.getApi(networkName);

// Pre check: check if the approve has already been performed.
try {
const approval = await pendulumNode.api.query.tokenAllowance.approvals(
state.state.inputTokenPendulumDetails,
state.state.pendulumEphemeralAddress,
NABLA_ROUTER,
);
const requiredAmount = new Big(state.state.inputAmountBeforeSwapRaw);
const approvedAmount = approval.toString() !== '' ? Big(approval.toString()) : Big(0);
if (approvedAmount.gte(requiredAmount)) {
logger.info(`NablaApprovePhaseHandler: Amount already approved. Skipping approval.`);
return this.transitionToNextPhase(state, 'nablaSwap');
}
} catch (e) {
throw this.createRecoverableError(
`NablaApprovePhaseHandler: Could not check if the approve has already been performed. ${(e as Error).message}`,
);
}

try {
const { txData: nablaApproveTransaction } = this.getPresignedTransaction(state, 'nablaApprove');
// This is a new item that might not be available on old states.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { RampPhase, decodeSubmittableExtrinsic, getAddressForFormat } from '@packages/shared';
import {
AXL_USDC_MOONBEAM,
FiatToken,
RampPhase,
decodeSubmittableExtrinsic,
getAddressForFormat,
getAnyFiatTokenDetailsMoonbeam,
} from '@packages/shared';
import RampState from '../../../../models/rampState.model';
import { BasePhaseHandler } from '../base-phase-handler';

import { submitXTokens } from '../../xcm/send';

import Big from 'big.js';
import { moonbeam } from 'viem/chains';
import { getEvmTokenBalance } from '../../moonbeam/balance';
import { ApiManager } from '../../pendulum/apiManager';
import { submitXTokens } from '../../xcm/send';
import { StateMetadata } from '../meta-state-types';

export class PendulumToMoonbeamXCMPhaseHandler extends BasePhaseHandler {
Expand All @@ -14,16 +23,33 @@ export class PendulumToMoonbeamXCMPhaseHandler extends BasePhaseHandler {

protected async executePhase(state: RampState): Promise<RampState> {
const apiManager = ApiManager.getInstance();
const networkName = 'pendulum';
const pendulumNode = await apiManager.getApi(networkName);
const pendulumNode = await apiManager.getApi('pendulum');

const { pendulumEphemeralAddress } = state.state as StateMetadata;
const { pendulumEphemeralAddress, moonbeamEphemeralAddress, outputAmountBeforeFinalStep, outputTokenType } =
state.state as StateMetadata;

if (!pendulumEphemeralAddress) {
throw new Error('Pendulum ephemeral address is not defined in the state. This is a bug.');
if (!pendulumEphemeralAddress || !moonbeamEphemeralAddress) {
throw new Error('Ephemeral address(es) not defined in the state. This is a bug.');
}

const didInputTokenArrivedOnMoonbeam = async () => {
// Token is always either axlUSDC or BRL.
const tokenAddress =
state.type === 'off' ? getAnyFiatTokenDetailsMoonbeam(FiatToken.BRL).moonbeamErc20Address : AXL_USDC_MOONBEAM;
const balance = await getEvmTokenBalance({
tokenAddress: tokenAddress as `0x${string}`,
ownerAddress: moonbeamEphemeralAddress as `0x${string}`,
chain: moonbeam,
});

return balance.gte(Big(outputAmountBeforeFinalStep.raw));
};

try {
if (await didInputTokenArrivedOnMoonbeam()) {
return this.transitionToNextPhase(state, this.nextPhaseSelector(state));
}

const { txData: pendulumToMoonbeamTransaction } = this.getPresignedTransaction(state, 'pendulumToMoonbeam');

if (typeof pendulumToMoonbeamTransaction !== 'string') {
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/tokens/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { assetHubTokenConfig } from '../assethub/config';
import { evmTokenConfig } from '../evm/config';
import { moonbeamTokenConfig } from '../moonbeam/config';
import { stellarTokenConfig } from '../stellar/config';
import { FiatToken, OnChainToken, RampCurrency } from '../types/base';
import { FiatToken, OnChainToken, PendulumDetails, RampCurrency } from '../types/base';
import { AssetHubToken } from '../types/base';
import { EvmToken } from '../types/evm';
import { MoonbeamTokenDetails } from '../types/moonbeam';
Expand Down Expand Up @@ -115,7 +115,7 @@ export function getPendulumCurrencyId(fiatToken: FiatToken) {
/**
* Get Pendulum details for a token
*/
export function getPendulumDetails(tokenType: RampCurrency, network?: Networks) {
export function getPendulumDetails(tokenType: RampCurrency, network?: Networks): PendulumDetails {
const tokenDetails = isFiatTokenEnum(tokenType)
? getAnyFiatTokenDetails(tokenType)
: network
Expand Down
Loading