ERC1155 Pointer Contracts
ERC1155 pointer contracts extend Sei’s interoperability framework to support multi-token standards. They allow interaction between ERC1155 contracts and their Cosmos equivalents. This supports use cases for gaming, collectibles, and complex token economies.
Overview
The ERC1155 standard allows a single contract to manage multiple token types - both fungible and non-fungible tokens. ERC1155 pointers maintain this flexibility while providing cross-environment functionality.
Key Features
- Multi-Token Support: Bridge multiple token types in a single pointer contract
- Batch Operations: Efficient batch transfers and operations
- Flexible Token Types: Support for both fungible and non-fungible tokens
- Gas Optimization: Reduced gas costs for complex multi-token operations
- Cross-Environment Compatibility: Direct interaction between Cosmos and EVM
Prerequisites
Before deploying ERC1155 pointers, ensure you have:
- Sei CLI (
seid
) installed - Installation Guide - Access to the ERC1155 contract address you want to bridge
- Sufficient SEI tokens for transaction fees
- Understanding of Pointer Contracts Overview
Checking Existing Pointers
Always verify if a pointer already exists for your ERC1155 contract:
seid q evm pointer ERC1155 [CONTRACT_ADDRESS] --node=https://rpc.sei-apis.com
Example:
seid q evm pointer ERC1155 0x1234567890abcdef1234567890abcdef12345678 --node=https://rpc.sei-apis.com
ERC1155 Pointer Registration
Important: CW→ERC registration is disabled for security reasons. Only ERC→CW registration is currently supported.
ERC1155 → CosmWasm Pointer
To create a CosmWasm pointer for an existing ERC1155 contract:
seid tx evm register-pointer ERC1155 $ERC1155_CONTRACT_ADDRESS \
--from=$SENDER \
--chain-id=pacific-1 \
--broadcast-mode=block \
--gas=300000 \
--fees=10000usei \
--node=https://rpc.sei-apis.com
Parameters
ERC1155_CONTRACT_ADDRESS
: The address of the ERC1155 contract--from
: Your wallet/key name--chain-id
: Network identifier (pacific-1
for mainnet,atlantic-2
for testnet)--gas
: Gas limit (ERC1155 operations may require more gas)--fees
: Transaction fees in usei
Example Registration
# Set environment variables
export ERC1155_ADDRESS="0xabcdef1234567890abcdef1234567890abcdef12"
export SENDER="my-wallet"
# Register the pointer
seid tx evm register-pointer ERC1155 $ERC1155_ADDRESS \
--from=$SENDER \
--chain-id=pacific-1 \
--broadcast-mode=block \
--gas=350000 \
--fees=12000usei \
--node=https://rpc.sei-apis.com
Verification
After registration, confirm the pointer was created successfully:
seid q evm pointer ERC1155 $ERC1155_ADDRESS --node=https://rpc.sei-apis.com
ERC1155 Pointer Usage
Solidity Integration
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
contract ERC1155PointerExample is ERC1155Holder {
IERC1155 public immutable erc1155Contract;
constructor(address _erc1155Address) {
erc1155Contract = IERC1155(_erc1155Address);
}
// Batch transfer multiple token types
function batchTransferToPointer(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) external {
erc1155Contract.safeBatchTransferFrom(
msg.sender,
to,
ids,
amounts,
data
);
}
// Check balance of specific token ID
function getTokenBalance(address account, uint256 tokenId)
external
view
returns (uint256)
{
return erc1155Contract.balanceOf(account, tokenId);
}
// Check multiple token balances
function getBatchBalances(
address[] memory accounts,
uint256[] memory ids
) external view returns (uint256[] memory) {
return erc1155Contract.balanceOfBatch(accounts, ids);
}
}
CosmWasm Integration
use cosmwasm_std::{
Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128,
};
use cw1155::{Cw1155ExecuteMsg, Cw1155QueryMsg, TokensResponse};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
pub erc1155_pointer: Addr,
}
// Execute message to interact with ERC1155 pointer
pub fn execute_transfer_batch(
deps: DepsMut,
env: Env,
info: MessageInfo,
to: Addr,
token_ids: Vec<String>,
amounts: Vec<Uint128>,
) -> Result<Response, ContractError> {
let erc1155_pointer = ERC1155_POINTER.load(deps.storage)?;
let transfer_msg = Cw1155ExecuteMsg::SendBatch {
from: Some(info.sender.to_string()),
to: to.to_string(),
batch: token_ids.into_iter().zip(amounts.into_iter())
.map(|(id, amount)| (id, amount, None))
.collect(),
msg: None,
};
let cosmos_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: erc1155_pointer.to_string(),
msg: to_binary(&transfer_msg)?,
funds: vec![],
});
Ok(Response::new()
.add_message(cosmos_msg)
.add_attribute("action", "batch_transfer")
.add_attribute("to", to))
}
Advanced Use Cases
Gaming Assets Bridge
contract GameAssetsBridge {
IERC1155 public gameAssets;
mapping(uint256 => string) public assetTypes;
// Token ID ranges
uint256 constant WEAPONS_START = 1;
uint256 constant ARMOR_START = 10000;
uint256 constant CONSUMABLES_START = 20000;
constructor(address _gameAssets) {
gameAssets = IERC1155(_gameAssets);
}
function bridgeGameAssets(
address player,
uint256[] memory tokenIds,
uint256[] memory amounts
) external {
// Validate token types
for (uint256 i = 0; i < tokenIds.length; i++) {
require(isValidGameAsset(tokenIds[i]), "Invalid game asset");
}
// Transfer to bridge
gameAssets.safeBatchTransferFrom(
player,
address(this),
tokenIds,
amounts,
""
);
// Emit bridge event for off-chain processing
emit AssetsBridged(player, tokenIds, amounts);
}
function isValidGameAsset(uint256 tokenId) internal pure returns (bool) {
return tokenId >= WEAPONS_START;
}
}
Multi-Token Marketplace
contract MultiTokenMarketplace {
struct Listing {
address seller;
address tokenContract;
uint256 tokenId;
uint256 amount;
uint256 pricePerToken;
bool active;
}
mapping(bytes32 => Listing) public listings;
function createListing(
address tokenContract,
uint256 tokenId,
uint256 amount,
uint256 pricePerToken
) external {
bytes32 listingId = keccak256(
abi.encodePacked(msg.sender, tokenContract, tokenId, block.timestamp)
);
// Transfer tokens to marketplace
IERC1155(tokenContract).safeTransferFrom(
msg.sender,
address(this),
tokenId,
amount,
""
);
listings[listingId] = Listing({
seller: msg.sender,
tokenContract: tokenContract,
tokenId: tokenId,
amount: amount,
pricePerToken: pricePerToken,
active: true
});
emit ListingCreated(listingId, msg.sender, tokenContract, tokenId);
}
function purchaseTokens(bytes32 listingId, uint256 amount) external payable {
Listing storage listing = listings[listingId];
require(listing.active, "Listing not active");
require(amount <= listing.amount, "Insufficient tokens");
uint256 totalPrice = amount * listing.pricePerToken;
require(msg.value >= totalPrice, "Insufficient payment");
// Transfer tokens to buyer
IERC1155(listing.tokenContract).safeTransferFrom(
address(this),
msg.sender,
listing.tokenId,
amount,
""
);
// Update listing
listing.amount -= amount;
if (listing.amount == 0) {
listing.active = false;
}
// Transfer payment to seller
payable(listing.seller).transfer(totalPrice);
// Refund excess payment
if (msg.value > totalPrice) {
payable(msg.sender).transfer(msg.value - totalPrice);
}
}
}
Gas Considerations
ERC1155 operations, especially batch operations, may require higher gas limits:
Operation | Estimated Gas | Use Case |
---|---|---|
Single Transfer | ~45,000 | Basic token transfer |
Batch Transfer (5 tokens) | ~120,000 | Multi-token operations |
Pointer Registration | ~300,000 | Initial setup |
Complex Multi-Token Operations | ~500,000+ | Gaming marketplaces |
Gas Optimization Tips
contract OptimizedERC1155Operations {
// Use packed structs for batch operations
struct BatchTransfer {
uint128 tokenId;
uint128 amount;
}
function optimizedBatchTransfer(
address to,
BatchTransfer[] calldata transfers
) external {
uint256 length = transfers.length;
uint256[] memory ids = new uint256[](length);
uint256[] memory amounts = new uint256[](length);
// Unpack in a single loop
for (uint256 i = 0; i < length;) {
ids[i] = transfers[i].tokenId;
amounts[i] = transfers[i].amount;
unchecked { ++i; }
}
IERC1155(tokenContract).safeBatchTransferFrom(
msg.sender,
to,
ids,
amounts,
""
);
}
}
Security Considerations
Security: Always validate token IDs and amounts in multi-token operations to prevent unauthorized transfers.
Input Validation
contract SecureERC1155Bridge {
mapping(uint256 => bool) public allowedTokenIds;
uint256 public maxBatchSize = 100;
modifier validBatch(uint256[] memory ids, uint256[] memory amounts) {
require(ids.length == amounts.length, "Array length mismatch");
require(ids.length <= maxBatchSize, "Batch too large");
for (uint256 i = 0; i < ids.length; i++) {
require(allowedTokenIds[ids[i]], "Token not allowed");
require(amounts[i] > 0, "Invalid amount");
}
_;
}
function secureBatchTransfer(
address to,
uint256[] memory ids,
uint256[] memory amounts
) external validBatch(ids, amounts) {
// Safe to proceed with transfer
IERC1155(tokenContract).safeBatchTransferFrom(
msg.sender,
to,
ids,
amounts,
""
);
}
}
Troubleshooting
Common Issues
Registration Fails
# Check if pointer already exists
seid q evm pointer ERC1155 $CONTRACT_ADDRESS
# Verify contract exists and is ERC1155
# Check gas limits and fees
Transfer Failures
- Verify token ownership and approvals
- Check for sufficient gas limits
- Ensure contract implements ERC1155Receiver if receiving
- Validate token IDs exist in the contract
Performance Issues
- Use batch operations when possible
- Implement pagination for large datasets
- Cache frequently accessed data
- Optimize struct packing
Debug Commands
# Check pointer status
seid q evm pointer ERC1155 $CONTRACT_ADDRESS --output json
# Verify transaction
seid q tx $TX_HASH --output json
# Check account balance
seid q bank balances $ADDRESS
Best Practices
- Batch Operations: Use batch transfers for multiple tokens
- Gas Estimation: Always estimate gas for complex operations
- Input Validation: Validate all token IDs and amounts
- Error Handling: Implement comprehensive error handling
- Events: Emit events for cross-chain tracking
- Testing: Thoroughly test on testnet before mainnet deployment
Success: ERC1155 pointers provide multi-token bridging capabilities. Start with simple use cases and expand to complex token economies.