Karak 2024 NativeVault shares and transfer restrictions
Karak's 2024 NativeVault rejects wallet-to-wallet share transfers with the NotImplemented error. Both transfer and transferFrom unconditionally revert, so changing the recipient or adding an allowance can't enable them. Its native accounting can still mint and burn shares, which explains how balances change despite the transfer restriction.
Native share balances can change through internal accounting even though the 2024 NativeVault implementation rejects transfers between wallets.
Share transfers and native balance updates
Sending existing shares and adjusting a native position's share balance reach different functions in the 2024 implementation. A caller uses transfer or transferFrom to request movement of existing shares between holders. Internal accounting methods issue or remove shares when the contract updates the assets credited to a node owner. They're different operations, even when a wallet displays their effects in the same balance field.
| Function | Purpose | Execution path | Share balance effect |
|---|---|---|---|
transfer
|
Send existing shares to a recipient | External transfer call | Reverts without moving shares |
transferFrom
|
Move existing shares on a holder's behalf | External delegated transfer call | Reverts without moving shares |
_increaseBalance
|
Credit an increase in restaked assets | Internal accounting path | Mints shares if the enclosing call succeeds |
_decreaseBalance
|
Debit a reduction in restaked assets | Internal accounting path | Burns shares if the enclosing call succeeds |
| Internal issuance and burning don't provide an external route for transferring existing shares. | |||
The share contract's implementation determines which operations it supports. When it matches this native design, a transfer trace ending in
NotImplemented
confirms an unsupported operation. A successful accounting adjustment instead changes the credited owner's balance through minting or burning. The corresponding mint or burn and updated owner balance confirm the adjustment. A token's presence in a wallet doesn't show that a transfer completed.
Why do NativeVault share transfers fail with NotImplemented?
The 2024 NativeVault implementation rejects both transfer and transferFrom calls with an unconditional NotImplemented revert. Neither override reaches a balance update or a recipient-specific condition. Although their signatures declare a Boolean return value, execution ends with the error before either function returns that value.
The restriction therefore applies to the requested operation, without depending on the sender's displayed share balance. It isn't a waiting period that expires after a snapshot or an allowance update. The function bodies don't read those values before reverting. A failed share transfer also doesn't establish that the underlying native position has disappeared; it establishes that this transfer call couldn't move its shares.
Approvals can't bypass the transfer override
An allowance authorizes a spender to request a transfer; it doesn't change the function that executes that request. Here,
transferFrom
immediately reverts without checking the spender's allowance. An approval, even if successfully recorded, therefore can't unlock this share-transfer path. This distinction also matters for integrations that expect to take custody of shares after receiving authorization: permission alone can't supply the missing transfer behavior.
Owner records and the native position
A native position associates its node and validator records with an owner address, alongside that address's share balance. The 2024 implementation keeps these records in separate mappings. A change in share quantity and a change in recorded node ownership describe different state changes. The transfer overrides implement neither operation, while the native accounting helpers update balances for the specified owner.
The addresses behind the balance
The owner credited with shares
The internal minting helper credits the address passed into the accounting update. It also increases that owner's recorded restaked ETH. Its burning counterpart debits the corresponding owner when accounting removes assets. The share balance therefore participates in a position that contains additional owner-specific records. Reading a share amount alone doesn't reveal the node address or the validator details behind that credited position.
The node and validator records
The contract maps a node address to its owner and stores validator details under the owner's native-node record.
getNodeOwner
exposes the node-to-owner relationship.
getValidatorDetails
takes a validator public-key hash and a node owner to locate the relevant entry. These relationships explain which position an accounting adjustment concerns. A recipient parameter in an unrelated operation doesn't automatically replace the owner in these mappings.
Share accounting can change without a transfer
Successful native accounting updates can issue or remove shares while wallet-to-wallet transfers remain disabled. The contract ties these adjustments to changes in restaked ETH credited to a node owner. That relationship explains a growing or shrinking share balance without requiring a transfer from another holder. It also separates the underlying asset adjustment from the share quantity that represents it.
For a positive adjustment,
_increaseBalance
calculates shares with
convertToShares
and calls
_mint
for the credited owner. It increases both the vault's recorded total assets and that owner's total restaked ETH. The asset amount supplied to the helper and the share amount that it issues use different accounting units. The conversion determines their relationship.
For a reduction,
_decreaseBalance
uses
previewWithdraw
to calculate the shares associated with the asset debit. It runs the withdrawal hook and burns those shares from the affected owner. Both asset records then decrease by the asset amount. This internal use of a withdrawal calculation doesn't invoke the disabled external
withdraw
entry point.
_updateBalance
chooses the adjustment from a signed asset delta. A positive delta selects issuance, a negative delta selects burning and a zero delta leaves the balance unchanged.
Snapshots supply one source of these adjustments. Once the snapshot's remaining proof count reaches zero,
_updateSnapshot
combines the node balance with the signed validator balance delta. It credits the node ETH, records the completed snapshot timestamp and updates share accounting. An unfinished snapshot retains its stored progress. A node with no active validators can finish its snapshot in the call that starts it; otherwise, completion requires all remaining validator proofs.
Generic deposit and redemption methods have separate limits
The native implementation also rejects standard external deposit, mint, withdraw and redeem calls with
NotImplemented
. Their presence in the contract interface doesn't make them executable entry paths. This extends the integration boundary beyond a wallet's Send command. An application that expects ordinary token-vault deposit or immediate redemption behavior can't infer that support from the inherited method names.
External calls and internal helpers
External
mint
and internal
_mint
are distinct functions. The former exposes a caller-facing entry point that this implementation disables; the latter issues shares within native accounting. Likewise, the rejected public
withdraw
method differs from the internal reduction that burns shares. The underscore is part of the function identifier and helps distinguish these paths when reading a call trace.
Previews and conversions
previewDeposit
and
previewRedeem
also explicitly revert in this implementation. Other calculations still support native bookkeeping, including
convertToShares
and
previewWithdraw
. Their availability doesn't enable the rejected operations. A readable conversion describes an accounting relationship; it doesn't independently create an executable deposit or redemption path.
An ETH recipient doesn't become the share owner
The native withdrawal recipient identifies where ETH should go, while the node owner identifies whose position supplies it. In the 2024 code, the queued withdrawal stores these addresses separately. Finalization reduces the recorded owner's balance and sends the resulting ETH to the stored recipient. That asset payment doesn't transfer native shares to the recipient. Withdrawal conditions govern whether payment can proceed, so the recipient field offers no shortcut around the share-transfer restriction and doesn't itself prove that ETH has arrived.
The token vault uses a different share-transfer path
The separate 2024 token
Vault
implementation inherits share-transfer behavior instead of installing NativeVault's unconditional transfer overrides. Its
startRedeem
path explicitly calls
transferFrom
to move the staker's shares into the vault. That gives the transfer operation a concrete role in token-vault redemption accounting, although other conditions still govern whether the enclosing operation succeeds.
This distinction keeps the native restriction within its proper scope. The token vault's redemption logic doesn't create a transfer route for native shares. Equally, a native share-transfer failure doesn't establish that every restaking vault rejects transfers. The relevant object is the share contract that holds the balance, together with the implementation responsible for its functions.
Slashing changes the assets behind a share balance
A share quantity can remain unchanged while slashing reduces the assets that support it. In the native design,
slashAssets
reduces the vault's recorded total assets without directly burning each owner's shares. After slashing, convertToAssets calculates the asset amount represented by a share balance. Share portability doesn't determine whether a position faces losses from the security obligations of Distributed Secure Services (DSS).
The internal
_transferToSlashStore
helper deals with ETH assigned to slashing. It calculates the gap between a node owner's total restaked ETH and the asset equivalent of that owner's shares. The helper limits the ETH sent to the slash store to the smaller of that gap and the node's available ETH. This ETH movement has a different object and destination from a share transfer between wallets.
The word
transfer
inside that helper's name describes movement of slashed assets. It doesn't make the disabled public share-transfer methods available.
Does ERC-4626 require every vault share to be transferable?
ERC-4626 permits non-transferable vault shares to reject transfer and transferFrom calls. The standard's share interface therefore doesn't promise unrestricted wallet-to-wallet movement. That allowance addresses share portability specifically. It doesn't establish that a contract satisfies every other requirement of the standard, particularly when its specialized design disables additional entry points.
An integration that requires custody of shares needs an executable transfer path. A balance-display integration may only need readable balances and metadata. These requirements differ, so a working portfolio display doesn't establish that the same contract supports custody transfers. The native override determines that operation's behavior even when other inherited methods remain useful.
Legacy access and the 2024 implementation boundary
V1 staking, V2 staking and the K2 bridge now appear as legacy access. The transfer restriction described here belongs to the 2024 NativeVault implementation. The native position's contract code determines its available calls. The V2 interface's Deposit and Unstake labels don't identify the share-transfer implementation.
The contract address and its network identify the relevant share balance; the code behind that address governs its behavior. Configured names and symbols describe the token without proving which implementation handles its transfers. Within the 2024 native design, wallet-to-wallet movement ends at the explicit revert in
transferFrom
.
Helpful answers about Karak
Does a higher gas limit bypass NotImplemented on a native share transfer?
A higher gas limit can't bypass the explicit NotImplemented revert in the 2024 NativeVault transfer methods. Gas limits govern execution resources; they don't replace a function's behavior. This error differs from running out of gas, so increasing the limit doesn't turn the rejected share-transfer operation into a supported one.
Can transferring zero native shares avoid the revert?
A zero-value share transfer still reverts in the 2024 NativeVault implementation. Both transfer and transferFrom ignore their amount argument before executing NotImplemented. The restriction therefore includes a call that requests no share movement. A zero-value transfer isn't a successful test of transfer compatibility for this particular contract.
What does NativeVault's implementation() getter return?
The 2024 NativeVault implementation() getter returns its configured NativeNode implementation. It supports the node's beacon-based implementation lookup, so its return value doesn't identify the vault code that implements share transfers. Confusing the node implementation with the share contract's implementation can lead an integration to inspect the wrong functions.
How many decimal places did the 2024 NativeVault configure for shares?
The 2024 NativeVault initialization sets its share decimal precision to 18. The decimals() getter returns that configured value for interpreting raw balances. Decimal precision defines display scaling, while the conversion functions determine the asset value represented by shares. It doesn't set a fixed asset-per-share ratio or confer transfer support.
Do snapshot proofs submitted by another address transfer my native shares?
Submitting snapshot proofs doesn't transfer native shares to the submitting address. The 2024 function permits proof submission for a specified node owner, subject to its proof and snapshot checks. Any resulting accounting adjustment targets that owner's position. The caller's role in submitting valid proofs doesn't make the caller the owner of the credited shares.