Enabling Bridging
Bridging moves assets between Ethereum L1 and a Prividium™ network. Enabling it requires configuration across three independent layers.
- Permissions-API rules: function permissions on the L2 system contracts (and per-token L2 ERC-20 contracts) that the User Panel calls during bridging.
- L1 Transaction Filterer: an optional L1 contract that controls which L1→L2 deposits are accepted.
- Frontend settings: user-panel environment variables that show or hide the bridge UI.
How bridging uses permissions
Prividium™ applies its deny-by-default permission model to system contracts. After system-contract sync, every bridge function starts as Forbidden. Create the rules below to turn bridging on for a new environment.
Deposits and withdrawals use permissions differently:
- Deposits move funds in through an L1 transaction. The L2 crediting runs as an automatic priority-queue transaction
that bypasses Prividium and needs no L2 write permission. Before it can start a deposit, though, the User Panel
reads token metadata from
L2NativeTokenVaulton L2. If those reads are Forbidden, the User Panel cannot start the deposit. Deposits therefore require L2 read permissions. - Withdrawals are user-signed L2 transactions. The User Panel needs both the read permissions (same token metadata) and write permissions on the withdrawal functions.
The three bridge system contracts are registered automatically:
| Contract | Address |
|---|---|
L2BaseToken | 0x000000000000000000000000000000000000800A |
L2AssetRouter | 0x0000000000000000000000000000000000010003 |
L2NativeTokenVault | 0x0000000000000000000000000000000000010004 |
Create the permission rules below from each contract's Function Permissions tab in the Admin Dashboard, or through the API.
Token-resolution read permissions
The User Panel reads token metadata from L2 before every deposit and withdrawal, for both ETH and ERC-20. Grant these
reads as All Users (public) because they are read-only view calls. You can also gate them by role if your policy
requires it.
L2NativeTokenVault at 0x0000000000000000000000000000000000010004 (read access):
| Function | Selector | Why needed |
|---|---|---|
L1_CHAIN_ID() | 0x2f90b184 | Resolve the chain's asset identifiers |
BASE_TOKEN_ASSET_ID() | 0xcb944dec | Detect whether the asset is the base token |
WETH_TOKEN() | 0x37d277d4 | Returns the configured wrapped base token address |
originChainId(bytes32) | 0x5f3455b5 | Determine where the asset originates |
l2TokenAddress(address) | 0xf5f15168 | Map the L1 token to its L2 address |
Withdrawals also read one function on L2AssetRouter at 0x0000000000000000000000000000000000010003 (read access):
| Function | Selector | Why needed |
|---|---|---|
l1TokenAddress(address) | 0xf54266a2 | Map the L2 token back to its L1 address (withdrawal) |
The User Panel reads the first four L2NativeTokenVault functions on every deposit and withdrawal, including plain
ETH. Any user who lacks full sequencer access needs all four permitted to bridge in either direction.
Enabling deposits
A deposit requires:
- The token-resolution read permissions above (the four common functions for ETH; add
l2TokenAddressfor ERC-20). - The L1 Transaction Filterer to allow the deposit (see Transaction Filterer).
The L2 execution of a deposit is a priority-queue transaction and needs no L2 write permission. The reads above are the only L2-side configuration a deposit needs.
Enabling withdrawals
A withdrawal requires the read permissions above plus the following write permissions. Grant as All Users
(public) or Check Role.
| Contract | Function | Selector | Asset |
|---|---|---|---|
L2BaseToken | withdraw(address) | 0x51cff8d9 | ETH / base |
L2AssetRouter | withdraw(bytes32,bytes) | 0x4a2e35ba | ERC-20 |
L2NativeTokenVault | ensureTokenIsRegistered(address) | 0x19a2a285 | ERC-20 |
| L2 ERC-20 token | approve(address,uint256) | 0x095ea7b3 | ERC-20 |
Withdrawals must also be enabled in the frontend with VITE_WITHDRAWAL_ENABLED=true (see
Frontend configuration).
ERC-20 considerations
- Per-token permissions are required. Each bridgeable ERC-20 has its own L2 address and needs its own rules:
approve(address,uint256)(write) for withdrawals, andallowance(address,address)andbalanceOf(address)as reads. Add a rule for every token you intend to bridge, because no wildcard covers them. - Token registration.
ensureTokenIsRegistered(address)registers the token inL2NativeTokenVaultand returns its asset id; it must be permitted (write) before an ERC-20 withdrawal can be routed. - Frontend token list. Additional ERC-20s shown in the user panel are configured with
VITE_BRIDGE_TOKENS(JSON). The L2 token address for each entry must be resolved correctly; see the private user-panel environment variable docs for the schema.
Transaction Filterer
The Transaction Filterer is an optional L1 contract that gates which L1→L2 transactions (including deposits) are queued. It is the deposit-side admission control and is independent of the L2 permission rules above.
Key behaviors:
setDepositsAllowed(bool)toggles whether non-whitelisted addresses may deposit.- When deposits are allowed, a non-whitelisted EOA may deposit only to its own address.
- Whitelisted addresses (
grantWhitelist/revokeWhitelist) may perform arbitrary L1→L2 transactions. - L1 contract addresses are aliased during deposit, so L1 contracts cannot deposit unless explicitly whitelisted.
If deposits are rejected at L1 (no L2 priority transaction appears) even though permissions are correct, check the
filterer's depositsAllowed flag and the sender's whitelist status. For deployment and the full method reference, see
Transaction Filterer.
Frontend configuration
These user-panel environment variables control the bridge UI only. They do not create permission rules and do not affect the Transaction Filterer.
| Variable | Purpose | Default |
|---|---|---|
VITE_BRIDGING_ENABLED | Show the bridge UI (deposits) | false |
VITE_WITHDRAWAL_ENABLED | Show withdrawal features in the UI | false |
VITE_BRIDGE_TOKENS | Additional ERC-20 tokens for bridging | [] |
VITE_L1_RPC_URL | L1 RPC endpoint (required when bridging) | – |
See the private user-panel environment variable docs for the full list.
Verification checklist
Confirm a permission rule exists for each (contract, function) below, that its access type matches, and that its
rule is All Users (public) or a Check Role rule the user holds. None should remain Forbidden.
Deposit — ETH
-
L2NativeTokenVault.L1_CHAIN_ID()— read -
L2NativeTokenVault.BASE_TOKEN_ASSET_ID()— read -
L2NativeTokenVault.WETH_TOKEN()— read -
L2NativeTokenVault.originChainId(bytes32)— read - Transaction Filterer allows the sender
Deposit — ERC-20 (all of the above, plus)
-
L2NativeTokenVault.l2TokenAddress(address)— read
Withdraw — ETH (the four common reads, plus)
-
L2BaseToken.withdraw(address)— write -
VITE_WITHDRAWAL_ENABLED=true
Withdraw — ERC-20 (the four common reads, plus)
-
L2AssetRouter.l1TokenAddress(address)— read -
L2NativeTokenVault.ensureTokenIsRegistered(address)— write -
<token>.approve(address,uint256)— write -
<token>.allowance(address,address)— read -
L2AssetRouter.withdraw(bytes32,bytes)— write -
VITE_WITHDRAWAL_ENABLED=true
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Deposit fails immediately, no L1 transaction is sent | A token-resolution read permission on 0x…010004 is Forbidden (e.g. L1_CHAIN_ID). Grant the four common reads. |
| Deposit rejected at L1 / no L2 priority transaction appears | Transaction Filterer depositsAllowed=false, or the sender is not whitelisted (or is an aliased L1 contract). |
| Withdrawal "not authorized" | A write permission is missing (withdraw, ensureTokenIsRegistered, or the token's approve). |