Skip to content
Closed
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
19 changes: 12 additions & 7 deletions src/cctp-finalizer/utils/evmUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
CHAIN_IDs,
depositToHypercore,
decodeCctpV2HookData,
TOKEN_SYMBOLS_MAP,
CCTPHookData,
} from "../../utils";
import { CONTRACT_ADDRESSES } from "../../common/ContractAddresses";
Expand Down Expand Up @@ -41,9 +40,7 @@ export async function checkIfAlreadyProcessedEvm(
}

export function shouldCreateHyperCoreAccount(hookData?: CCTPHookData): boolean {
const isDestinationUsdc = hookData?.finalToken === TOKEN_SYMBOLS_MAP.USDC.addresses[CHAIN_IDs.HYPEREVM];
const isSponsoredFlow = hookData?.maxBpsToSponsor > 0;
return isSponsoredFlow || isDestinationUsdc;
return hookData?.accountCreationMode > 0;
}

export async function createHyperCoreAccountIfNotExists(
Expand All @@ -55,8 +52,8 @@ export async function createHyperCoreAccountIfNotExists(
if (!shouldCreateHyperCoreAccount(hookData)) {
logger.debug({
at: "evmUtils#createHyperCoreAccountIfNotExists",
message: "Skipping deposit to Hypercore because its not sponsored flow",
maxBpsToSponsor: hookData?.maxBpsToSponsor,
message: "Skipping deposit to Hypercore because accountCreationMode is None",
accountCreationMode: hookData?.accountCreationMode,
finalRecipient: hookData?.finalRecipient,
});
return;
Expand All @@ -67,8 +64,16 @@ export async function createHyperCoreAccountIfNotExists(
at: "evmUtils#createHyperCoreAccountIfNotExists",
message: "Recipient address does not exist, depositing to Hypercore",
finalRecipient: hookData.finalRecipient,
accountCreationMode: hookData.accountCreationMode,
});
await depositToHypercore(hookData.finalRecipient, signer, logger);
await depositToHypercore(
hookData.finalRecipient,
signer,
logger,
hookData.accountCreationMode,
hookData.destinationDex,
hookData.actionData
);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/common/abi/HyperliquidDepositHandler.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"type": "uint256"
},
{
"internalType": "address",
"name": "user",
"type": "address"
"internalType": "bytes",
"name": "message",
"type": "bytes"
}
],
"name": "depositToHypercore",
Expand Down
21 changes: 19 additions & 2 deletions src/utils/HyperliquidUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,32 @@ async function _callWithRetry<T, A extends any[]>(
}
}

export async function depositToHypercore(account: string, signer: Signer, logger: winston.Logger): Promise<string> {
export async function depositToHypercore(
account: string,
signer: Signer,
logger: winston.Logger,
accountCreationMode: number,
destinationDex: number,
actionData?: string
): Promise<string> {
const transactionClient = new TransactionClient(logger);
const chainId = CHAIN_IDs.HYPEREVM;
const contract = new ethers.Contract(
CONTRACT_ADDRESSES[chainId].hyperliquidDepositHandler.address,
CONTRACT_ADDRESSES[chainId].hyperliquidDepositHandler.abi,
signer
);
const depositToHypercoreArgs = [TOKEN_SYMBOLS_MAP.USDH.addresses[chainId], bnZero, account];

// Encode the message parameter: first byte is the AccountActivationMode,
// remainder is abi.encode(user, destinationDex[, signature]).
const modeByte = ethers.utils.hexlify([accountCreationMode]);
const encodedParams =
accountCreationMode === 2 // FromDonationBox: includes activation signature
? ethers.utils.defaultAbiCoder.encode(["address", "uint32", "bytes"], [account, destinationDex, actionData])
Comment on lines +150 to +151
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Compare accountCreationMode using BigNumber semantics

In depositToHypercore, the FromDonationBox branch is gated by accountCreationMode === 2, but createHyperCoreAccountIfNotExists passes hookData.accountCreationMode from decodeCctpV2HookData, which is sourced from ethers.utils.defaultAbiCoder.decode without numeric normalization. In ethers v5 this value is a BigNumber-like object, so strict equality to a JS number does not match; the code then encodes message without actionData, producing an invalid payload for donation-box account creation flows. Convert decoded uint fields to numbers at decode time (or use BigNumber .eq(2) here).

Useful? React with 👍 / 👎.

: ethers.utils.defaultAbiCoder.encode(["address", "uint32"], [account, destinationDex]);
const message = ethers.utils.hexlify(ethers.utils.concat([modeByte, encodedParams]));

const depositToHypercoreArgs = [TOKEN_SYMBOLS_MAP.USDH.addresses[chainId], bnZero, message];
const depositToHypercoreTx = await submitTransaction(
{
contract: contract,
Expand Down
Loading