Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Deposit/Withdrawal) Fetch Bridge Provider Quote #3402

Merged
merged 40 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4f805ca
feat: display immersive bridge assets
JoseRFelix Jun 19, 2024
252e0d0
feat: display selected asset
JoseRFelix Jun 20, 2024
b0f1c8a
Merge remote-tracking branch 'origin/stage' into jose/fe-453-ui-canon…
JoseRFelix Jun 20, 2024
9b462f8
feat: add skeleton loader to amount screen
JoseRFelix Jun 20, 2024
681c4e3
feat: handle back button, and improve amount input
JoseRFelix Jun 20, 2024
c1136b0
fix: pagination test
JoseRFelix Jun 20, 2024
2947d22
Merge remote-tracking branch 'origin/stage' into jose/fe-453-ui-canon…
JoseRFelix Jun 20, 2024
39124f2
fix: type
JoseRFelix Jun 20, 2024
2b8924c
fix: more bridge options
JoseRFelix Jun 20, 2024
f653001
improvement: @jonator feedback
JoseRFelix Jun 24, 2024
9135ad1
improvement: @jonator feedback
JoseRFelix Jun 24, 2024
566e72a
feat: aggregate chains and assets
JoseRFelix Jun 24, 2024
1162d59
feat: merge assets by chain id
JoseRFelix Jun 25, 2024
84e92fa
feat: aggregate assets display them initially on amount screen
JoseRFelix Jun 25, 2024
2e8524e
feat: add local bridge transfer router
JoseRFelix Jun 26, 2024
1e60d72
feat: use supported asset as source asset
JoseRFelix Jun 27, 2024
bb6245b
improvement: type fix
JoseRFelix Jun 27, 2024
acdccec
feat: handle wallets in immersive bridge
JoseRFelix Jun 27, 2024
11e6be5
feat: handle connection edge cases
JoseRFelix Jun 27, 2024
27f7677
feat: set up wallet connection from immersive bridge
JoseRFelix Jun 28, 2024
63570fa
feat: create initial use bridge quote hook
JoseRFelix Jun 28, 2024
c1ceb71
Merge remote-tracking branch 'origin/stage' into jose/fe-453-ui-canon…
JoseRFelix Jun 28, 2024
f29a97b
improvement: @jonator feedback
JoseRFelix Jun 28, 2024
2868eba
feat: remove source denom from minimal asset
JoseRFelix Jun 28, 2024
8be9aa4
test: update snapshots
JoseRFelix Jun 28, 2024
3d5bb06
Merge remote-tracking branch 'origin/jose/fe-453-ui-canonical-asset-s…
JoseRFelix Jun 28, 2024
b838ad5
Merge remote-tracking branch 'origin/stage' into jose/fe-453-ui-canon…
JoseRFelix Jun 28, 2024
0fdeb42
Merge remote-tracking branch 'origin/jose/fe-453-ui-canonical-asset-s…
JoseRFelix Jun 28, 2024
8981144
feat: receive quote from input
JoseRFelix Jun 29, 2024
fb38d8e
fix: build
JoseRFelix Jun 29, 2024
42873d2
Merge remote-tracking branch 'origin/stage' into jose/fe-575-fetch-br…
JoseRFelix Jun 29, 2024
3564c49
feat: display quote remaining time
JoseRFelix Jun 29, 2024
3a42b4b
feat: add provider details
JoseRFelix Jun 30, 2024
9e9541a
test: update snapshot
JoseRFelix Jun 30, 2024
ce88df4
fix: translation
JoseRFelix Jun 30, 2024
0b80ced
feat: add bridge provider dropdown
JoseRFelix Jul 1, 2024
ad185a5
feat: add details icons and tooltips
JoseRFelix Jul 1, 2024
55007b6
fix build
jonator Jul 1, 2024
28192b8
fix tests & build
jonator Jul 1, 2024
1f01db5
feat: add amount and confirmation screen
JoseRFelix Jul 1, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {
AxelarQueryAPI,
} from "@axelar-network/axelarjs-sdk";
import { estimateGasFee } from "@osmosis-labs/tx";
import { NativeEVMTokenConstantAddress } from "@osmosis-labs/utils";
import { CacheEntry } from "cachified";
import { LRUCache } from "lru-cache";
// eslint-disable-next-line import/no-extraneous-dependencies
import { rest } from "msw";

import { MockAssetLists } from "../../__tests__/mock-asset-lists";
import { server } from "../../__tests__/msw";
import { NativeEVMTokenConstantAddress } from "../../ethereum";
import { BridgeProviderContext } from "../../interface";
import { AxelarBridgeProvider } from "../index";
import {
Expand Down
3 changes: 2 additions & 1 deletion packages/bridge/src/axelar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { ibcProtoRegistry } from "@osmosis-labs/proto-codecs";
import { estimateGasFee } from "@osmosis-labs/tx";
import type { IbcTransferMethod } from "@osmosis-labs/types";
import {
EthereumChainInfo,
getAssetFromAssetList,
getKeyByValue,
isNil,
NativeEVMTokenConstantAddress,
} from "@osmosis-labs/utils";
import { cachified } from "cachified";
import {
Expand All @@ -23,7 +25,6 @@ import {
} from "viem";

import { BridgeQuoteError } from "../errors";
import { EthereumChainInfo, NativeEVMTokenConstantAddress } from "../ethereum";
import {
BridgeAsset,
BridgeChain,
Expand Down
6 changes: 3 additions & 3 deletions packages/bridge/src/axelar/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SourceChain } from "../chain";
import { EthereumChainInfo } from "../ethereum";
import { AxelarSourceChain, EthereumChainInfo } from "@osmosis-labs/utils";

Copy link
Contributor

Choose a reason for hiding this comment

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

Add @deprecated tag for the import.

The import should be marked as deprecated to indicate that it will be removed in the future.

- import { AxelarSourceChain, EthereumChainInfo } from "@osmosis-labs/utils";
+/** @deprecated Prefer using Axelar chain/asset list API via bridge providers instead */
+import { AxelarSourceChain, EthereumChainInfo } from "@osmosis-labs/utils";
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { AxelarSourceChain, EthereumChainInfo } from "@osmosis-labs/utils";
/** @deprecated Prefer using Axelar chain/asset list API via bridge providers instead */
import { AxelarSourceChain, EthereumChainInfo } from "@osmosis-labs/utils";

import { BridgeEnvironment } from "../interface";

export type SourceChainTokenConfig = {
/** Source Chain identifier. */
Copy link
Member

Choose a reason for hiding this comment

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

Let's add @deprecated here as we don't need it after we commit to new d/w flow

I thought I did this in a prev PR

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I see that it has been added after merging with stage

id: SourceChain;
id: AxelarSourceChain;
chainId?: number;
/** Address of origin ERC20 token for that origin chain. Leave blank to
* prefer native ETH currency if `id` is not a Cosmos chain in `ChainInfo`.
Expand Down
22 changes: 0 additions & 22 deletions packages/bridge/src/chain.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/bridge/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from "./axelar";
export * from "./bridge-providers";
export * from "./chain";
export * from "./errors";
export * from "./interface";
export * from "./skip";
Expand Down
9 changes: 6 additions & 3 deletions packages/bridge/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ const evmChainSchema = z.object({
chainType: z.literal("evm"),
});

const bridgeChainSchema = z.union([cosmosChainSchema, evmChainSchema]);
export const bridgeChainSchema = z.discriminatedUnion("chainType", [
cosmosChainSchema,
evmChainSchema,
]);

export type BridgeChain = z.infer<typeof bridgeChainSchema>;

Expand All @@ -154,7 +157,7 @@ export interface BridgeStatus {
maintenanceMessage?: string;
}

const bridgeAssetSchema = z.object({
export const bridgeAssetSchema = z.object({
/**
* The denomination of the asset.
*/
Expand All @@ -176,7 +179,7 @@ const bridgeAssetSchema = z.object({

export type BridgeAsset = z.infer<typeof bridgeAssetSchema>;

const bridgeSupportedAssetsSchema = z.object({
export const bridgeSupportedAssetsSchema = z.object({
/**
* The originating chain information.
*/
Expand Down
12 changes: 8 additions & 4 deletions packages/bridge/src/skip/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { CoinPretty } from "@keplr-wallet/unit";
import { ibcProtoRegistry } from "@osmosis-labs/proto-codecs";
import { estimateGasFee } from "@osmosis-labs/tx";
import { CosmosCounterparty, EVMCounterparty } from "@osmosis-labs/types";
import { isNil } from "@osmosis-labs/utils";
import {
EthereumChainInfo,
isNil,
NativeEVMTokenConstantAddress,
} from "@osmosis-labs/utils";
import cachified from "cachified";
import {
Address,
Expand All @@ -19,7 +23,6 @@ import {
} from "viem";

import { BridgeQuoteError } from "../errors";
import { EthereumChainInfo, NativeEVMTokenConstantAddress } from "../ethereum";
import {
BridgeAsset,
BridgeChain,
Expand Down Expand Up @@ -331,6 +334,7 @@ export class SkipBridgeProvider implements BridgeProvider {
...chainInfo,
address: sharedOriginAsset.denom,
denom:
sharedOriginAsset.recommended_symbol ??
sharedOriginAsset.symbol ??
sharedOriginAsset.name ??
sharedOriginAsset.denom,
Expand Down Expand Up @@ -773,9 +777,9 @@ export class SkipBridgeProvider implements BridgeProvider {

const url = new URL("https://ibc.fun/");
url.searchParams.set("src_chain", String(fromChain.chainId));
url.searchParams.set("src_asset", fromAsset.address);
url.searchParams.set("src_asset", fromAsset.address.toLowerCase());
url.searchParams.set("dest_chain", String(toChain.chainId));
url.searchParams.set("dest_asset", toAsset.address);
url.searchParams.set("dest_asset", toAsset.address.toLowerCase());

return { urlProviderName: "IBC.fun", url };
}
Expand Down
9 changes: 7 additions & 2 deletions packages/bridge/src/squid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
} from "@0xsquid/sdk";
import { CoinPretty, Dec } from "@keplr-wallet/unit";
import { CosmosCounterparty, EVMCounterparty } from "@osmosis-labs/types";
import { apiClient, ApiClientError, isNil } from "@osmosis-labs/utils";
import {
apiClient,
ApiClientError,
EthereumChainInfo,
isNil,
NativeEVMTokenConstantAddress,
} from "@osmosis-labs/utils";
import { cachified } from "cachified";
import Long from "long";
import {
Expand All @@ -21,7 +27,6 @@ import {
} from "viem";

import { BridgeQuoteError } from "../errors";
import { EthereumChainInfo, NativeEVMTokenConstantAddress } from "../ethereum";
import {
BridgeAsset,
BridgeChain,
Expand Down
3 changes: 2 additions & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"jest-util": "^29.7.0",
"lru-cache": "^10.0.1",
"superjson": "^2.2.1",
"zod": "^3.22.4"
"zod": "^3.22.4",
"viem": "2.13.3"
},
"devDependencies": {
"@types/jest-in-case": "^1.0.6",
Expand Down
133 changes: 132 additions & 1 deletion packages/server/src/queries/complex/assets/__tests__/assets.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AssetLists as MockAssetLists } from "../../../__tests__/mock-asset-lists";
import { getAsset, getAssets } from "../index";
import { getAsset, getAssets, getAssetWithVariants } from "../index";

describe("getAssets", () => {
describe("search", () => {
Expand Down Expand Up @@ -85,3 +85,134 @@ describe("getAsset", () => {
).toThrow();
});
});

describe("getAssetWithVariants", () => {
it("should return the asset with its variants, with the canonical asset first", () => {
const result = getAssetWithVariants({
assetLists: MockAssetLists,
anyDenom: "USDC",
});

expect(result).toMatchInlineSnapshot(`
[
{
"coinDecimals": 6,
"coinDenom": "USDC",
"coinGeckoId": "usd-coin",
"coinImageUrl": "/tokens/generated/usdc.svg",
"coinMinimalDenom": "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4",
"coinName": "USDC",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "uusdc",
"variantGroupKey": "USDC",
},
{
"coinDecimals": 6,
"coinDenom": "USDC.axl",
"coinGeckoId": "axlusdc",
"coinImageUrl": "/tokens/generated/usdc.axl.svg",
"coinMinimalDenom": "ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858",
"coinName": "USD Coin (Axelar)",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "uusdc",
"variantGroupKey": "USDC",
},
{
"coinDecimals": 6,
"coinDenom": "polygon.USDC.axl",
"coinGeckoId": "usd-coin",
"coinImageUrl": "/tokens/generated/polygon.usdc.axl.svg",
"coinMinimalDenom": "ibc/231FD77ECCB2DB916D314019DA30FE013202833386B1908A191D16989AD80B5A",
"coinName": "USD Coin (Polygon)",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "polygon-uusdc",
"variantGroupKey": "USDC",
},
{
"coinDecimals": 6,
"coinDenom": "avalanche.USDC.axl",
"coinGeckoId": "usd-coin",
"coinImageUrl": "/tokens/generated/avalanche.usdc.axl.svg",
"coinMinimalDenom": "ibc/F17C9CA112815613C5B6771047A093054F837C3020CBA59DFFD9D780A8B2984C",
"coinName": "USD Coin (Avalanche)",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "avalanche-uusdc",
"variantGroupKey": "USDC",
},
{
"coinDecimals": 6,
"coinDenom": "USDC.grv",
"coinGeckoId": "gravity-bridge-usdc",
"coinImageUrl": "/tokens/generated/usdc.grv.svg",
"coinMinimalDenom": "ibc/9F9B07EF9AD291167CF5700628145DE1DEB777C2CFC7907553B24446515F6D0E",
"coinName": "USDC (Gravity Bridge)",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "gravity0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"variantGroupKey": "USDC",
},
{
"coinDecimals": 6,
"coinDenom": "USDC.wh",
"coinGeckoId": undefined,
"coinImageUrl": "/tokens/generated/usdc.wh.svg",
"coinMinimalDenom": "ibc/6B99DB46AA9FF47162148C1726866919E44A6A5E0274B90912FD17E19A337695",
"coinName": "USD Coin (Wormhole)",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/GGh9Ufn1SeDGrhzEkMyRKt5568VbbxZK2yvWNsd6PbXt",
"variantGroupKey": "USDC",
},
{
"coinDecimals": 6,
"coinDenom": "solana.USDC.wh",
"coinGeckoId": undefined,
"coinImageUrl": "/tokens/generated/solana.usdc.wh.svg",
"coinMinimalDenom": "ibc/F08DE332018E8070CC4C68FE06E04E254F527556A614F5F8F9A68AF38D367E45",
"coinName": "Solana USD Coin (Wormhole)",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/HJk1XMDRNUbRrpKkNZYui7SwWDMjXZAsySzqgyNcQoU3",
"variantGroupKey": "USDC",
},
]
`);
});

it("should throw an error if the asset is not found", () => {
expect(() =>
getAssetWithVariants({
assetLists: MockAssetLists,
anyDenom: "unotexist",
})
).toThrow("unotexist not found in asset list");
});

it("Should still return assets even if they have no variants", () => {
expect(
getAssetWithVariants({
assetLists: MockAssetLists,
anyDenom: "ATOM",
})
).toMatchInlineSnapshot(`
[
{
"coinDecimals": 6,
"coinDenom": "ATOM",
"coinGeckoId": "cosmos",
"coinImageUrl": "/tokens/generated/atom.svg",
"coinMinimalDenom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
"coinName": "Cosmos Hub",
"isUnstable": false,
"isVerified": true,
"sourceDenom": "uatom",
"variantGroupKey": "ATOM",
},
]
`);
});
});
10 changes: 6 additions & 4 deletions packages/server/src/queries/complex/assets/bridge.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Asset as AssetListAsset, AssetList } from "@osmosis-labs/types";

import { Asset } from ".";
import {
Asset as AssetListAsset,
AssetList,
MinimalAsset,
} from "@osmosis-labs/types";

/** A bridgeable asset. */
export type BridgeAsset = {
Expand All @@ -11,7 +13,7 @@ export type BridgeAsset = {
/** Appends bridge info to a given asset. If asset is not found in asset list, empty bridge info will be returned.
* @throws if a given asset is not found in asset list.
*/
export function getBridgeAsset<TAsset extends Asset>(
export function getBridgeAsset<TAsset extends MinimalAsset>(
assetLists: AssetList[],
asset: TAsset
): TAsset & BridgeAsset {
Expand Down
40 changes: 40 additions & 0 deletions packages/server/src/queries/complex/assets/ethereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
EthereumChainInfo,
NativeEVMTokenConstantAddress,
} from "@osmosis-labs/utils";
import { Address, createPublicClient, erc20Abi, http } from "viem";

export async function getEvmBalance({
address,
userAddress,
chainId,
}: {
address: string;
userAddress: string;
chainId: number;
}) {
Comment on lines +7 to +15
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve type safety for function parameters.

The function parameters can be improved by defining a TypeScript interface for the input object.

interface GetEvmBalanceParams {
  address: string;
  userAddress: string;
  chainId: number;
}

export async function getEvmBalance({
  address,
  userAddress,
  chainId,
}: GetEvmBalanceParams) {
  // function body
}

const evmChain = Object.values(EthereumChainInfo).find(
(chain) => String(chain.id) === String(chainId)
);

if (!evmChain) {
throw new Error(`Chain with id ${chainId} not found`);
}
Comment on lines +20 to +22
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure consistent error messages.

The error message should include more context to help with debugging.

- throw new Error(`Chain with id ${chainId} not found`);
+ throw new Error(`Ethereum chain with id ${chainId} not found in EthereumChainInfo`);
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!evmChain) {
throw new Error(`Chain with id ${chainId} not found`);
}
if (!evmChain) {
throw new Error(`Ethereum chain with id ${chainId} not found in EthereumChainInfo`);
}


const publicClient = createPublicClient({
chain: evmChain,
transport: http(evmChain.rpcUrls.default.http[0]),
});

const balance =
address === NativeEVMTokenConstantAddress
? await publicClient.getBalance({ address: userAddress as Address })
: await publicClient.readContract({
abi: erc20Abi,
address: address as Address,
functionName: "balanceOf",
args: [userAddress as Address],
});
Comment on lines +29 to +37
Copy link
Contributor

Choose a reason for hiding this comment

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

Handle potential errors in contract calls.

Ensure that potential errors in contract calls are handled gracefully.

let balance;
try {
  balance =
    address === NativeEVMTokenConstantAddress
      ? await publicClient.getBalance({ address: userAddress as Address })
      : await publicClient.readContract({
          abi: erc20Abi,
          address: address as Address,
          functionName: "balanceOf",
          args: [userAddress as Address],
        });
} catch (error) {
  console.error(`Failed to fetch balance for address ${address}: ${error.message}`);
  throw error;
}


return balance;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure balance is returned in a consistent format.

Consider ensuring that the returned balance is in a consistent format (e.g., a string or a specific unit) to avoid potential issues in downstream code.

-  return balance;
+  return balance.toString();
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return balance;
return balance.toString();

}
Loading
Loading