Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
77f1d72
feat: new command to finalize using CoreWriter + rename
shankars99 Dec 11, 2025
4c21c86
docs: usage of finalizeEvmContract via CoreWriter
shankars99 Dec 11, 2025
e2a9adf
feat: quote asset helper function to list all quote assets and check …
shankars99 Dec 11, 2025
60357d1
chore: adding index.ts
shankars99 Dec 11, 2025
2c38846
chore: registerSpot uses new quote asset checker for custom quote assets
shankars99 Dec 11, 2025
2d08b3b
docs: new quote asset command
shankars99 Dec 11, 2025
10e4b96
chore: add USDe as known quote asset
shankars99 Dec 11, 2025
474be2d
chore: improved error handling on parsing
shankars99 Dec 11, 2025
7710a35
chore: update examples to use quote asset checker
shankars99 Dec 11, 2025
ca53638
docs: quote asset documentation
shankars99 Dec 11, 2025
2a2b0f6
pnpm lint:fix
shankars99 Dec 11, 2025
9903f27
pnpm changeset
shankars99 Dec 11, 2025
32dddf5
docs: add FeeAbstraction Composer
shankars99 Dec 11, 2025
f187628
chore: improved error handling when token is not deployed
shankars99 Dec 13, 2025
83a6931
docs: package README to only display sdk commands
shankars99 Dec 16, 2025
1ed241a
error-log: update error log to account for http errors
shankars99 Dec 16, 2025
0e0435f
docs: categorize Finalize methods - sdk and corewriter
shankars99 Dec 16, 2025
fc0f3d0
chore: fix path references
shankars99 Dec 16, 2025
c35fa22
rename to list-quote-assets
shankars99 Dec 16, 2025
b2569a0
chore: remove unnecessary typecasting
shankars99 Dec 16, 2025
e8c22de
Merge branch 'main' into hlp/sdk-finalize-and-quote-asset-update
shankars99 Dec 17, 2025
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
6 changes: 6 additions & 0 deletions .changeset/twenty-chairs-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@layerzerolabs/hyperliquid-composer": patch
"@layerzerolabs/oft-hyperliquid-example": patch
---

sdk updates for quote asset improvement
79 changes: 74 additions & 5 deletions examples/oft-hyperliquid/HYPERLIQUID.README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,16 @@ npx @layerzerolabs/hyperliquid-composer trading-fee \

### 7. Enable Quote Token Capability (Optional)

Enables your token to be used as a quote asset for trading pairs. **Dependency:** Requires specific trading fee share value (see Step 6 above). See: [Permissionless Spot Quote Assets](https://hyperliquid.gitbook.io/hyperliquid-docs/hypercore/permissionless-spot-quote-assets)
Enables your token to be used as a quote asset for trading pairs.

> ⚠️ **Important**: Review the complete [Quote Assets (Fee Tokens)](https://github.com/LayerZero-Labs/devtools/tree/main/packages/hyperliquid-composer/HYPERLIQUID.README.md#quote-assets-fee-tokens) section in the main documentation for:
>
> - Mainnet requirements (technical and liquidity)
> - Testnet requirements (50 HYPE stake + active order book)
> - Order book maintenance for `HYPE/YOUR_ASSET` pair
> - Composer selection guidance (use `FeeToken` variant for quote assets)

**Dependency:** Requires trading fee share configuration (see Step 6 above).

```bash
npx @layerzerolabs/hyperliquid-composer enable-quote-token \
Expand Down Expand Up @@ -161,6 +170,8 @@ npx @layerzerolabs/hyperliquid-composer request-evm-contract \

### 2. Finalize EVM Contract Link

#### Hypercore action method

```bash
npx @layerzerolabs/hyperliquid-composer finalize-evm-contract \
--token-index <coreIndex> \
Expand All @@ -169,6 +180,20 @@ npx @layerzerolabs/hyperliquid-composer finalize-evm-contract \
[--log-level {info | verbose}]
```

#### CoreWriter Method

Alternative method using direct CoreWriter interaction. This is useful if you prefer to use Foundry's `cast` command.

```bash
npx @layerzerolabs/hyperliquid-composer finalize-evm-contract-corewriter \
--token-index <coreIndex> \
--nonce <nonce> \
--network {testnet | mainnet} \
[--log-level {info | verbose}]
```

The command will output the calldata and a ready-to-use `cast send` command for finalizing the EVM contract link via the CoreWriter precompile at `0x3333333333333333333333333333333333333333`.

## Post-Launch Management

### Freeze/Unfreeze Users
Expand Down Expand Up @@ -264,6 +289,25 @@ npx @layerzerolabs/hyperliquid-composer spot-auction-status \
[--log-level {info | verbose}]
```

### Check if Token is Quote Asset

Check if a specific token is a quote asset, or list all quote assets when no token index is provided. Quote assets are automatically paired with HYPE when promoted by the Hyperliquid protocol.

```bash
# List all quote assets
npx @layerzerolabs/hyperliquid-composer list-quote-asset \
--network {testnet | mainnet} \
[--log-level {info | verbose}]

# Check if specific token is a quote asset
npx @layerzerolabs/hyperliquid-composer list-quote-asset \
--filter-token-index <coreIndex> \
--network {testnet | mainnet} \
[--log-level {info | verbose}]
```

The command returns `yes` or `no` when checking a specific token, or lists all quote assets when no token index is provided.

## Utilities

### Convert Token Index to Bridge Address
Expand Down Expand Up @@ -490,11 +534,11 @@ npx @layerzerolabs/hyperliquid-composer trading-fee \

This step enables the token to be used as a quote asset for trading pairs. This allows other tokens to form trading pairs against your token (e.g., TOKEN/YOUR_TOKEN instead of only YOUR_TOKEN/USDC).

> ⚠️ **Requirements**:
> ⚠️ **Important**: Review the complete [Quote Assets (Fee Tokens)](../../packages/hyperliquid-composer/HYPERLIQUID.README.md#quote-assets-fee-tokens) section in the main documentation for detailed requirements, including:
>
> - Requires specific trading fee share value (see Step 6/7 above)
> - Review all requirements at: [Permissionless Spot Quote Assets](https://hyperliquid.gitbook.io/hyperliquid-docs/hypercore/permissionless-spot-quote-assets)
> - Contact the Hyperliquid team for the most up-to-date information
> - Mainnet: Complete technical and liquidity requirements
> - Testnet: Simplified requirements (50 HYPE stake + active order book)
> - Order book maintenance for `HYPE/YOUR_ASSET` pair after enablement
>
> 📝 **Note**: This can be executed after the trading fee share is set and even after deployment and linking are complete.

Expand All @@ -506,6 +550,18 @@ npx @layerzerolabs/hyperliquid-composer enable-quote-token \
[--log-level {info | verbose}]
```

**Prerequisites:**

- Trading fee share must be set (see Step 6/7 above)
- **Testnet**: 50 HYPE staked + active BUY and SELL limit orders on your token's order book
- **Mainnet**: All requirements per [Hyperliquid's documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/hypercore/permissionless-spot-quote-assets)

**After Execution:**

- A `HYPE/YOUR_ASSET` trading pair is automatically created
- You must maintain order book requirements for the new `HYPE/ASSET` pair
- Verify quote asset status with the `list-quote-asset` command

### Step 6.7/7 `enableAlignedQuoteToken` (Optional)

This step enables the token to be used as an aligned quote asset for trading pairs. Aligned quote tokens have special properties and requirements different from regular quote tokens.
Expand Down Expand Up @@ -559,6 +615,19 @@ npx @layerzerolabs/hyperliquid-composer finalize-evm-contract \
--private-key $PRIVATE_KEY_HYPERLIQUID
```

**Alternative: Using CoreWriter directly with Foundry**

If you prefer to use Foundry's `cast` command, you can generate the calldata and send the transaction directly:

```bash
npx @layerzerolabs/hyperliquid-composer finalize-evm-contract-corewriter \
--token-index <coreIndex> \
--nonce <deployment-nonce> \
--network {testnet | mainnet}
```

This will output the calldata and a ready-to-use `cast send` command that you can execute directly.

## Deploy the Composer

While the composer could have been deployed at any point in time due to its statelessness, it is technically the final step of the deployment process. The following script automatically handles the block switching for you.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CHAIN_IDS,
EthersSigner,
getCoreSpotDeployment,
isQuoteAsset,
useBigBlock,
useSmallBlock,
} from '@layerzerolabs/hyperliquid-composer'
Expand Down Expand Up @@ -73,6 +74,30 @@ const deploy: DeployFunction = async (hre) => {
// Validates and returns the native spot
const hip1Token = getCoreSpotDeployment(coreSpotIndex, isTestnet)

// Check if the token is a quote asset - warn if it is
console.log(`\nChecking if token ${coreSpotIndex} is a quote asset...`)
const { isQuoteAsset: isQuote, tokenName } = await isQuoteAsset(isTestnet, parseInt(coreSpotIndex), loglevel)

if (isQuote) {
console.warn(`\n[WARNING] Token ${coreSpotIndex}${tokenName ? ` (${tokenName})` : ''} IS a quote asset!`)
console.warn(`For quote assets, you should use MyHyperLiquidComposer_FeeToken instead.`)
console.warn(`FeeToken composer provides automatic user activation using the token itself.\n`)

const { continueAnyway } = await inquirer.prompt([
{
type: 'confirm',
name: 'continueAnyway',
message: 'Do you want to continue with FeeAbstraction anyway?',
default: false,
},
])

if (!continueAnyway) {
console.log('Deployment cancelled.')
process.exit(0)
}
}

const { address: address_oft } = await hre.deployments.get(contractName_oft).catch(async () => {
console.log(`Deployment file for ${contractName_oft}.json in deployments/${networkName} not found`)
const { proceedWithOFTAddress } = await inquirer.prompt([
Expand Down
18 changes: 18 additions & 0 deletions examples/oft-hyperliquid/deploy/MyHyperLiquidComposer_FeeToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CHAIN_IDS,
EthersSigner,
getCoreSpotDeployment,
isQuoteAsset,
useBigBlock,
useSmallBlock,
} from '@layerzerolabs/hyperliquid-composer'
Expand Down Expand Up @@ -52,6 +53,23 @@ const deploy: DeployFunction = async (hre) => {
// Validates and returns the native spot
const hip1Token = getCoreSpotDeployment(coreSpotIndex, isTestnet)

// Check if the token is a quote asset - required for FeeToken composer
console.log(`\nChecking if token ${coreSpotIndex} is a quote asset...`)
const { isQuoteAsset: isQuote, tokenName } = await isQuoteAsset(isTestnet, parseInt(coreSpotIndex), loglevel)

if (!isQuote) {
console.error(`\n[ERROR] Token ${coreSpotIndex} is NOT a quote asset!`)
console.error(`The FeeToken composer can ONLY be used with quote assets (tokens paired with HYPE).`)
console.error(`\nThis token is not a quote asset. Please use one of the following composers instead:`)
console.error(` - MyHyperliquidComposer (regular)`)
console.error(` - MyHyperLiquidComposer_FeeAbstraction`)
console.error(` - MyHyperLiquidComposer_Recoverable`)
throw new Error(`Token ${coreSpotIndex} is not a quote asset. FeeToken composer requires a quote asset.`)
}

console.log(`[OK] Confirmed: Token ${coreSpotIndex}${tokenName ? ` (${tokenName})` : ''} is a quote asset`)
console.log(` FeeToken composer can be used for automatic user activation.\n`)

const { address: address_oft } = await hre.deployments.get(contractName_oft).catch(async () => {
console.log(`Deployment file for ${contractName_oft}.json in deployments/${networkName} not found`)
const { proceedWithOFTAddress } = await inquirer.prompt([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CHAIN_IDS,
EthersSigner,
getCoreSpotDeployment,
isQuoteAsset,
useBigBlock,
useSmallBlock,
} from '@layerzerolabs/hyperliquid-composer'
Expand Down Expand Up @@ -54,6 +55,30 @@ const deploy: DeployFunction = async (hre) => {
// Validates and returns the native spot
const hip1Token = getCoreSpotDeployment(coreSpotIndex, isTestnet)

// Check if the token is a quote asset - warn if it is
console.log(`\nChecking if token ${coreSpotIndex} is a quote asset...`)
const { isQuoteAsset: isQuote, tokenName } = await isQuoteAsset(isTestnet, parseInt(coreSpotIndex), loglevel)

if (isQuote) {
console.warn(`\n[WARNING] Token ${coreSpotIndex}${tokenName ? ` (${tokenName})` : ''} IS a quote asset!`)
console.warn(`For quote assets, you should use MyHyperLiquidComposer_FeeToken instead.`)
console.warn(`FeeToken composer provides automatic user activation using the token itself.\n`)

const { continueAnyway } = await inquirer.prompt([
{
type: 'confirm',
name: 'continueAnyway',
message: 'Do you want to continue with recoverable composer anyway?',
default: false,
},
])

if (!continueAnyway) {
console.log('Deployment cancelled.')
process.exit(0)
}
}

const { address: address_oft } = await hre.deployments.get(contractName_oft).catch(async () => {
console.log(`Deployment file for ${contractName_oft}.json in deployments/${networkName} not found`)
const { proceedWithOFTAddress } = await inquirer.prompt([
Expand Down
25 changes: 25 additions & 0 deletions examples/oft-hyperliquid/deploy/MyHyperliquidComposer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CHAIN_IDS,
EthersSigner,
getCoreSpotDeployment,
isQuoteAsset,
useBigBlock,
useSmallBlock,
} from '@layerzerolabs/hyperliquid-composer'
Expand Down Expand Up @@ -52,6 +53,30 @@ const deploy: DeployFunction = async (hre) => {
// Validates and returns the native spot
const hip1Token = getCoreSpotDeployment(coreSpotIndex, isTestnet)

// Check if the token is a quote asset - warn if it is
console.log(`\nChecking if token ${coreSpotIndex} is a quote asset...`)
const { isQuoteAsset: isQuote, tokenName } = await isQuoteAsset(isTestnet, parseInt(coreSpotIndex), loglevel)

if (isQuote) {
console.warn(`\n[WARNING] Token ${coreSpotIndex}${tokenName ? ` (${tokenName})` : ''} IS a quote asset!`)
console.warn(`For quote assets, you should use MyHyperLiquidComposer_FeeToken instead.`)
console.warn(`FeeToken composer provides automatic user activation using the token itself.\n`)

const { continueAnyway } = await inquirer.prompt([
{
type: 'confirm',
name: 'continueAnyway',
message: 'Do you want to continue with regular composer anyway?',
default: false,
},
])

if (!continueAnyway) {
console.log('Deployment cancelled.')
process.exit(0)
}
}

const { address: address_oft } = await hre.deployments.get(contractName_oft).catch(async () => {
console.log(`Deployment file for ${contractName_oft}.json in deployments/${networkName} not found`)
const { proceedWithOFTAddress } = await inquirer.prompt([
Expand Down
Loading
Loading