Bridging $EWT Between EWX <--> ETH (Manual)
This guide explains the steps required for bridging $EWT tokens between Energy Web X (EWX) and Ethereum Mainnet (ETH) manually via Polkadot.js explorer and Energy-Bridge-ETH contract interactions.
Key Components
Tier-1 (T1): Ethereum Mainnet (ETH, EVM)
Tier-2 (T2): Energy Web X (EWX, Substrate parachain)
Energy Bridge Contract: Deployed on Ethereum, coordinates token transfers to/from EWX
Token Manager Pallet: Handles bridge operations on EWX
t2PublicKey: the EWX Substrate account’s public key in hex bytes
Lowering Tokens: EWX → Ethereum
This guide explains how to move $EWT from Energy Web X (EWX, Tier-2) to Ethereum (Tier-1) using the on-chain bridge. The process involves scheduling a lower on EWX and then claiming it through the Energy Bridge smart contract on Ethereum.
Step 1: Schedule a Lower on EWX
Navigate to Polkadot.JS Portal connected to the EWX network.
Go to
Developer→Extrinsics.Under
using the selected account, choose the EWX account that holds the tokens to be lowered.Under
submit the following extrinsic, select:tokenManager → scheduleDirectLower(from, tokenId, amount, t1Recipient).Fill in the parameters:
from: EWX account to lower from (your account)
tokenId: Ethereum address of the token to lower (e.g., the EWT ERC-20 address)
amount: Amount to lower (in wei units, e.g.,
1000000000000000000for 1 EWT)t1Recipient: Ethereum address to receive the lowered tokens
Submit and sign the transaction.
The transaction will schedule a lowering event after a predefined delay (check
tokenManager.lowerSchedulePeriodinDeveloper→Chain Statefor the exact number of blocks).

Step 2: Monitor the Scheduled Lower
Go to
Network→Explorerand look for the event:TokenManager.LowerRequestedClick on the event to view details and copy the
lowerId.Alternatively:
Network→Schedulershows pending scheduled lowers.Network→Event Calendardisplays the approximate execution date and time.

Step 3: Wait for Execution and Obtain the Proof
After the scheduled number of blocks elapse, the system will execute the lower.
Once executed, a new event TokenManager.LowerReadyToClaim will appear.
Go to
Developer→Chain State.Select
tokenManager→lowersReadyToClaim.Toggle
include optionand enter yourlowerIdto filter results.Retrieve the proof, specifically the
encodedLowerDatafield. This proof will be required on Ethereum.

Step 4: Claim the Lower on Ethereum
Visit the Energy Bridge Contract (Ethereum mainnet): Energy Bridge Proxy Contract
Connect your Ethereum wallet.
Under the
Write Proxytab, selectclaimLower.Paste the
encodedLowerDataretrieved from EWX.Click
Write, sign the transaction, and wait for confirmation.
Upon success, the tokens will be unlocked and transferred to your Ethereum address, and a new Lower transaction will appear on the contract.

Notes
Ensure your Ethereum account has sufficient ETH to cover gas fees.
The lowering process is asynchronous, the on-chain scheduler on EWX must reach the required block height before the proof becomes claimable.
The bridge only supports whitelisted tokens (e.g., $EWT).
Lifting Tokens: Ethereum → EWX
There are two methods for lifting EW tokens: Lift, which first requires approving the bridge contract to spend your tokens; and PermitLift, which combines an ERC-2612 permit (EIP-712 signature) with the lift, allowing tokens to be lifted in a single transaction with no prior approve() needed.
Alternative 1: Lift
Step 1: Access the Energy Bridge Contract
Visit the Energy Bridge Contract (Ethereum mainnet): Energy Bridge Proxy Contract
Select
Write as ProxyConnect your Ethereum wallet holding $EWT (e.g., MetaMask) by clicking on
Connect web3.
Step 2: Approve the Bridge to Spend Your Tokens
Before lifting, the bridge must be authorized to transfer your tokens.
Identify the ERC-20 EWT token contract on Ethereum: EWT ERC-20 Proxy Contract
Click the link above, or find the address through the Polkadot.JS Portal:
Developer→Chain State→tokenManager→avtTokenContract():H160Click "+", and copy the address in the
tokenManager.avtTokenContract: H160field.
Once the contract is open on Etherscan →
Contract→Write as Proxy→approve(spender, amount).In the
spenderfield, enter the Energy Bridge address:0x5dDed30f8cd557257CcDC4a530cB77AC45f0259DIn the
amountfield, enter the number of tokens (in wei) you wish to lift.Example:
1000000000000000000= 1 EWT
Click
Write, sign, and wait for confirmation.

Step 3: Execute the Lift
Return to the Energy Bridge Proxy Contract page.
Under
Write as Proxy, locate theliftfunction.Fill in the parameters:
token
ERC-20 token address to lift
0xB66a5D30D04f076E78ffB0d045C55846Fdcde928
t2PublicKey (bytes)
Hexadecimal public key of your EWX account
Convert your EWX address using Polkadot.js → Developer → Utilities → Address Converter
amount
Number of tokens (in wei) to lift
1000000000000000000 (= 1 EWT)
Click
Write, confirm the transaction, and wait for it to be mined.

After submission, your tokens are locked in the bridge contract, and a Lifted event is emitted on Ethereum.

Step 4: Monitor the Lift on EWX
Once the bridge relayer processes the Ethereum Lifted event (typically within 5–10 minutes):
On the EWX explorer, open
Network→Explorer.Look for
tokenManager.AVTLiftedevents.Optionally, check
Developer→Chain State→ethereumBridge→activeEthereumRange()to see the Ethereum block range being processed.When your transaction is within that range, your lifted tokens will appear in your EWX account.
To verify, open Accounts → My Accounts and confirm the updated balance.

Alternative 2: PermitLift
permitLift simplifies the process by embedding an ERC-2612 permit signature directly into the lift call.
This removes the need for a separate approve() step.
Steps:
On Etherscan, open the same Energy Bridge Proxy Contract →
Write as Proxy.Locate the
permitLiftfunction.Prepare an EIP-712 signature (v, r, s) offline for the ERC-2612 permit corresponding to your token.
Fill the parameters:
token
ERC-20 token address (0xB66a5D30D04f076E78ffB0d045C55846Fdcde928)
t2PublicKey
Hex of your EWX account (as above)
amount
Amount in wei
deadline
Expiration timestamp for the permit
v, r, s
Components of the EIP-712 signature
Click Write, sign, and confirm. Your lift is processed in a single transaction.
Monitoring on EWX follows the same path as the standard lift.
Programmatic example permitLift.js
#!/usr/bin/env node
const { ethers } = require("ethers");
require("dotenv").config();
async function main() {
const {
RPC_URL,
PRIVATE_KEY,
BRIDGE_ADDR,
TOKEN_ADDR,
T2_PUBKEY,
AMOUNT = "0.1",
DECIMALS = "18",
DEADLINE_SECS = "3600",
} = process.env;
if (!RPC_URL || !PRIVATE_KEY) {
console.error("Missing RPC_URL or PRIVATE_KEY env vars");
process.exit(1);
}
if (!BRIDGE_ADDR || !TOKEN_ADDR || !T2_PUBKEY) {
console.error("Missing BRIDGE_ADDR, TOKEN_ADDR or T2_PUBKEY env vars");
process.exit(1);
}
const provider = new ethers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
// Minimal ABIs required for this flow
const BRIDGE_ABI = [
"function permitLift(address token, bytes32 t2PubKey, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external returns (bool)"
];
const TOKEN_ABI = [
"function name() view returns (string)",
"function nonces(address owner) view returns (uint256)"
];
const bridge = new ethers.Contract(BRIDGE_ADDR, BRIDGE_ABI, signer);
const token = new ethers.Contract(TOKEN_ADDR, TOKEN_ABI, signer);
const amount = ethers.parseUnits(AMOUNT, Number(DECIMALS));
const deadline = Math.floor(Date.now() / 1000) + Number(DEADLINE_SECS);
const owner = await signer.getAddress();
const domain = {
name: await token.name(),
version: "1",
chainId: (await signer.provider.getNetwork()).chainId,
verifyingContract: await token.getAddress(),
};
const types = {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline",type: "uint256" },
],
};
const message = {
owner,
spender: await bridge.getAddress(),
value: amount,
nonce: await token.nonces(owner),
deadline,
};
const sig = await signer.signTypedData(domain, types, message);
const { v, r, s } = ethers.Signature.from(sig);
const tx = await bridge.permitLift(await token.getAddress(), T2_PUBKEY, amount, deadline, v, r, s);
const receipt = await tx.wait();
console.log("permitLift tx: ", receipt.hash);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
Last updated