Skip to content
Draft
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"version": "8.5.0",
"author": "J M Rossy",
"dependencies": {
"@bootnodedev/intents-framework-core": "^0.1.0",
"@chakra-ui/next-js": "^2.4.2",
"@chakra-ui/react": "^2.8.2",
"@cosmjs/cosmwasm-stargate": "^0.32.4",
Expand Down
172 changes: 170 additions & 2 deletions src/consts/warpRoutes.ts
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

an example file for testing purposes, this change should be removed before merging.

Original file line number Diff line number Diff line change
@@ -1,9 +1,177 @@
import { WarpCoreConfig } from '@hyperlane-xyz/sdk';
import { TokenStandard, WarpCoreConfig } from '@hyperlane-xyz/sdk';

const ROUTER = '0x6d2175B89315A9EB6c7eA71fDE54Ac0f294aDC34';
const ITT = '0x5f94BC7Fb4A2779fef010F96b496cD36A909E818';
Comment on lines +3 to +4
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have this in the const/config.ts or const/args.ts file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, if you want to keep these in the codebase.

My idea was to leave this file untocuched after the PR feedback (i.e.: roll back these changes).

My intention was to share some implementation reference to help on the feedback.


// A list of Warp Route token configs
// These configs will be merged with the warp routes in the configured registry
// The input here is typically the output of the Hyperlane CLI warp deploy command
export const warpRouteConfigs: WarpCoreConfig = {
tokens: [],
tokens: [
{
addressOrDenom: ROUTER,
chainName: 'optimismsepolia',
collateralAddressOrDenom: ITT,
connections: [
{
token: 'ethereum|basesepolia|' + ITT,
},
{
token: 'ethereum|arbitrumsepolia|' + ITT,
},
{
token: 'ethereum|sepolia|' + ITT,
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ITT',
standard: TokenStandard.EvmIntent,
symbol: 'ITT',
},
{
addressOrDenom: ROUTER,
chainName: 'basesepolia',
collateralAddressOrDenom: ITT,
connections: [
{
token: 'ethereum|optimismsepolia|' + ITT,
},
{
token: 'ethereum|arbitrumsepolia|' + ITT,
},
{
token: 'ethereum|sepolia|' + ITT,
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ITT',
standard: TokenStandard.EvmIntent,
symbol: 'ITT',
},
{
addressOrDenom: ROUTER,
chainName: 'arbitrumsepolia',
collateralAddressOrDenom: ITT,
connections: [
{
token: 'ethereum|optimismsepolia|' + ITT,
},
{
token: 'ethereum|basesepolia|' + ITT,
},
{
token: 'ethereum|sepolia|' + ITT,
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ITT',
standard: TokenStandard.EvmIntent,
symbol: 'ITT',
},
{
addressOrDenom: ROUTER,
chainName: 'sepolia',
collateralAddressOrDenom: ITT,
connections: [
{
token: 'ethereum|optimismsepolia|' + ITT,
},
{
token: 'ethereum|arbitrumsepolia|' + ITT,
},
{
token: 'ethereum|basesepolia|' + ITT,
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ITT',
standard: TokenStandard.EvmIntent,
symbol: 'ITT',
},
{
addressOrDenom: ROUTER,
chainName: 'optimismsepolia',
connections: [
{
token: 'ethereum|basesepolia|',
},
{
token: 'ethereum|arbitrumsepolia|',
},
{
token: 'ethereum|sepolia|',
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ETH',
standard: TokenStandard.EvmIntentNative,
symbol: 'ETH',
},
{
addressOrDenom: ROUTER,
chainName: 'basesepolia',
connections: [
{
token: 'ethereum|optimismsepolia|',
},
{
token: 'ethereum|arbitrumsepolia|',
},
{
token: 'ethereum|sepolia|',
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ETH',
standard: TokenStandard.EvmIntentNative,
symbol: 'ETH',
},
{
addressOrDenom: ROUTER,
chainName: 'arbitrumsepolia',
connections: [
{
token: 'ethereum|optimismsepolia|',
},
{
token: 'ethereum|basesepolia|',
},
{
token: 'ethereum|sepolia|',
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ETH',
standard: TokenStandard.EvmIntentNative,
symbol: 'ETH',
},
{
addressOrDenom: ROUTER,
chainName: 'sepolia',
connections: [
{
token: 'ethereum|optimismsepolia|',
},
{
token: 'ethereum|arbitrumsepolia|',
},
{
token: 'ethereum|basesepolia|',
},
],
decimals: 18,
logoURI: '/deployments/warp_routes/ETH/logo.svg',
name: 'ETH',
standard: TokenStandard.EvmIntentNative,
symbol: 'ETH',
},
],
options: {},
};
4 changes: 3 additions & 1 deletion src/features/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface AppState {
updateTransferStatus: (
i: number,
s: TransferStatus,
options?: { msgId?: string; originTxHash?: string },
options?: { msgId?: string; originTxHash?: string; orderId?: string; remoteTxHash?: string },
) => void;
failUnconfirmedTransfers: () => void;

Expand Down Expand Up @@ -114,6 +114,8 @@ export const useStore = create<AppState>()(
txs[i].status = s;
txs[i].msgId ||= options?.msgId;
txs[i].originTxHash ||= options?.originTxHash;
txs[i].orderId ||= options?.orderId;
txs[i].remoteTxHash ||= options?.remoteTxHash;
Comment on lines +117 to +118
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two extra attributes added to the tx information. This will help to track the intent completion and to host the transfer information on the destination chain.

image

return {
transfers: txs,
};
Expand Down
13 changes: 12 additions & 1 deletion src/features/tokens/TokenListModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TokenIcon } from '../../components/icons/TokenIcon';
import { TextInput } from '../../components/input/TextField';
import { config } from '../../consts/config';
import InfoIcon from '../../images/icons/info-circle.svg';
import { isIntentStandard } from '../../utils/intents';
import { useMultiProvider } from '../chains/hooks';
import { getChainDisplayName } from '../chains/utils';
import { useWarpCore } from './hooks';
Expand Down Expand Up @@ -143,12 +144,22 @@ export function TokenList({
</div>
<div className="ml-2 min-w-0 shrink text-left">
<div className="w-full truncate text-xs">
{t.token.collateralAddressOrDenom || t.token.addressOrDenom || 'Native chain token'}
{(isIntentStandard(t.token.standard)
? t.token.collateralAddressOrDenom
: t.token.addressOrDenom) ?? 'Native chain token'}
</div>
<div className="mt-0.5 flex space-x-1 text-xs">
<span>{`Decimals: ${t.token.decimals}`}</span>
<span>-</span>
<span>{`Chain: ${getChainDisplayName(multiProvider, t.token.chainName)}`}</span>
{isIntentStandard(t.token.standard) && (
<>
<span>-</span>
<span>
<i>via intents</i>
</span>
</>
)}
Comment on lines +155 to +162
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's an intent, we let the user know (this is useful in the case that the same token can be bridged via WR or intents)

</div>
</div>
{t.disabled && (
Expand Down
47 changes: 46 additions & 1 deletion src/features/tokens/balances.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Hyperlane7683__factory as Hyperlane7683Factory } from '@bootnodedev/intents-framework-core';
import { IToken, MultiProtocolProvider, Token } from '@hyperlane-xyz/sdk';
import { isValidAddress } from '@hyperlane-xyz/utils';
import { assert, isValidAddress } from '@hyperlane-xyz/utils';
import { useAccountAddressForChain } from '@hyperlane-xyz/widgets';
import { useQuery } from '@tanstack/react-query';
import { toast } from 'react-toastify';
Expand Down Expand Up @@ -86,3 +87,47 @@ export function useEvmWalletBalance(

return { balance: data, isError, isLoading };
}
export async function checkOrderFilled({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

polls for the destination chain Filling

destination,
orderId,
originToken,
multiProvider,
}: {
destination: ChainName;
orderId: string;
originToken: Token;
multiProvider: MultiProtocolProvider;
}): Promise<string> {
const provider = multiProvider.toMultiProvider().getProvider(destination);
const connection = originToken.getConnectionForChain(destination);

assert(connection?.token.addressOrDenom, 'No connection found for destination chain');

const contract = Hyperlane7683Factory.connect(connection.token.addressOrDenom, provider);
const filter = contract.filters.Filled();

const BLOCKS_TO_CHECK = 10;
const BLOCK_CHECK_INTERVAL = 4_000;

return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
try {
const to = await provider.getBlockNumber();
const from = to - BLOCKS_TO_CHECK;
const events = await contract.queryFilter(filter, from, to);

for (const event of events) {
const resolvedOrder = event.args.orderId;

if (resolvedOrder === orderId) {
clearInterval(intervalId);
resolve(event.transactionHash);
}
}
} catch (error) {
clearInterval(intervalId);
reject(error);
}
}, BLOCK_CHECK_INTERVAL);
});
}
16 changes: 12 additions & 4 deletions src/features/transfer/TransferTokenForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { TextField } from '../../components/input/TextField';
import { WARP_QUERY_PARAMS } from '../../consts/args';
import { config } from '../../consts/config';
import { Color } from '../../styles/Color';
import { isIntentStandard } from '../../utils/intents';
import { logger } from '../../utils/logger';
import { getQueryParams, updateQueryParam } from '../../utils/queryParams';
import { ChainConnectionWarning } from '../chains/ChainConnectionWarning';
Expand Down Expand Up @@ -469,12 +470,18 @@ function ReviewDetails({ visible }: { visible: boolean }) {
</div>
)}
<div>
<h4>{`Transaction${isApproveRequired ? ' 2' : ''}: Transfer Remote`}</h4>
<h4>{`Transaction${isApproveRequired ? ' 2' : ''}:
${destinationToken && isIntentStandard(destinationToken.standard) ? 'Open Order' : 'Transfer Remote'}
`}</h4>
<div className="ml-1.5 mt-1.5 space-y-1.5 border-l border-gray-300 pl-2 text-xs">
{destinationToken?.addressOrDenom && (
<p className="flex">
<span className="min-w-[6.5rem]">Remote Token</span>
<span>{destinationToken.addressOrDenom}</span>
<span>
{isIntentStandard(destinationToken.standard)
? (destinationToken.collateralAddressOrDenom ?? 'Native chain token')
: destinationToken.addressOrDenom}
</span>
</p>
)}
<p className="flex">
Expand All @@ -484,15 +491,15 @@ function ReviewDetails({ visible }: { visible: boolean }) {
{fees?.localQuote && fees.localQuote.amount > 0n && (
<p className="flex">
<span className="min-w-[6.5rem]">Local Gas (est.)</span>
<span>{`${fees.localQuote.getDecimalFormattedAmount().toFixed(4) || '0'} ${
<span>{`${fees.localQuote.getDecimalFormattedAmount().toFixed(18) || '0'} ${
fees.localQuote.token.symbol || ''
}`}</span>
</p>
)}
{fees?.interchainQuote && fees.interchainQuote.amount > 0n && (
<p className="flex">
<span className="min-w-[6.5rem]">Interchain Gas</span>
<span>{`${fees.interchainQuote.getDecimalFormattedAmount().toFixed(4) || '0'} ${
<span>{`${fees.interchainQuote.getDecimalFormattedAmount().toFixed(18) || '0'} ${
fees.interchainQuote.token.symbol || ''
}`}</span>
</p>
Expand Down Expand Up @@ -577,6 +584,7 @@ async function validateForm(
origin,
accounts,
);

const result = await warpCore.validateTransfer({
originTokenAmount: token.amount(amountWei),
destination,
Expand Down
Loading