Integration examples
Swap from stHYPE to Wrapped HYPE
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 Sovereign Pool.
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 https://www.stakedhype.fi/.
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 = '0xEF340bA1e70510739f0E82E0c9511497182FBe86';
// 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 = '0xE20725BEa0aF2b125Fe21882385275cDbfA94298';
// 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();
Swap from WHYPE to stHYPE
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 = '0xEF340bA1e70510739f0E82E0c9511497182FBe86';
// 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 = '0xE20725BEa0aF2b125Fe21882385275cDbfA94298';
// 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();
Swap from HYPE to stHYPE
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 = '0xEF340bA1e70510739f0E82E0c9511497182FBe86';
// 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 = '0xE20725BEa0aF2b125Fe21882385275cDbfA94298';
// 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();
Last updated
Was this helpful?