Bifrost SLPx Stake
Execute Bifrost vETH liquid staking operations: mint, redeem, and claim.
Contract & Network
vETH is deployed on Ethereum and three L2 networks. The same contract address is used across all chains.
| Chain | ChainId | VETH Contract | WETH (underlying) | Default RPC | Fallback RPC |
|---|
| Ethereum | 1 | INLINECODE0 | INLINECODE1 | INLINECODE2 | INLINECODE3 |
| Base |
8453 |
0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 |
0x4200000000000000000000000000000000000006 |
https://base.publicnode.com |
https://1rpc.io/base |
| Optimism | 10 |
0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 |
0x4200000000000000000000000000000000000006 |
https://optimism.publicnode.com |
https://1rpc.io/op |
| Arbitrum | 42161 |
0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 |
0x82aF49447D8a07e3bd95BD0d56f35241523fBab1 |
https://arbitrum-one.publicnode.com |
https://1rpc.io/arb |
Configuration
On first run, ask the user whether they want to configure custom settings. If not, use the defaults above.
Environment Variables
| Variable | Description | Default |
|---|
| INLINECODE16 | Target chain name (ethereum, base, optimism, arbitrum) | INLINECODE21 |
| INLINECODE22 |
Custom RPC endpoint | Per-chain default from table above |
|
BIFROST_VETH_ADDRESS | VETH contract address (override) |
0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 |
|
BIFROST_PRIVATE_KEY | Private key for agent-side signing (hex, with or without 0x prefix) | Not set (manual signing mode) |
Wallet Setup
Two signing modes. Default is manual signing (no setup needed).
Default: Manual Signing
Output complete transaction details (to, value, data, gas, chainId). User signs with their own wallet (MetaMask, Ledger, CLI, etc.).
Option: Agent-Side Signing
Set BIFROST_PRIVATE_KEY as an environment variable, or import via Foundry keystore:
CODEBLOCK0
When BIFROST_PRIVATE_KEY is set, the agent can sign and broadcast transactions directly using cast send.
Quick Reference
Write Operations
| Operation | Function | Selector | Description |
|---|
| Mint vETH (via ETH) | INLINECODE29 | INLINECODE30 | Stake native ETH to mint vETH. ETH is sent as msg.value. The contract wraps ETH → WETH internally — no ERC-20 approval needed. Reverts EthNotSent() if INLINECODE33 |
| Mint vETH (via WETH) |
deposit(uint256,address) |
0x6e553f65 | Deposit WETH directly to mint vETH for
receiver. Requires prior WETH approval to the VETH contract |
| Redeem vETH |
redeem(uint256,address,address) |
0xba087652 | Burn
shares of vETH to initiate ETH withdrawal for
receiver. ETH enters a redemption queue and is NOT returned instantly. Requires
owner == msg.sender or sufficient allowance |
| Claim as ETH |
withdrawCompleteToETH() |
0x3ec549e9 | Claim ALL completed withdrawals as native ETH. Internally calls
withdrawCompleteTo(this) then unwraps WETH → ETH. Reverts
EthTransferFailed() if ETH transfer fails |
| Claim as WETH |
withdrawComplete() |
0x266a3bce | Claim ALL completed withdrawals as WETH to
msg.sender. Use this if
withdrawCompleteToETH() fails |
| Claim to address |
withdrawCompleteTo(address) |
0xf29ee493 | Claim ALL completed withdrawals as WETH to a specified
receiver address |
Pre-Execution Query Functions
| Query | Function | Selector | Description |
|---|
| Preview deposit | INLINECODE53 | INLINECODE54 | Simulate deposit and return exact vETH shares to be minted |
| Preview redeem |
previewRedeem(uint256) |
0x4cdad506 | Simulate redemption and return exact ETH to be returned |
| Fallback: shares calc |
convertToShares(uint256) |
0xc6e6f592 | Convert ETH amount to vETH shares using current Oracle exchange rate |
| Fallback: assets calc |
convertToAssets(uint256) |
0x07a2d13a | Convert vETH shares to ETH value using current Oracle exchange rate |
| vETH balance |
balanceOf(address) |
0x70a08231 | Get vETH token balance of a specific address |
| Max redeemable |
maxRedeem(address) |
0xd905777e | Maximum vETH shares the owner can redeem in a single tx |
| Claimable ETH |
canWithdrawalAmount(address) |
0x52a630b9 | Returns
(totalAvailableAmount, pendingDeleteIndex, pendingDeleteAmount). First value = ETH ready to claim |
How to Call
Read queries — use eth_call (no gas):
CODEBLOCK1
If previewDeposit or previewRedeem fails, fall back to convertToShares / convertToAssets (same encoding).
Write transactions — use cast send (requires wallet):
CODEBLOCK2
Calldata Encoding (for manual signing output)
- - uint256: convert wei to hex, left-pad to 64 chars
- address: remove 0x prefix, left-pad to 64 chars
- INLINECODE74 returns 3 × uint256 (192 hex chars):
(totalAvailableAmount, pendingDeleteIndex, pendingDeleteAmount). First 64 chars = claimable ETH amount
API 1: Mint vETH (Stake ETH)
Pre-Execution
- 1. Query rate:
previewDeposit(amount) → expected vETH - Check wallet:
BIFROST_PRIVATE_KEY env var or Foundry keystore INLINECODE78 - Display preview and wait for CONFIRM
Transaction
| Field | Value |
|---|
| To | INLINECODE79 |
| Value |
User's ETH amount in wei |
| Data |
0x1166dab6 |
| ChainId | Per selected chain |
Manual Signing Output
CODEBLOCK3
API 2: Redeem vETH (Unstake)
Pre-Execution
- 1. Check
balanceOf(user) ≥ redeem amount - Query
previewRedeem(shares) → expected ETH - Check INLINECODE83
- Display preview (warn: ETH enters queue, NOT instant) and wait for CONFIRM
Transaction
| Field | Value |
|---|
| To | INLINECODE84 |
| Value |
0 |
| Data | ABI-encoded
redeem(shares, userAddr, userAddr) |
| ChainId | Per selected chain |
Encode calldata: INLINECODE87
API 3: Claim Redeemed ETH
Pre-Execution
- 1. Check
canWithdrawalAmount(user) — first return value = claimable amount - If 0: inform user redemption may still be processing
- If > 0: display claimable amount and wait for CONFIRM
Transaction
| Field | Value |
|---|
| To | INLINECODE89 |
| Value |
0 |
| Data |
0x3ec549e9 |
| ChainId | Per selected chain |
Agent Behavior
- 1. Environment check: on first interaction, ask user if they want to configure
BIFROST_CHAIN, BIFROST_RPC_URL, or BIFROST_PRIVATE_KEY. If not, use Ethereum Mainnet defaults with manual signing mode - RPC selection: use
BIFROST_RPC_URL if set; otherwise use per-chain default RPC. Fall back to per-chain fallback RPC on failure - Multi-chain awareness: when user specifies a chain (e.g. "on Base", "on Arbitrum"), switch to that chain's RPC, WETH address, and chainId accordingly
- Wallet detection: check
BIFROST_PRIVATE_KEY env var or Foundry keystore bifrost-agent. If found, ask user whether to use it. If not, output tx data for manual signing - CONFIRM required: display transaction preview (amount, rate, expected output, chain) and require user to type CONFIRM before any write
- Private key import requires CONFIRM: show security warning first, require CONFIRM before accepting key
- Key retention is user-controlled: after tx, ask user whether to keep or delete the key
- Balance pre-check: verify sufficient ETH/vETH before building tx
- Prefer cast, fall back to curl: use pre-computed calldata from selector table if cast fails
- No credential display: never echo private keys; truncate addresses (first 6 + last 4)
- Post-completion tip: if no wallet configured, suggest "set up wallet" after operation
- After successful tx, provide block explorer link:
https://etherscan.io/tx/{hash} (Ethereum), https://basescan.org/tx/{hash} (Base), https://optimistic.etherscan.io/tx/{hash} (Optimism), https://arbiscan.io/tx/{hash} (Arbitrum) - Useful links: direct users to Bifrost vETH page or Bifrost App when relevant
Security
- 1. Private keys are opt-in only — default outputs unsigned tx data
- Explicit CONFIRM for every write operation
- Validate amounts against balance and protocol limits
- Recommend dedicated wallet with limited funds for agent-side signing
Error Handling
| Error | User Message |
|---|
| INLINECODE102 (0x8689d991) | "No ETH included. Please specify the amount." |
| INLINECODE103 |
"ETH transfer failed. Try claiming as WETH with withdrawComplete()." |
|
ZeroWithdrawAmount() (0xd6d9e665) | "No claimable ETH. Your redemption may still be processing." |
|
ERC4626ExceededMaxRedeem (0xb94abeec) | "Redeem exceeds your maximum. Check balance." |
|
Pausable: paused | "VETH contract is paused. Try again later." |
| Insufficient ETH | "Insufficient ETH. Balance: {bal}, Needed: {amount + gas}." |
| Insufficient vETH | "Insufficient vETH. Balance: {bal}, Requested: {amount}." |
| Max withdraw count exceeded | "Too many pending redemptions. Claim existing ones first." |
| RPC failure | "Unable to connect. Retrying with backup endpoint..." |
Notes
- 1.
depositWithETH() wraps ETH → WETH internally via WETH.deposit(). No ERC-20 approval needed. For direct WETH deposits, use deposit(uint256,address) instead (requires WETH approval) - INLINECODE110 internally calls
withdrawCompleteTo(address(this)) to receive WETH, then unwraps to ETH via WETH.withdraw(), then sends ETH to caller. If ETH transfer fails, use withdrawComplete() to receive WETH instead - Redemption is NOT instant —
redeem() / withdraw() add entries to the withdrawal queue, processed in batches via Bifrost cross-chain mechanism - All write functions are protected by
whenNotPaused and nonReentrant (ReentrancyGuardUpgradeable) - Gas estimates are approximate; use
cast estimate for accuracy
Bifrost SLPx 质押
执行 Bifrost vETH 流动性质押操作:铸造、赎回和领取。
合约与网络
vETH 部署在以太坊和三个 L2 网络上。所有链使用相同的合约地址。
| 链 | ChainId | VETH 合约 | WETH(底层资产) | 默认 RPC | 备用 RPC |
|---|
| 以太坊 | 1 | 0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | https://ethereum.publicnode.com | https://1rpc.io/eth |
| Base |
8453 | 0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 | 0x4200000000000000000000000000000000000006 | https://base.publicnode.com | https://1rpc.io/base |
| Optimism | 10 | 0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 | 0x4200000000000000000000000000000000000006 | https://optimism.publicnode.com | https://1rpc.io/op |
| Arbitrum | 42161 | 0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 | 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1 | https://arbitrum-one.publicnode.com | https://1rpc.io/arb |
配置
首次运行时,询问用户是否要配置自定义设置。如果不配置,则使用上述默认值。
环境变量
| 变量 | 描述 | 默认值 |
|---|
| BIFROSTCHAIN | 目标链名称(ethereum、base、optimism、arbitrum) | ethereum |
| BIFROSTRPC_URL |
自定义 RPC 端点 | 上表中每条链的默认值 |
| BIFROST
VETHADDRESS | VETH 合约地址(覆盖) | 0xc3997ff81f2831929499c4eE4Ee4e0F08F42D4D8 |
| BIFROST
PRIVATEKEY | 代理端签名的私钥(十六进制,带或不带 0x 前缀) | 未设置(手动签名模式) |
钱包设置
两种签名模式。默认为手动签名(无需设置)。
默认:手动签名
输出完整的交易详情(to、value、data、gas、chainId)。用户使用自己的钱包(MetaMask、Ledger、CLI 等)签名。
选项:代理端签名
将 BIFROSTPRIVATEKEY 设置为环境变量,或通过 Foundry 密钥库导入:
bash
cast wallet import bifrost-agent --interactive
当设置了 BIFROSTPRIVATEKEY 时,代理可以直接使用 cast send 签名并广播交易。
快速参考
写入操作
| 操作 | 函数 | 选择器 | 描述 |
|---|
| 铸造 vETH(通过 ETH) | depositWithETH() | 0x1166dab6 | 质押原生 ETH 以铸造 vETH。ETH 作为 msg.value 发送。合约内部将 ETH 包装为 WETH — 无需 ERC-20 授权。如果 msg.value == 0,则回滚 EthNotSent() |
| 铸造 vETH(通过 WETH) |
deposit(uint256,address) | 0x6e553f65 | 直接存入 WETH 以为 receiver 铸造 vETH。需要事先向 VETH 合约授权 WETH |
| 赎回 vETH | redeem(uint256,address,address) | 0xba087652 | 销毁 shares 数量的 vETH 以为 receiver 发起 ETH 提现。ETH 进入赎回队列,不会立即返回。需要 owner == msg.sender 或足够的授权额度 |
| 领取为 ETH | withdrawCompleteToETH() | 0x3ec549e9 | 将所有完成的提现领取为原生 ETH。内部调用 withdrawCompleteTo(this) 然后将 WETH 解包为 ETH。如果 ETH 转账失败,回滚 EthTransferFailed() |
| 领取为 WETH | withdrawComplete() | 0x266a3bce | 将所有完成的提现领取为 WETH 到 msg.sender。如果 withdrawCompleteToETH() 失败,请使用此函数 |
| 领取到指定地址 | withdrawCompleteTo(address) | 0xf29ee493 | 将所有完成的提现领取为 WETH 到指定的 receiver 地址 |
执行前查询函数
| 查询 | 函数 | 选择器 | 描述 |
|---|
| 预览存款 | previewDeposit(uint256) | 0xef8b30f7 | 模拟存款并返回将铸造的精确 vETH 份额 |
| 预览赎回 |
previewRedeem(uint256) | 0x4cdad506 | 模拟赎回并返回将返还的精确 ETH |
| 备用:份额计算 | convertToShares(uint256) | 0xc6e6f592 | 使用当前预言机汇率将 ETH 数量转换为 vETH 份额 |
| 备用:资产计算 | convertToAssets(uint256) | 0x07a2d13a | 使用当前预言机汇率将 vETH 份额转换为 ETH 价值 |
| vETH 余额 | balanceOf(address) | 0x70a08231 | 获取指定地址的 vETH 代币余额 |
| 最大可赎回量 | maxRedeem(address) | 0xd905777e | 所有者在单笔交易中可赎回的最大 vETH 份额 |
| 可领取的 ETH | canWithdrawalAmount(address) | 0x52a630b9 | 返回 (totalAvailableAmount, pendingDeleteIndex, pendingDeleteAmount)。第一个值 = 可领取的 ETH |
如何调用
读取查询 — 使用 eth_call(无 gas):
bash
方法 A:cast(推荐)
cast call
\
SIGNATURE>(TYPES>)() \
--rpc-url
方法 B:curl(如果 cast 不可用)
curl -s -X POST \
-H Content-Type: application/json \
-d {jsonrpc:2.0,id:1,method:ethcall,params:[{to:CONTRACT>,data:},latest]}
如果 previewDeposit 或 previewRedeem 失败,回退到 convertToShares / convertToAssets(编码方式相同)。
写入交易 — 使用 cast send(需要钱包):
bash
铸造 vETH(质押原生 ETH)
cast send \
depositWithETH() --value INWEI> \
--rpc-url URL> --private-key KEY>
赎回 vETH(解除质押)
cast send \
redeem(uint256,address,address) INWEI> ADDR> ADDR> \
--rpc-url URL> --private-key KEY>
领取 ETH(提取完成的赎回)
cast send \
withdrawCompleteToETH() \
--rpc-url URL> --private-key KEY>
Calldata 编码(用于手动签名输出)
- - uint256:将 wei 转换为十六进制,左填充至 64 个字符
- address:移除 0x 前缀,左填充至 64 个字符
- canWithdrawalAmount 返回 3 个 uint256(192 个十六进制字符):(totalAvailableAmount, pendingDeleteIndex, pendingDeleteAmount)。前 64 个字符 = 可领取的 ETH 数量