Skip to content
Open
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
12 changes: 11 additions & 1 deletion api/_bridges/sponsored-intent/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getFullRelayers,
getTransferRestrictedRelayers,
} from "../../../_relayer-address";
import { signDigestWithSponsor } from "../../../_sponsorship-signature";

export function getHyperEvmChainId(destinationChainId: number) {
return [CHAIN_IDs.HYPEREVM, CHAIN_IDs.HYPERCORE].includes(destinationChainId)
Expand Down Expand Up @@ -60,7 +61,16 @@ export function getDepositMessage(params: {
}) {
const { outputToken, recipient } = params;
if (isToHyperCore(outputToken.chainId)) {
return ethers.utils.defaultAbiCoder.encode(["address"], [recipient]);
const encodedRecipient = ethers.utils.defaultAbiCoder.encode(
["address"],
[recipient]
);
const hashedRecipient = ethers.utils.keccak256(encodedRecipient);
const signature = signDigestWithSponsor(hashedRecipient);
return ethers.utils.defaultAbiCoder.encode(
["address", "bytes"],
[recipient, signature]
);
}
return "0x";
}
Expand Down
3 changes: 2 additions & 1 deletion api/_bridges/sponsored-intent/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ export const USDH_FILL_DESTINATION_GAS_LIMIT_USD = 0.25; // 0.25 USD

// TODO: Pull from @across-protocol/contracts once `HyperliquidDepositHandler`
// deployments are available upstream.
// Taken from https://hyperevmscan.io/address/0x420D76Aa59A56953C920a1D1451b0B73147334F1
export const HYPERLIQUID_DEPOSIT_HANDLER_ADDRESS =
"0x861E127036B28D32f3777B4676F6bbb9e007d195";
"0x420D76Aa59A56953C920a1D1451b0B73147334F1";
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can also go with the across constants library, based on the info from this recent PR: https://github.com/across-protocol/contracts/blob/199910196f1f7b57b7c6e2e056260c78012b3b85/broadcast/deployed-addresses.json#L546

Copy link
Contributor Author

@dohaki dohaki Dec 16, 2025

Choose a reason for hiding this comment

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

I thought so too but the latest beta contracts version is still referring to 0x861E127036B28D32f3777B4676F6bbb9e007d195 and we need the new one

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, should we also notify the contracts team about this, or do they know anyway?
Will approve for now, but would be great if we can omit having to hardcode addresses.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, if they release a non-beta version we should be able to pull addresses from there soon


export const SUPPORTED_INPUT_TOKENS = [
TOKEN_SYMBOLS_MAP.USDC,
Expand Down
16 changes: 1 addition & 15 deletions api/_sponsorship-signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const getSponsorshipSigner = (): ethers.Wallet => {

/**
* Signs a raw digest with the sponsorship signer.
* This is used for CCTP signatures where the contract expects a signature on the unprefixed hash.
* This is used for sponsored signatures where the contract expects a signature on the unprefixed hash.
* @param {string} digest The raw digest to sign.
* @returns {string} The signature string.
*/
Expand All @@ -34,17 +34,3 @@ export const signDigestWithSponsor = (digest: string): string => {
const signature = signer._signingKey().signDigest(digest);
return utils.joinSignature(signature);
};

/**
* Signs a message with the sponsorship signer.
* This adds the EIP-191 prefix to the message before signing.
* Use this when the contract expects `toEthSignedMessageHash().recover()`.
* @param {Uint8Array} message The message to sign.
* @returns {Promise<string>} The signature string.
*/
export const signMessageWithSponsor = (
message: Uint8Array
): Promise<string> => {
const signer = getSponsorshipSigner();
return signer.signMessage(message);
};
10 changes: 10 additions & 0 deletions test/api/_bridges/sponsored-intent/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ import {
HYPERLIQUID_DEPOSIT_HANDLER_ADDRESS,
} from "../../../../api/_bridges/sponsored-intent/utils/constants";
import { USDC_ON_OPTIMISM, USDH_ON_HYPEREVM, USDH_ON_HYPERCORE } from "./utils";
import { signDigestWithSponsor } from "../../../../api/_sponsorship-signature";

jest.mock("../../../../api/_balance");
jest.mock("../../../../api/_hypercore", () => ({
...jest.requireActual("../../../../api/_hypercore"),
accountExistsOnHyperCore: jest.fn(),
}));
jest.mock("../../../../api/_relayer-address");
jest.mock("../../../../api/_sponsorship-signature", () => ({
signDigestWithSponsor: jest.fn(),
}));

describe("api/_bridges/sponsored-intent/utils/common", () => {
beforeEach(() => {
Expand Down Expand Up @@ -108,6 +112,12 @@ describe("api/_bridges/sponsored-intent/utils/common", () => {
});

describe("getDepositMessage", () => {
beforeEach(() => {
(signDigestWithSponsor as jest.Mock).mockReturnValue(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
);
});

it("should return encoded address if to HyperCore", () => {
const recipient = "0x0000000000000000000000000000000000000123";
const outputToken = USDH_ON_HYPERCORE;
Expand Down
Loading