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

set connect version 0.3.19-beta.0-development #961

Merged
merged 35 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
51a294b
Feature address input for target
yuli-ferna Jul 24, 2024
3a8ab97
Fix prettier
yuli-ferna Jul 24, 2024
8e7a6be
improve conditions
yuli-ferna Jul 25, 2024
84f6f8a
Merge main
yuli-ferna Jul 25, 2024
32c7192
Add new sanctioned condition
yuli-ferna Jul 25, 2024
136a192
validation of cosmos and sui
yuli-ferna Jul 26, 2024
a5f1866
fis prettier
yuli-ferna Jul 26, 2024
5c55d64
New version of connect
yuli-ferna Jul 26, 2024
f85871a
change sanctioned
yuli-ferna Jul 26, 2024
8bf2a79
change sanctioned
yuli-ferna Jul 26, 2024
4bb8f1e
change sanctioned
yuli-ferna Jul 26, 2024
6a755ad
fix code
yuli-ferna Jul 26, 2024
e5a55f1
pdate chain trm list
yuli-ferna Jul 26, 2024
5c44c58
Merge main
yuli-ferna Jul 30, 2024
e3f6b38
Review fixes
yuli-ferna Jul 30, 2024
988e567
Merge main
yuli-ferna Jul 30, 2024
0f8c28d
update connect
yuli-ferna Jul 30, 2024
5ded8c7
format
yuli-ferna Jul 30, 2024
3740482
fix
yuli-ferna Jul 30, 2024
13d0811
fix test
yuli-ferna Jul 30, 2024
f5aa8b1
set connect version 0.3.19-beta.0-development
sebastianscatularo Aug 1, 2024
3b9f9cf
enable manual target address
sebastianscatularo Aug 1, 2024
5ee59c0
update
yuli-ferna Aug 2, 2024
f70fc63
update
yuli-ferna Aug 2, 2024
9627117
update WIF config
yuli-ferna Aug 5, 2024
e7f8214
update connect version to beta.1
sebastianscatularo Aug 5, 2024
2afaafa
update connect version to beta.1
sebastianscatularo Aug 5, 2024
5b2d346
fix cosmos validation
yuli-ferna Aug 6, 2024
a73a532
Merge branch 'deploy/connect-0.3.19-rc' of https://github.com/XLabs/p…
yuli-ferna Aug 6, 2024
5ca0079
update connect
yuli-ferna Aug 6, 2024
dab3c47
update connect version to beta.5
sebastianscatularo Aug 7, 2024
c372253
update connect
yuli-ferna Aug 7, 2024
4216bb9
update connect version to beta.7
sebastianscatularo Aug 8, 2024
80c55d0
Fix ofac check eth
yuli-ferna Aug 9, 2024
16f2ade
added amount to metrics
danielisaacgeslin Aug 9, 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
684 changes: 561 additions & 123 deletions apps/connect/package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion apps/connect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
"@mui/icons-material": "^5.14.11",
"@mui/material": "^5.12.1",
"@tanstack/react-query": "^5.14.2",
"@wormhole-foundation/wormhole-connect": "^0.3.18",
"@wormhole-foundation/wormhole-connect": "^0.3.19-beta.7-development",
"aptos": "^1.21.0",
"bech32": "^2.0.0",
"dompurify": "^3.0.6",
"mixpanel-browser": "^2.53.0",
"react": "^18.2.0",
Expand All @@ -41,6 +43,7 @@
"@testing-library/dom": "^10.3.1",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/bs58": "^4.0.4",
"@types/dompurify": "^3.0.5",
"@types/jest": "^29.5.12",
"@types/mixpanel-browser": "^2.49.0",
Expand Down
7 changes: 5 additions & 2 deletions apps/connect/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { PrivacyPolicyPath, isPreview, isProduction } from "./utils/constants";
import Banner from "./components/atoms/Banner";
import { ENV } from "@env";
import { clearUrl, pushResumeUrl } from "./navs/navs";
import { validateTransferHandler } from "./providers/sanctions";
import { validateTransfer } from "./utils/transferVerification";
//import { validateTransferHandler } from "./providers/sanctions"; // TO DO: Use this function

const defaultConfig: WormholeConnectConfig = {
...ENV.wormholeConnectConfig,
Expand All @@ -29,6 +30,8 @@ const defaultConfig: WormholeConnectConfig = {
// Clear the URL when a transfer is successful
clearUrl(e);
},
// validateTransfer
validateTransferHandler: validateTransfer,
isRouteSupportedHandler: async (td: any) => {
// Disable manual NTT for Lido wstETH
if (
Expand All @@ -52,7 +55,7 @@ export default function Root() {
const config: ComponentProps<typeof WormholeConnect>["config"] = useMemo(
() => ({
...defaultConfig,
validateTransferHandler,
//validateTransferHandler,
searchTx: {
...(txHash && { txHash }),
...(sourceChain && { chainName: sourceChain }),
Expand Down
8 changes: 4 additions & 4 deletions apps/connect/src/env/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ export const versions: Env["versions"] = [
version: `v${packageJson.dependencies["@wormhole-foundation/wormhole-connect"]}`,
},
];

export const CLUSTER = envVars.VITE_APP_CLUSTER || "testnet";
export const wormholeConnectConfigCommon: Partial<WormholeConnectConfig> = {
walletConnectProjectId: envVars.VITE_APP_WALLET_CONNECT_PROJECT_ID || "",

env: envVars.VITE_APP_CLUSTER || "testnet",
env: CLUSTER,
rpcs: {},
showHamburgerMenu: false,
explorer: {
href: `https://wormholescan.io/#/txs?address={:address}&network=${envVars.VITE_APP_CLUSTER || "testnet"}`,
href: `https://wormholescan.io/#/txs?address={:address}&network=${CLUSTER}`,
},
manualTargetAddress: true,
menu: [
{
label: "Advanced Tools",
Expand Down
6 changes: 3 additions & 3 deletions apps/connect/src/env/token-bridge.mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export const ENV: Env = {
networks: [ALGORAND, ACALA, SEI, MORE],
} as WormholeConnectConfig["moreNetworks"],
tokensConfig: {
WIF: {
key: "WIF",
symbol: "WIF",
$WIF: {
key: "$WIF",
symbol: "$WIF",
nativeChain: "solana",
tokenId: {
chain: "solana",
Expand Down
7 changes: 4 additions & 3 deletions apps/connect/src/hooks/useQueryParams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChainName, coalesceChainName, isChain } from "@certusone/wormhole-sdk";
import { coalesceChainName, isChain } from "@certusone/wormhole-sdk";
import { ChainName } from "@wormhole-foundation/wormhole-connect";
import { useMemo } from "react";

const getChainValue = (
Expand All @@ -8,12 +9,12 @@ const getChainValue = (
const sourceChain = query.get(key);
if (sourceChain) {
if (isChain(sourceChain)) {
return coalesceChainName(sourceChain);
return coalesceChainName(sourceChain) as ChainName;
}

const chainId = Number(sourceChain);
if (isChain(chainId)) {
return coalesceChainName(chainId);
return coalesceChainName(chainId) as ChainName;
}
}
return null;
Expand Down
33 changes: 12 additions & 21 deletions apps/connect/src/providers/sanctions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import {
RISK_LEVEL_SANCTION,
SanctionResponse,
TRM_URL,
validateTransferHandler,
getTrmChainName,
isSanctionedAddress,
} from "./sanctions";

describe("sanctions", () => {
let transferDetails: Parameters<typeof validateTransferHandler>[0];
let transferDetails: Parameters<typeof isSanctionedAddress>[0];
let validResponse: SanctionResponse;
beforeEach(() => {
global.fetch = jest.fn().mockResolvedValue({ json: jest.fn() });
transferDetails = {
fromChain: "fromChain",
fromChain: "ethereum",
fromWalletAddress: "fromWalletAddress",
toChain: "toChain",
toChain: "avalanche",
toWalletAddress: "toWalletAddress",
} as any;
validResponse = {
Expand All @@ -33,17 +34,15 @@ describe("sanctions", () => {
global.fetch = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue([validResponse]),
});
expect(await validateTransferHandler(transferDetails)).toEqual({
isValid: true,
});
expect(await isSanctionedAddress(transferDetails)).toEqual(false);
expect(global.fetch).toHaveBeenNthCalledWith(
1,
TRM_URL,
expect.objectContaining({
body: JSON.stringify([
{
address: transferDetails.fromWalletAddress,
chain: transferDetails.fromChain,
chain: getTrmChainName(transferDetails.fromChain),
accountExternalId: ACCOUNT_ID,
},
]),
Expand All @@ -56,7 +55,7 @@ describe("sanctions", () => {
body: JSON.stringify([
{
address: transferDetails.toWalletAddress,
chain: transferDetails.toChain,
chain: getTrmChainName(transferDetails.toChain),
accountExternalId: ACCOUNT_ID,
},
]),
Expand All @@ -67,9 +66,7 @@ describe("sanctions", () => {
it("should be valid when api fails", async () => {
console.error = jest.fn();
global.fetch = jest.fn().mockRejectedValue({});
expect(await validateTransferHandler(transferDetails)).toEqual({
isValid: true,
});
expect(await isSanctionedAddress(transferDetails)).toEqual(false);
});

it("should be valid when api returns unexpected data", async () => {
Expand All @@ -78,9 +75,7 @@ describe("sanctions", () => {
json: jest.fn().mockResolvedValue({ random: true }),
});

expect(await validateTransferHandler(transferDetails)).toEqual({
isValid: true,
});
expect(await isSanctionedAddress(transferDetails)).toEqual(false);
});

it("should NOT be valid when one address is NOT valid because of the address risk level", async () => {
Expand All @@ -101,9 +96,7 @@ describe("sanctions", () => {
]);
global.fetch = jest.fn().mockResolvedValue({ json });

expect(await validateTransferHandler(transferDetails)).toEqual({
isValid: false,
});
expect(await isSanctionedAddress(transferDetails)).toEqual(true);
});

it("should NOT be valid when one address is NOT valid because of the entity risk level", async () => {
Expand All @@ -121,8 +114,6 @@ describe("sanctions", () => {
]);
global.fetch = jest.fn().mockResolvedValue({ json });

expect(await validateTransferHandler(transferDetails)).toEqual({
isValid: false,
});
expect(await isSanctionedAddress(transferDetails)).toEqual(true);
});
});
82 changes: 66 additions & 16 deletions apps/connect/src/providers/sanctions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
import { WormholeConnectConfig } from "@wormhole-foundation/wormhole-connect";
import {
CHAIN_ID_ALGORAND,
CHAIN_ID_ARBITRUM,
CHAIN_ID_AVAX,
CHAIN_ID_BSC,
CHAIN_ID_BTC,
CHAIN_ID_CELO,
CHAIN_ID_OPTIMISM,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_KLAYTN,
ChainId,
isEVMChain,
ChainName,
toChainId,
isCosmWasmChain,
} from "@certusone/wormhole-sdk";
import { ExtendedTransferDetails } from "node_modules/@wormhole-foundation/wormhole-connect/lib/src/config/types";

export interface SanctionResponse {
addressRiskIndicators: { categoryRiskScoreLevel: number; riskType: string }[];
Expand All @@ -11,6 +28,29 @@ export const ACCOUNT_ID = "PortalBridge";
export const RISK_LEVEL_SANCTION: number = 10;
export const RISK_ADDRESS_INDICATOR_TYPE = "OWNERSHIP";

// TRM screening chain names map with wormhole chain ids
// https://documentation.trmlabs.com/tag/Supported-Blockchain-List
export const getTrmChainName = (chain: ChainName | ChainId) => {
const id = toChainId(chain as ChainName);
const trmChainNames: any = {
[CHAIN_ID_ALGORAND]: "algorand",
[CHAIN_ID_ARBITRUM]: "arbitrum",
[CHAIN_ID_AVAX]: "avalanche_c_chain",
[CHAIN_ID_BSC]: "binance_smart_chain",
[CHAIN_ID_BTC]: "bitcoin",
[CHAIN_ID_CELO]: "celo",
[CHAIN_ID_KLAYTN]: "klaytn",
[CHAIN_ID_OPTIMISM]: "optimism",
[CHAIN_ID_POLYGON]: "polygon",
[CHAIN_ID_SOLANA]: "solana",
};

if (trmChainNames[id]) return trmChainNames[id];
if (isCosmWasmChain(id)) return "cosmos";
if (isEVMChain(id)) return "ethereum";

return "";
};
const isSanctioned = async ({
chain,
address,
Expand Down Expand Up @@ -41,19 +81,29 @@ const isSanctioned = async ({
}
};

export const validateTransferHandler: NonNullable<
WormholeConnectConfig["validateTransferHandler"]
> = async (transferDetails) => {
const [isOriginSanctioned, isTargetSanctioned] = await Promise.all([
isSanctioned({
chain: transferDetails.fromChain,
address: transferDetails.fromWalletAddress,
}),
isSanctioned({
chain: transferDetails.toChain,
address: transferDetails.toWalletAddress,
}),
]);

return { isValid: !isOriginSanctioned && !isTargetSanctioned };
export const isSanctionedAddress = async (
transferDetails: ExtendedTransferDetails
) => {
const [isOriginSanctioned, isTargetSanctioned, isTargetSanctionedEth] =
await Promise.all([
isSanctioned({
chain: getTrmChainName(transferDetails.fromChain as ChainName),
address: transferDetails.fromWalletAddress,
}),
isSanctioned({
chain: getTrmChainName(transferDetails.toChain as ChainName),
address: transferDetails.toWalletAddress,
}),
...(transferDetails.toChain !== "ethereum" &&
isEVMChain(transferDetails.toChain)
? [
isSanctioned({
chain: "ethereum",
address: transferDetails.toWalletAddress,
}),
]
: []),
]);

return isOriginSanctioned || isTargetSanctioned || isTargetSanctionedEth;
};
1 change: 1 addition & 0 deletions apps/connect/src/providers/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const eventHandler = (e: WormholeConnectEvent) => {
toTokenAddress: getTokenAddress(e.details.toToken),
txId: e.details.txId,
USDAmount: e.details.USDAmount,
amount: e.details.amount,
route:
{
bridge: "Manual Bridge",
Expand Down
35 changes: 35 additions & 0 deletions apps/connect/src/utils/transferVerification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
ExtendedTransferDetails,
ValidateTransferResult,
} from "node_modules/@wormhole-foundation/wormhole-connect/lib/src/config/types";
import { ChainName } from "@certusone/wormhole-sdk";
import { isValidAddress } from "./validAddress";
import { isSanctionedAddress } from "../../src/providers/sanctions";

export const validateTransfer = async (
tx: ExtendedTransferDetails
): Promise<ValidateTransferResult> => {
tx.toChain;
tx.toWalletAddress;
tx.route;
try {
// Check OFAC (sanctioned)
const isSanctioned = await isSanctionedAddress(tx);
if (isSanctioned) {
return { isValid: false, error: "Sanctioned target address" };
}
} catch (error) {
console.error(error);
}

// Correct Address Validation (based on chain selected)
const isValid = await isValidAddress(
tx.toWalletAddress,
tx.toChain as ChainName
);
if (!isValid) {
return { isValid: false, error: "Not valid target address" };
}

return { isValid: true };
};
Loading
Loading