Since the Hydra hard fork in 2022, Conflux has two spaces: Core Space and eSpace. Both spaces use CFX as their native token and they run on the same blockchain. For more details, please refer to the documentation or to this article.
It is possible to move CFX or ERC20 tokens between Core Space and eSpace. This operation is called cross-space transfer. In the next section, we will explain how you can make such transfers.
The easiest way to transfer assets between Core Space and eSpace is to use ConfluxHub. We recommend that you set up two wallets: Fluent for Core Space and MetaMask for eSpace.
Follow these steps to make a cross-space transfer:
Connect Wallet
to connect your Fluent and MetaMask wallets to ConfluxHub.To: Conflux eSpace
at the top shows that you are making a transfer from Core Space to eSpace. If you would like to make a transfer in the other direction, click on the arrow next to this text.Conflux eSpace Destination Address
field, type in your eSpace address or click the MetaMask icon on the right to fill this field automatically.Approve
, click on it to submit an ERC20 token approval first.Transfer
, click on it to make the transfer.Making a cross-space transfer from eSpace to Core Space follows a similar process but it has two main steps: First, transfer the token to the bridge on eSpace. Second, withdraw the token from the bridge on Core Space. Please follow the site's instructions.
⚠️ When making a cross-space transfer, always double check your addresses to avoid accidental asset loss.
Below we will discuss how to make cross-space transfers programmatically.
CIP-90 introduced the concept of two spaces running on one blockchain, and defined a new internal contract to connect the two. This contract is available under the address cfx:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaa2sn102vjv
(hex: 0x0888000000000000000000000000000000000006
) on Core Space.
interface CrossSpace {
/* methods for cross-space CFX transfers */
function transferEVM(bytes20 to) external payable returns (bytes memory output);
function withdrawFromMapped(uint256 value) external;
function mappedBalance(address addr) external view returns (uint256);
/* methods for other cross-space operations */
function callEVM(bytes20 to, bytes calldata data) external payable returns (bytes memory output);
function staticCallEVM(bytes20 to, bytes calldata data) external view returns (bytes memory output);
// ...
}
Each Core Space account has a mapped account in eSpace. The mapped address is calculated the following way:
function cfxMappedEVMSpaceAddress(address) {
const { hexAddress } = decode(address);
const mappedBuf = keccak256(hexAddress).slice(-20);
return checksumAddress(`0x${mappedBuf.toString('hex')}`);
}
You can get the mapped address simply using js-conflux-sdk
:
const { address } = require('js-conflux-sdk');
address.cfxMappedEVMSpaceAddress('cfx:aamgvyzht7h1zxdghb9ee9w26wrz8rd3gj837392dp');
// '0x62954816cE133B41Ab888e1b68b62549DE2f32e0'
For bridging CFX from Core Space to eSpace, you can use CrossSpace.transferEVM
. When you call this internal contract method, the attached CFX tokens are first transferred from your account to its mapped account on eSpace, then transferred from the mapped account to the destination address to
.
const { address, Conflux, Drip } = require('js-conflux-sdk');
// private key of the sender account on Core Space
const PRIVATE_KEY = '0x...';
// receiver address on eSpace
const to = '0x1111111111111111111111111111111111111111';
async function main() {
const conflux = new Conflux({ url: 'https://main.confluxrpc.com', networkId: 1029 });
const account = conflux.wallet.addPrivateKey(PRIVATE_KEY);
const CrossSpace = conflux.InternalContract('CrossSpaceCall');
console.log(`Sender: ${account.address}`);
console.log(`Mapped: ${address.cfxMappedEVMSpaceAddress(account.address)}`);
console.log(`Receiver: ${to}`);
const receipt = await CrossSpace
.transferEVM(to)
.sendTransaction({ from: account, value: Drip.fromCFX(1) })
.executed();
console.log(receipt);
}
main();
⚠️ It is important to note that this whole process happens within a single transaction. Through this atomicity, cross-space transfers generally offer much better security than cross-chain operations.
For bridging CFX from eSpace back to Core Space, you first need to send the tokens to the receiver account's mapped account on eSpace. After this, the receiver account can call CrossSpace.withdrawFromMapped
to withdraw the tokens to Core Space.
const { address, Conflux, Drip } = require('js-conflux-sdk');
// private key of the receiver account on Core Space
const PRIVATE_KEY = '0x...';
async function main() {
const conflux = new Conflux({ url: 'https://main.confluxrpc.com', networkId: 1029 });
const account = conflux.wallet.addPrivateKey(PRIVATE_KEY);
const CrossSpace = conflux.InternalContract('CrossSpaceCall');
console.log(`Sender: ${account.address}`);
console.log(`Mapped: ${address.cfxMappedEVMSpaceAddress(account.address)}`);
const receipt = await CrossSpace
.withdrawFromMapped(Drip.fromCFX(1))
.sendTransaction({ from: account })
.executed();
console.log(receipt);
}
main();
For ERC20 cross-space transfers, Conflux offers an ERC20 bridge. This bridge consists of two main contracts, one in Core Space (ConfluxSide.sol
), one in eSpace (EvmSide.sol
).
Let us assume that you want to transfer some USDT from Core Space to eSpace.
ConfluxSide
contract on Core Space.ConfluxSide
notifies EvmSide
of the transfer through CrossSpace.callEVM
.EvmSide
checks if the token (USDT) have a corresponding ERC20 token on eSpace. If not, then it deploys one.EvmSide
mints the same amount of eSpace USDT as was locked in Core Space.When transferring your USDT back to Core Space, the tokens are first burnt on eSpace, and then the corresponding tokens are unlocked on Core Space. The process is analogous for tokens originally deployed on eSpace. Please refer to the code for details.