Multi-Chain Profiles
How ERC-7866 enables consistent identities across blockchains.
Single Identity, Multiple Chains
Deploy SoulProfile to multiple EVM networks and users maintain a consistent identity:
User: 0xAlice (same address across all chains)
Ethereum: username "alice" → alice@eth.soul
Polygon: username "alice" → alice@polygon.soul
Arbitrum: username "alice" → alice@arb.soul
Base: username "alice" → alice@base.soul
Optimism: username "alice" → alice@op.soulSetup
Deploy to Multiple Networks
Deploy the same SoulProfile contract to each network:
# Deploy to Ethereum
forge create contracts/core/SoulProfile.sol:SoulProfile \
--rpc-url $ETHEREUM_RPC
# Deploy to Polygon
forge create contracts/core/SoulProfile.sol:SoulProfile \
--rpc-url $POLYGON_RPC
# Deploy to Arbitrum
forge create contracts/core/SoulProfile.sol:SoulProfile \
--rpc-url $ARBITRUM_RPCCreate Profile Once Per Chain
Users create profiles on each network they want to participate in:
// On Ethereum
ethereumProfile.createProfile("alice", "ipfs://avatar...");
// On Polygon
polygonProfile.createProfile("alice", "ipfs://avatar...");
// On Arbitrum
arbitrumProfile.createProfile("alice", "ipfs://avatar...");Profile Format
Human-Readable Identifiers
Format: username@network_slug.soul
alice@eth.soul (Ethereum mainnet)
alice@sepolia.soul (Ethereum Sepolia)
alice@polygon.soul (Polygon mainnet)
alice@mumbai.soul (Polygon Mumbai testnet)
alice@arb.soul (Arbitrum One)
alice@base.soul (Base)
alice@op.soul (Optimism)Decentralized Identifiers (DIDs)
Format: did:eip155:chainId:address
Based on EIP-155 chain identifiers:
did:eip155:1:0xAlice... (Ethereum mainnet)
did:eip155:11155111:0xAlice... (Ethereum Sepolia)
did:eip155:137:0xAlice... (Polygon)
did:eip155:80001:0xAlice... (Polygon Mumbai)
did:eip155:42161:0xAlice... (Arbitrum One)
did:eip155:8453:0xAlice... (Base)
did:eip155:10:0xAlice... (Optimism)Use Cases
Cross-Chain Gaming
A player maintains one identity across games on different chains:
GameA on Ethereum → alice@eth.soul (Warrior level 50)
GameB on Polygon → alice@polygon.soul (Mage level 30)
GameC on Arbitrum → alice@arb.soul (Ranger level 45)
All owned by: 0xAliceEach game can:
- Look up profiles by address on their chain
- Set game-specific avatars
- Read the default avatar as fallback
- Discover players cross-chain through indexing
Multi-Chain DApps
A DApp deployed on multiple networks:
// app.sol on Ethereum
IErc7866 ethereumProfiles = new SoulProfile();
// app.sol on Polygon
IErc7866 polygonProfiles = new SoulProfile();
function userProfile(address user) returns (Profile memory) {
// Fetch from current chain's profile contract
if (!_currentChainProfiles.hasProfile(user)) {
return empty;
}
return _currentChainProfiles.getProfile(user);
}Profile Discovery
Index profiles across chains:
// Build a multi-chain username registry
const profiles: Record<string, ChainProfiles> = {};
async function indexProfiles() {
const chains = [
{ name: "ethereum", rpc: "..." },
{ name: "polygon", rpc: "..." },
{ name: "arbitrum", rpc: "..." }
];
for (const chain of chains) {
const contract = new ethers.Contract(
PROFILE_ADDRESS,
ABI,
new ethers.JsonRpcProvider(chain.rpc)
);
// Listen for ProfileCreated events
contract.on("ProfileCreated", (owner, username) => {
if (!profiles[username]) {
profiles[username] = {};
}
profiles[username][chain.name] = owner;
});
}
}
// Look up where a user has profiles
function getUserProfiles(username: string) {
return profiles[username] || {};
// Returns: { ethereum: 0xAlice, polygon: 0xAlice, arbitrum: 0xAlice }
}Address Consistency
Users keep the same address across all EVM chains. This is a fundamental property of EVM:
- Same mnemonic/private key → same address on every chain
- No bridging or cross-chain messaging needed
- Each chain's contract is independent
- Profiles are not synchronized (each chain is separate)
Multi-Chain Avatars
Each network has independent avatar settings:
// On Ethereum
ethereumProfile.setDappAvatar("GameA", "ipfs://eth-avatar", true);
// On Polygon (different avatar for same dApp)
polygonProfile.setDappAvatar("GameA", "ipfs://polygon-avatar", true);
// Game reads from the chain it's deployed on
// Ethereum GameA sees eth-avatar
// Polygon GameA sees polygon-avatarThis enables:
- Network-specific branding — Different avatars per chain
- Chain-specific contexts — Layer 2 avatars differ from mainnet
- Independent state — Each chain manages its own profile state
Indexing Across Chains
To provide a unified multi-chain experience:
// Listen to all chains simultaneously
async function buildMultiChainIndex() {
const chains = [
{ id: 1, name: "ethereum", contract: ethereumSoulProfile },
{ id: 137, name: "polygon", contract: polygonSoulProfile },
{ id: 42161, name: "arbitrum", contract: arbitrumSoulProfile }
];
const userProfiles = new Map<string, Map<string, any>>();
for (const chain of chains) {
chain.contract.on("ProfileCreated", (owner, username) => {
if (!userProfiles.has(username)) {
userProfiles.set(username, new Map());
}
const chainProfiles = userProfiles.get(username)!;
chainProfiles.set(chain.name, {
address: owner,
chainId: chain.id,
fullId: `did:eip155:${chain.id}:${owner}`
});
});
}
return userProfiles;
}Bridge Considerations
ERC-7866 profiles are not bridged between chains:
- Each chain has a separate, independent SoulProfile contract
- No cross-chain messaging required
- No wrapped tokens or bridges
- Each profile is sovereign to its chain
If you need synchronized profiles across chains, you would implement:
- Off-chain indexing and replication
- Custom cross-chain messaging
- Centralized registry (defeats decentralization)
The recommended approach is per-chain independence — users create profiles where they participate.
Best Practices
For Users
- Create profiles on chains where you're active
- Use the same username across chains for discoverability
- Keep addresses consistent (same wallet)
- Update avatars on each chain independently
For dApps
- Support profile lookups on your deployed chain
- Use addresses as primary identifier (not username)
- Fall back to default avatar if dApp-specific avatar not set
- Consider the user's multi-chain presence in UX
For Indexers
- Run RPC nodes for each chain
- Listen to ProfileCreated events on each chain
- Aggregate data off-chain
- Index by username for discovery
- Track DIDs for multi-chain lookups