Skip to content

Commit

Permalink
feat: integrate ibc send to interchain agent
Browse files Browse the repository at this point in the history
  • Loading branch information
Hemanthghs committed Sep 25, 2024
1 parent f9051ae commit add8bcc
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 59 deletions.
39 changes: 31 additions & 8 deletions frontend/src/components/interchain-agent/InterchainAgentDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,45 @@ interface InterchainAgentDialogProps {

/* eslint-disable @typescript-eslint/no-explicit-any */

function parseTransaction(input: string): { type: string; data: any } | null {
const regex = /^(\w+)\s+(\d+(?:\.\d+)?)\s+(\w+)\s+to\s+([a-zA-Z0-9]+)$/i;
const match = input.match(regex);
function parseTransaction(input: string): { type: string; data: any } | null {
// Regex for "send <amount> <denom> to <address> from <chainID>"
const regexWithChainID = /^(\w+)\s+(\d+(?:\.\d+)?)\s+(\w+)\s+to\s+([a-zA-Z0-9]+)\s+from\s+([\w-]+)$/i;

// Regex for "send <amount> <denom> to <address>"
const regexWithoutChainID = /^(\w+)\s+(\d+(?:\.\d+)?)\s+(\w+)\s+to\s+([a-zA-Z0-9]+)$/i;

let match;

// First, check for the regex with chainID
match = input.match(regexWithChainID);
if (match) {
const [, typeWithChainID, amountWithChainID, denomWithChainID, addressWithChainID, chainID] = match;
return {
type: typeWithChainID,
data: {
amount: amountWithChainID,
denom: denomWithChainID.toUpperCase(),
address: addressWithChainID,
chainID: chainID,
},
};
}

// Then, check for the regex without chainID
match = input.match(regexWithoutChainID);
if (match) {
const [, type, amount, denom, address] = match;
const [, typeWithoutChainID, amountWithoutChainID, denomWithoutChainID, addressWithoutChainID] = match;
return {
type: type,
type: typeWithoutChainID,
data: {
amount: amount,
denom: denom.toUpperCase(),
address,
amount: amountWithoutChainID,
denom: denomWithoutChainID.toUpperCase(),
address: addressWithoutChainID,
},
};
}

// If no pattern is matched, return null
return null;
}

Expand Down
186 changes: 150 additions & 36 deletions frontend/src/custom-hooks/interchain-agent/useTransactions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import useGetChainInfo from '../useGetChainInfo';
import { SendMsg } from '@/txns/bank';
import { useAppDispatch, useAppSelector } from '../StateHooks';
import { txGeneric } from '@/store/features/common/commonSlice';
import {
resetGenericTxStatus,
resetTxAndHash,
setGenericTxStatus,
txGeneric,
} from '@/store/features/common/commonSlice';
import { TxStatus } from '@/types/enums';
import { addSessionItem } from '@/store/features/interchain-agent/agentSlice';
import { getTxnURLOnResolute } from '@/utils/util';
import { Delegate } from '@/txns/staking';
import useGetTxInputs from '../useGetTxInputs';
import {
txTransfer,
resetTxStatus as resetIBCTxStatus,
} from '@/store/features/ibc/ibcSlice';

const SUPPORTED_TXNS = ['send', 'delegate'];

Expand All @@ -18,74 +28,137 @@ const useTransactions = ({
chatInputTime: string;
}) => {
const dispatch = useAppDispatch();
const { getChainIDByCoinDenom, getDenomInfo, getChainInfo } =
useGetChainInfo();
const {
getChainIDByCoinDenom,
getDenomInfo,
getChainInfo,
getChainIDFromAddress,
} = useGetChainInfo();
const { txTransferInputs } = useGetTxInputs();
const nameToChainIDs = useAppSelector((state) => state.wallet.nameToChainIDs);
const supportedChainIDs = Object.keys(nameToChainIDs).map(
(chainName) => nameToChainIDs[chainName]
);

const [currentChainID, setCurrenChainID] = useState('');
const [currentChainID, setCurrentChainID] = useState('');

const txStatus = useAppSelector((state) => state.common.genericTransaction);
const tx = useAppSelector((state) => state.common.txSuccess.tx);
const currentSessionID = useAppSelector(
(state) => state.agent.currentSessionID
);
const isWalletConnected = useAppSelector((state) => state.wallet.connected);
const ibcTxStatus = useAppSelector((state) => state.ibc.txStatus);
const ibcTxError = useAppSelector((state) => state.ibc.txError);

const validateParsedTxnData = ({
parsedData,
}: {
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-explicit-any */
parsedData: { type: string; data: any };
}) => {
setCurrenChainID('');
setCurrentChainID('');
if (!isWalletConnected) {
return 'Please connect your wallet';
}
const providedChainID = parsedData?.data?.chainID;
if (providedChainID) {
if (supportedChainIDs.includes(providedChainID)) {
setCurrentChainID(providedChainID);
} else {
setCurrentChainID('');
return `Unsupported/Invalid chain ID: ${providedChainID}`;
}
}
const chainID = getChainIDByCoinDenom(parsedData.data.denom);
if (chainID) {
setCurrenChainID(chainID);
if (!providedChainID && chainID) {
setCurrentChainID(chainID);
if (!SUPPORTED_TXNS.includes(parsedData.type)) {
return `Unsupported transaction type ${parsedData.type}`;
}
if (parsedData.type === 'send' || parsedData.type === 'delegate') {
const amount = parseFloat(parsedData.data.amount);
if (isNaN(amount) || amount <= 0) {
return `Invalid amount ${parsedData.data?.amount || ''}`;
}
}
if (!providedChainID && !chainID) {
setCurrentChainID('');
return 'No chains found with given denom';
}
if (parsedData.type === 'send' || parsedData.type === 'delegate') {
const amount = parseFloat(parsedData.data.amount);
if (isNaN(amount) || amount <= 0) {
return `Invalid amount ${parsedData.data?.amount || ''}`;
}
return '';
}
return 'No chains found with given denom';
return '';
};

const initiateTransaction = ({
parsedData,
}: {
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-explicit-any */
parsedData: { type: string; data: any };
}) => {
dispatch(resetIBCTxStatus());
dispatch(resetTxAndHash());
dispatch(resetGenericTxStatus());
const chainID = getChainIDByCoinDenom(parsedData.data.denom);
const basicChainInfo = getChainInfo(chainID);
const { decimals, minimalDenom } = getDenomInfo(chainID);
const providedChainID = parsedData?.data?.chainID;

if (parsedData.type === 'send') {
const toAddress = parsedData.data?.address;
const amount = parseFloat(parsedData.data?.amount);
const msg = SendMsg(
basicChainInfo.address,
toAddress,
amount * 10 ** decimals,
minimalDenom
);
dispatch(
txGeneric({
basicChainInfo,
msgs: [msg],
memo: '',
denom: minimalDenom,
feegranter: '',
})
);
const destChainID = getChainIDFromAddress(parsedData.data?.address);
// Normal send
if (
!providedChainID ||
(providedChainID &&
providedChainID === chainID &&
destChainID === chainID)
) {
const basicChainInfo = getChainInfo(chainID);
const { decimals, minimalDenom } = getDenomInfo(chainID);
const toAddress = parsedData.data?.address;
const amount = parseFloat(parsedData.data?.amount);
const msg = SendMsg(
basicChainInfo.address,
toAddress,
amount * 10 ** decimals,
minimalDenom
);
dispatch(
txGeneric({
basicChainInfo,
msgs: [msg],
memo: '',
denom: minimalDenom,
feegranter: '',
})
);
// IBC Send
} else {
const { decimals, minimalDenom } = getDenomInfo(providedChainID);
const toAddress = parsedData.data?.address;
const amount = parseFloat(parsedData.data?.amount);
const destChainID = getChainIDFromAddress(toAddress);
if (!destChainID) {
dispatch(
setGenericTxStatus({
status: TxStatus.REJECTED,
errMsg: 'Invalid address',
})
);
return;
}
const txInputs = txTransferInputs(
providedChainID,
destChainID,
toAddress,
amount,
minimalDenom,
decimals
);
dispatch(txTransfer(txInputs));
}
}
if (parsedData.type === 'delegate') {
const basicChainInfo = getChainInfo(chainID);
const { decimals, minimalDenom } = getDenomInfo(chainID);
const valAddress = parsedData.data?.address;
const amount = parseFloat(parsedData.data?.amount);
const msg = Delegate(
Expand Down Expand Up @@ -139,6 +212,47 @@ const useTransactions = ({
}
}, [tx, txStatus]);

useEffect(() => {
if (
ibcTxStatus === TxStatus.IDLE &&
tx?.transactionHash &&
userInput?.length
) {
const { chainName } = getChainInfo(currentChainID);
dispatch(
addSessionItem({
request: {
[userInput]: {
errMessage: '',
result: `Transaction successful: [View here](${getTxnURLOnResolute(chainName, tx?.transactionHash || '')})`,
status: 'success',
date: chatInputTime,
},
},
sessionID: currentSessionID,
})
);
dispatch(setGenericTxStatus({ status: TxStatus.IDLE, errMsg: '' }));
} else if (ibcTxStatus === TxStatus.REJECTED && userInput?.length) {
dispatch(
addSessionItem({
request: {
[userInput]: {
errMessage: '',
result: `Transaction failed`,
status: 'failed',
date: chatInputTime,
},
},
sessionID: currentSessionID,
})
);
dispatch(
setGenericTxStatus({ status: TxStatus.REJECTED, errMsg: ibcTxError })
);
}
}, [tx, ibcTxStatus]);

return { validateParsedTxnData, initiateTransaction };
};

Expand Down
12 changes: 10 additions & 2 deletions frontend/src/store/features/common/commonSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ export const txGeneric = createAsyncThunk(
}
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
} catch (error: any) {
console.log("error: ", error);
console.log('error: ', error);
const errMessage = error?.response?.data?.error || error?.message;
dispatch(
setError({
type: 'error',
message: errMessage || ERR_UNKNOWN,
})
);
console.log("erro22", errMessage)
console.log('erro22', errMessage);
return rejectWithValue(errMessage || ERR_UNKNOWN);
}
}
Expand Down Expand Up @@ -196,6 +196,13 @@ export const commonSlice = createSlice({
resetGenericTxStatus: (state) => {
state.genericTransaction = initialState.genericTransaction;
},
setGenericTxStatus: (
state,
action: PayloadAction<{ status: TxStatus; errMsg: string }>
) => {
state.genericTransaction.status = action.payload.status;
state.genericTransaction.errMsg = action.payload.errMsg;
},
},
extraReducers: (builder) => {
builder
Expand Down Expand Up @@ -265,6 +272,7 @@ export const {
setChangeNetworkDialogOpen,
setAddNetworkDialogOpen,
resetGenericTxStatus,
setGenericTxStatus,
} = commonSlice.actions;

export default commonSlice.reducer;
Loading

0 comments on commit add8bcc

Please sign in to comment.