Integration examples
Last updated
Was this helpful?
Last updated
Was this helpful?
This is an example of how to swap stHYPE
into Wrapped HYPE
on STEX AMM. As always, the entry-point contract for swaps will be STEX AMM's respective .
Requirements to run the following example:
Ensure you have an EOA wallet with private key PRIVATE_KEY
inside a .env
file, with enough HYPE balance to pay for gas costs.
Ensure your EOA has non-zero stHYPE to execute the swap. See .
import { createWalletClient, defineChain, erc20Abi, http, parseEther, publicActions, parseAbi } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// Read `PRIVATE_KEY` from .env
// WARNING: Make sure this is an EOA on HyperEVM (chain id 999) with sufficient HYPE to pay for gas,
// as well as non-zero stHYPE to execute the swap
const dotenv = require('dotenv');
dotenv.config();
// ABI definitions
const STEX_AMM_ABI = parseAbi(['function getAmountOut(address _tokenIn, uint256 _amountIn, bool _isInstantWithdraw) view returns (uint256)']);
const SOVEREIGN_POOL_ABI = parseAbi([
'function swap(SovereignPoolSwapParams _params) returns (uint256, uint256)',
'struct SovereignPoolSwapParams { bool isSwapCallback; bool isZeroToOne; uint256 amountIn; uint256 amountOutMin; uint256 deadline; address recipient; address swapTokenOut; SovereignPoolSwapContextData swapContext; }',
'struct SovereignPoolSwapContextData { bytes externalContext; bytes verifierContext; bytes swapCallbackContext; bytes swapFeeModuleContext; }',
]);
// STEX AMM deployments
// Address of the Valantis Sovereign Pool,
// always the entry-point for swaps
const STEX_AMM_SOVEREIGN_POOL = '0x5365b6EF09253C7aBc0A9286eC578A9f4B413B7D';
// Address of STEX AMM Liquidity Module bound to `STEX_AMM_SOVEREIGN_POOL`.
// This is the main AMM module which handles all swap related pricing logic.
const STEX_AMM = '0x39694eFF3b02248929120c73F90347013Aec834d';
// Temporary solution, until Hyper EVM is supported in viem
const hyperEvm = defineChain({
id: 999,
name: 'HyperEVM',
nativeCurrency: {
decimals: 18,
name: 'HYPE',
symbol: 'HYPE',
},
rpcUrls: {
default: {
http: ['https://rpc.hyperliquid.xyz/evm'],
webSocket: ['wss://rpc.hyperliquid.xyz/evm'],
},
},
blockExplorers: {
default: { name: 'Blockscout', url: 'https://hyperliquid.cloud.blockscout.com/' },
},
contracts: {},
});
async function swapStHypeToWHYPE() {
// Address of stHYPE
const tokenIn = '0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1';
// Address of WHYPE
// WARNING: Sovereign Pool (the entry point for swaps)
// only works with ERC-20 tokens. Native token always needs to be wrapped.
const tokenOut = '0x5555555555555555555555555555555555555555';
const amountIn = parseEther('0.01');
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
chain: hyperEvm,
transport: http(),
account,
}).extend(publicActions);
// In `STEX_AMM_SOVEREIGN_POOL`, toke0=stHYPE and token1=WHYPE
const isZeroToOne = true;
// 60 seconds deadline. Can use block.timestamp for extra accuracy
const deadline = BigInt(Math.floor(Date.now() / 1_000) + 60);
const recipient = account.address;
// Ensure that `account` approves `STEX_AMM_SOVEREIGN_POOL`
// to spend `amountIn` of `tokenIn` before proceeding.
const tokenInAllowance = await walletClient.readContract({
address: tokenIn,
abi: erc20Abi,
functionName: 'allowance',
args: [account.address, STEX_AMM_SOVEREIGN_POOL],
});
if (tokenInAllowance < amountIn) {
console.log('Submitting tokenIn approval to Sovereign Pool');
await walletClient.writeContract({
chain: hyperEvm,
account,
address: tokenIn,
abi: erc20Abi,
functionName: 'approve',
args: [STEX_AMM_SOVEREIGN_POOL, amountIn],
gas: 100_000n,
});
// Wait 10 seconds to reflect state updates on RPC node,
// remove this in prod
await setTimeout(() => {}, 10_000);
}
// Estimate `tokenOut` amount before swapping
const amountOutExpected = await walletClient.readContract({
address: STEX_AMM,
abi: STEX_AMM_ABI,
functionName: 'getAmountOut',
args: [tokenIn, amountIn, false],
});
console.log(`Amount tokenOut expected on swap: ${amountOutExpected}`);
// Zero slippage, adjust as required
const amountOutMin = amountOutExpected;
// Simulate swap
let request;
try {
request = await walletClient.simulateContract({
chain: hyperEvm,
account,
address: STEX_AMM_SOVEREIGN_POOL,
abi: SOVEREIGN_POOL_ABI,
functionName: 'swap',
args: [
{
isSwapCallback: false,
isZeroToOne,
amountIn,
amountOutMin,
deadline,
recipient,
swapTokenOut: tokenOut,
swapContext: {
externalContext: '0x',
verifierContext: '0x',
swapCallbackContext: '0x',
swapFeeModuleContext: '0x',
},
},
],
gas: 250_000n,
});
} catch (error) {
console.log(`swapStHypeToWHYPE: Swap simulation failed with error: ${error.message}`);
return;
}
console.log('Submitting swap to Sovereign Pool');
// Execute swap
await walletClient.writeContract(request.request);
}
swapStHypeToWHYPE();
Requirements to run the following example:
Ensure you have an EOA wallet with private key PRIVATE_KEY
inside a .env
file, with enough HYPE balance to pay for gas costs.
Ensure that this wallet has enough HYPE or Wrapped HYPE (WHYPE) to swap.
WARNING: Valantis Sovereign Pool does not work with native token, hence HYPE needs to be wrapped into WHYPE and approved before the call to `SovereignPool::swap`.
import { createWalletClient, defineChain, erc20Abi, http, parseEther, publicActions, parseAbi } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// Read `PRIVATE_KEY` from .env
// WARNING: Make sure this is an EOA on HyperEVM (chain id 999) with sufficient HYPE to pay for gas,
// as well as non-zero stHYPE to execute the swap
const dotenv = require('dotenv');
dotenv.config();
// ABI definitions
const STEX_AMM_ABI = parseAbi(['function getAmountOut(address _tokenIn, uint256 _amountIn, bool _isInstantWithdraw) view returns (uint256)']);
const SOVEREIGN_POOL_ABI = parseAbi([
'function swap(SovereignPoolSwapParams _params) returns (uint256, uint256)',
'struct SovereignPoolSwapParams { bool isSwapCallback; bool isZeroToOne; uint256 amountIn; uint256 amountOutMin; uint256 deadline; address recipient; address swapTokenOut; SovereignPoolSwapContextData swapContext; }',
'struct SovereignPoolSwapContextData { bytes externalContext; bytes verifierContext; bytes swapCallbackContext; bytes swapFeeModuleContext; }',
]);
// STEX AMM deployments
// Address of the Valantis Sovereign Pool,
// always the entry-point for swaps
const STEX_AMM_SOVEREIGN_POOL = '0x5365b6EF09253C7aBc0A9286eC578A9f4B413B7D';
// Address of STEX AMM Liquidity Module bound to `STEX_AMM_SOVEREIGN_POOL`.
// This is the main AMM module which handles all swap related pricing logic.
const STEX_AMM = '0x39694eFF3b02248929120c73F90347013Aec834d';
// Temporary solution, until Hyper EVM is supported in viem
const hyperEvm = defineChain({
id: 999,
name: 'HyperEVM',
nativeCurrency: {
decimals: 18,
name: 'HYPE',
symbol: 'HYPE',
},
rpcUrls: {
default: {
http: ['https://rpc.hyperliquid.xyz/evm'],
webSocket: ['wss://rpc.hyperliquid.xyz/evm'],
},
},
blockExplorers: {
default: { name: 'Blockscout', url: 'https://hyperliquid.cloud.blockscout.com/' },
},
contracts: {},
});
async function swapWHYPEToStHYPE() {
// Address of WHYPE
// WARNING: Sovereign Pool (the entry point for swaps)
// only works with ERC-20 tokens. Native token always needs to be wrapped.
const tokenIn = '0x5555555555555555555555555555555555555555';
// Address of stHYPE
const tokenOut = '0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1';
const amountIn = parseEther('0.01');
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
chain: hyperEvm,
transport: http(),
account,
}).extend(publicActions);
// In `STEX_AMM_SOVEREIGN_POOL`, toke0=stHYPE and token1=WHYPE
const isZeroToOne = false;
// 60 seconds deadline. Can use block.timestamp for extra accuracy
const deadline = BigInt(Math.floor(Date.now() / 1_000) + 60);
const recipient = account.address;
const wHypeBalance = await walletClient.readContract({
address: tokenIn,
abi: erc20Abi,
functionName: 'balanceOf',
args: [account.address],
});
if (wHypeBalance < amountIn) {
console.log('Submitting wrapping of HYPE into WHYPE');
await walletClient.writeContract({
chain: hyperEvm,
account,
address: tokenIn,
abi: parseAbi(['function deposit() payable']),
functionName: 'deposit',
value: amountIn - wHypeBalance,
gas: 100_000n,
});
// Wait 10 seconds to reflect state updates on RPC node,
// remove this in prod
await setTimeout(() => {}, 10_000);
}
const wHypeAllowance = await walletClient.readContract({
address: tokenIn,
abi: erc20Abi,
functionName: 'allowance',
args: [account.address, STEX_AMM_SOVEREIGN_POOL],
});
if (wHypeAllowance < amountIn) {
console.log('Submitting WHYPE approval to Sovereign Pool');
await walletClient.writeContract({
chain: hyperEvm,
account,
address: tokenIn,
abi: erc20Abi,
functionName: 'approve',
args: [STEX_AMM_SOVEREIGN_POOL, amountIn],
gas: 100_000n,
});
// Wait 10 seconds to reflect state updates on RPC node,
// remove this in prod
await setTimeout(() => {}, 10_000);
}
// Estimate `tokenOut` amount before swapping
const amountOutExpected = await walletClient.readContract({
address: STEX_AMM,
abi: STEX_AMM_ABI,
functionName: 'getAmountOut',
args: [tokenIn, amountIn, false],
});
console.log(`Amount tokenOut expected on swap: ${amountOutExpected}`);
// Zero slippage, adjust as required
const amountOutMin = amountOutExpected;
// Simulate swap
let request;
try {
request = await walletClient.simulateContract({
chain: hyperEvm,
account,
address: STEX_AMM_SOVEREIGN_POOL,
abi: SOVEREIGN_POOL_ABI,
functionName: 'swap',
args: [
{
isSwapCallback: false,
isZeroToOne,
amountIn,
amountOutMin,
deadline,
recipient,
swapTokenOut: tokenOut,
swapContext: {
externalContext: '0x',
verifierContext: '0x',
swapCallbackContext: '0x',
swapFeeModuleContext: '0x',
},
},
],
gas: 250_000n,
});
} catch (error) {
console.log(`swapHYPEToStHype: Swap simulation failed with error: ${error.message}`);
return;
}
console.log('Submitting swap to Sovereign Pool');
// Execute swap
await walletClient.writeContract(request.request);
}
swapWHYPEToStHYPE();
In this example, we use Valantis' Swap Router swap
function to wrap HYPE
into WHYPE
before interacting with the Sovereign Pool.
import {
createWalletClient,
defineChain,
erc20Abi,
http,
parseEther,
publicActions,
parseAbi,
parseAbiParameters,
encodeAbiParameters,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// Read `PRIVATE_KEY` from .env
// WARNING: Make sure this is an EOA on HyperEVM (chain id 999) with sufficient HYPE to pay for gas,
// as well as non-zero stHYPE to execute the swap
const dotenv = require('dotenv');
dotenv.config();
// ABI definitions
const STEX_AMM_ABI = parseAbi(['function getAmountOut(address _tokenIn, uint256 _amountIn, bool _isInstantWithdraw) view returns (uint256)']);
const SOVEREIGN_POOL_ABI = parseAbi([
'function swap(SovereignPoolSwapParams _params) returns (uint256, uint256)',
'struct SovereignPoolSwapParams { bool isSwapCallback; bool isZeroToOne; uint256 amountIn; uint256 amountOutMin; uint256 deadline; address recipient; address swapTokenOut; SovereignPoolSwapContextData swapContext; }',
'struct SovereignPoolSwapContextData { bytes externalContext; bytes verifierContext; bytes swapCallbackContext; bytes swapFeeModuleContext; }',
]);
const SWAP_ROUTER_ABI = parseAbi([
'function swap(DirectSwapParams _directSwapParams) payable returns (uint256)',
'struct DirectSwapParams { bool[] isUniversalPool; address[] pools; uint256[] amountInSpecified; bytes[] payloads; bool isTokenOutEth; address tokenIn; address tokenOut; address recipient; uint256 amountOutMin; uint256 deadline; }',
]);
// STEX AMM deployments
// Address of the Valantis Sovereign Pool,
// always the entry-point for swaps
const STEX_AMM_SOVEREIGN_POOL = '0x5365b6EF09253C7aBc0A9286eC578A9f4B413B7D';
// Address of STEX AMM Liquidity Module bound to `STEX_AMM_SOVEREIGN_POOL`.
// This is the main AMM module which handles all swap related pricing logic.
const STEX_AMM = '0x39694eFF3b02248929120c73F90347013Aec834d';
// Not a requirement for a swap against a single pool,
// but useful for automatically wrapping HYPE into WHYPE
const SWAP_ROUTER = '0x5Abe35DDb420703bA6Dd7226ACDCb24be71192e5';
// Temporary solution, until Hyper EVM is supported in viem
const hyperEvm = defineChain({
id: 999,
name: 'HyperEVM',
nativeCurrency: {
decimals: 18,
name: 'HYPE',
symbol: 'HYPE',
},
rpcUrls: {
default: {
http: ['https://rpc.hyperliquid.xyz/evm'],
webSocket: ['wss://rpc.hyperliquid.xyz/evm'],
},
},
blockExplorers: {
default: { name: 'Blockscout', url: 'https://hyperliquid.cloud.blockscout.com/' },
},
contracts: {},
});
async function swapHYPEToStHype() {
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
chain: hyperEvm,
transport: http(),
account,
}).extend(publicActions);
// Address of WHYPE,
// even though the user will provide HYPE
const tokenIn = '0x5555555555555555555555555555555555555555';
// Address of stHYPE
const tokenOut = '0xfFaa4a3D97fE9107Cef8a3F48c069F577Ff76cC1';
const amountIn = parseEther('0.01');
const recipient = account.address;
// Estimate `tokenOut` amount before swapping
const amountOutExpected = await walletClient.readContract({
address: STEX_AMM,
abi: STEX_AMM_ABI,
functionName: 'getAmountOut',
args: [tokenIn, amountIn, false],
});
console.log(`Amount tokenOut expected on swap: ${amountOutExpected}`);
// Zero slippage, adjust as required
const amountOutMin = amountOutExpected;
// 60 seconds deadline. Can use block.timestamp for extra accuracy
const deadline = BigInt(Math.floor(Date.now() / 1_000) + 60);
const sovereignPoolPayload = encodeAbiParameters(
parseAbiParameters(
'(bool isZeroToOne, address recipient, address swapTokenOut, uint256 amountOutMin, bytes externalContext, bytes verificationContext, bytes swapFeeModuleContext)',
),
[
{
isZeroToOne: false,
recipient,
swapTokenOut: tokenOut,
amountOutMin,
externalContext: '0x',
verificationContext: '0x',
swapFeeModuleContext: '0x',
},
],
);
// Execute swap from HYPE to stHYPE
// `SWAP_ROUTER` automatically wraps into WHYPE before interacting with `STEX_AMM_SOVEREIGN_POOL`
let request;
try {
request = await walletClient.simulateContract({
chain: hyperEvm,
account,
address: SWAP_ROUTER,
abi: SWAP_ROUTER_ABI,
functionName: 'swap',
args: [
{
isUniversalPool: [false],
pools: [STEX_AMM_SOVEREIGN_POOL],
amountInSpecified: [amountIn],
payloads: [sovereignPoolPayload],
isTokenOutEth: false,
tokenIn,
tokenOut,
recipient,
amountOutMin,
deadline,
},
],
value: amountIn,
gas: 500_000n,
});
} catch (error) {
console.log(`swapHYPEToStHype: Swap simulation failed with error: ${error.message}`);
}
console.log('Submitting HYPE to stHYPE swap to Swap Router');
// Execute swap
await walletClient.writeContract(request.request);
}
swapHYPEToStHype();