An open-source tool for converting Solidity smart contracts to MultiversX-compatible Rust smart contracts.
# Install
pip install xtract # transpile only
pip install xtract[deploy] # + wallet creation and on-chain deployment
# 1. Transpile Solidity → Rust
xtract MyContract.sol # writes MyContract.rs
# 2. Build Rust → WASM (requires mxpy)
xtract build ./my_contract/
# 3. Create a wallet (first time)
xtract wallet create # saves to ~/.multiversx/wallet.pem
# → prints your address and funding URLs for devnet/testnet
# 4. Deploy
xtract deploy ./my_contract/output/my_contract.wasm \
--abi ./my_contract/output/my_contract.abi.json \
--wallet ~/.multiversx/wallet.pem \
--network devnet
# → prints contract address and explorer linknpm package: https://www.npmjs.com/package/xtract-cli
XTract analyzes Solidity code and generates MultiversX Rust code that can be compiled and deployed on the MultiversX blockchain. It supports a comprehensive set of Solidity features including control flow, mappings, modifiers, and inheritance stubs.
v1.0 adds the full deployment pipeline on top of the transpiler. See docs/v1.0/CHANGELOG.md for the complete change list.
xtract build— compiles transpiled Rust to WASM viamxpyxtract wallet create— generates a BIP39 wallet, saves as PEMxtract deploy— deploys compiled WASM to devnet/testnet/mainnetpip install xtract[deploy]— optional dep group for the above- TypeScript SDK —
xtract-cli/sdkexposesXtractTranspilerandContractDeployer
- Function body transpilation: Converts
require(),emit(),return, and assignments - Error handling: Maps Solidity
require()→ MultiversXrequire!()andrevert()/ custom error reverts →sc_panic!() - Event emission: Properly converts Solidity events to MultiversX event calls
- Storage operations: Handles variable assignments and storage access patterns
- Single mappings:
mapping(address => uint256)→ storage mappers with key parameters - Nested mappings:
mapping(address => mapping(address => uint256))→ multi-key storage mappers - Struct values in mappings: Single-level field reads and writes use MultiversX-safe local load-mutate-store code instead of direct mapper field access
- If/else statements: Full if/else transpilation with proper Rust syntax
- For loops: Counter-based loops transpiled to
for i in 0..nsyntax - While loops: While loop transpilation with condition conversion
- Payable functions: Automatic
#[payable("EGLD")]annotation
- Function modifiers:
onlyOwner,whenNotPaused, custom modifiers, including parameterized modifiers with argument substitution - Basic inheritance: Contract inheritance with supertrait stub generation and warnings for required manual integration
- Enhanced diagnostics: Detailed warnings for unsupported features and semantic differences, including
int256casts that rely on MultiversXBigIntnegative values
int8, int16, int32, and int64 casts map to Rust i8, i16, i32, and i64. int256 maps to MultiversX BigInt<Self::Api>, which has more limited negative number support than Solidity int256; review warnings on negative literals or variable casts before relying on equivalent storage or arithmetic behavior.
Test Coverage: 100% unit test success across 50 Solidity contracts and 149 pytest cases.
The xtract-cli package bundles a full TypeScript SDK for programmatic use:
import { XtractTranspiler, ContractDeployer } from 'xtract-cli/sdk';
// Transpile Solidity to Rust
const transpiler = new XtractTranspiler();
const result = await transpiler.transpileCode('contract Foo { uint x; }');
console.log(result.rustCode);
// Deploy compiled contract to MultiversX
const deployer = new ContractDeployer();
const deployed = await deployer.deploy({
network: 'devnet',
wasmPath: './output/foo.wasm',
abiPath: './output/foo.abi.json',
walletPath: './wallet.pem',
});
console.log(deployed.contractAddress);Prerequisite: the SDK shells out to the Python transpiler —
pip install xtractmust be run first (Python 3.9+).
# Transpile only (lightweight — just click)
pip install xtract
# Full pipeline: transpile + build wrapper + wallet + deploy
pip install xtract[deploy]
# CLI tool for npm users (includes TypeScript SDK)
npm install -g xtract-climxpy is required separately for the xtract build command:
pip install mxpy# Transpile
xtract transpile MyContract.sol # → MyContract.rs
xtract transpile MyContract.sol -o output.rs
xtract transpile -v MyContract.sol # verbose diagnostics
xtract transpile --json MyContract.sol # JSON to stdout
# Flags can be passed before the file without the 'transpile' subcommand:
xtract --json MyContract.sol # shorthand for 'xtract transpile --json'
xtract -v MyContract.sol # shorthand for 'xtract transpile -v'
# Build (requires mxpy)
xtract build ./my_contract/
# Wallet (requires xtract[deploy])
xtract wallet create
xtract wallet create --output ./wallet.pem
# Faucet (requires xtract[deploy])
xtract faucet # devnet EGLD → default wallet
xtract faucet --network testnet
xtract faucet --address erd1... # by address
# Deploy (requires xtract[deploy])
xtract deploy ./output/my_contract.wasm \
--abi ./output/my_contract.abi.json \
--wallet ~/.multiversx/wallet.pem \
--network devnet # devnet | testnet | mainnet# Create a simple Solidity contract
cat > MyStorage.sol << 'EOF'
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyStorage {
uint256 public value;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
event ValueChanged(uint256 indexed newValue);
constructor() {
owner = msg.sender;
}
function setValue(uint256 newValue) public onlyOwner {
require(newValue > 0, "Value must be positive");
value = newValue;
emit ValueChanged(newValue);
}
function getValue() public view returns (uint256) {
return value;
}
}
EOF
# Transpile it
xtract MyStorage.sol
# View the generated MultiversX Rust code
cat MyStorage.rs- Contract declarations, constructors (with parameters) mapped to
#[init], state variables - Single and nested mappings
- Single-level struct field access on mapping values, emitted as local
.get()bindings for reads and load-mutate-store for writes - Events with indexed parameters
- Custom errors, structs
- Functions: public/external write functions map to
#[endpoint], public/externalviewandpurefunctions map to#[view], and internal/private functions remain helper methods without endpoint annotations msg.value→self.call_value().egld_value()msg.sender→self.blockchain().get_caller()- Function modifiers — full pre/post body inlining, including parameterized modifiers such as
onlyRole(ADMIN_ROLE)andnonReentrant - Basic inheritance (
contract A is B, C) as a supertrait stub; manually integrate parent storage mappers and methods require/revert, if/else, for loops, while loops, do-while loops- Ternary expressions (
cond ? a : b→if cond { a } else { b }) delete var→.clear(),unchecked { }passthroughSafeMath/using-for→ inlined arithmetic operatorsbytes/bytes32casts from string and hex literals →ManagedBuffer
Custom error reverts map to sc_panic! string messages. revert CustomError() emits sc_panic!("CustomError"), revert CustomError("message") emits sc_panic!("message"), and typed custom error arguments are dropped with a diagnostic warning because MultiversX does not support Solidity-style typed errors.
Solidity fallback() and receive() handlers map to the MultiversX fallback entry point: #[fallback] fn call(&self). Payable receive() handlers also emit #[payable("EGLD")]. The transpiler emits a diagnostic warning because Solidity's separate receive/fallback dispatch semantics require manual review after conversion.
- Complex arithmetic expressions
- External contract calls
bytes/bytes32casts from numeric or unknown input types- Inheritance output, because parent storage mappers, methods, modifiers, and init logic are not automatically inherited
- Inline assembly — replaced with
// TODO: inline assembly removed — no MultiversX equivalent - Try-catch blocks — replaced with
// TODO: try-catch block removed — implement error handling manually
- Libraries
- Diamond inheritance
The test_cases/ directory contains 50 fully working examples including:
| Category | Examples |
|---|---|
| Basic | SimpleStorage, Counter, Config |
| Tokens | ERC20Token, SimpleToken, TokenMinter |
| NFTs | NFTMarketplace, Certificate, Badge |
| DeFi | Staking, RewardPool, TokenSwap, Vesting |
| Governance | Voting, Governance, Poll, Multisig |
| Access Control | OnlyOwner, AccessControl, Pausable |
| Patterns | Escrow, Auction, Lottery, Vault |
- docs/v1.0/GETTING_STARTED.md — 5-minute quickstart for EVM developers
- docs/v1.0/TRANSPILER_REFERENCE.md — full Solidity feature coverage table and
--jsonoutput format - docs/v1.0/SDK_REFERENCE.md — TypeScript SDK API reference
- docs/v1.0/MIGRATION_GUIDE.md — EVM → MultiversX concept mapping
- docs/v1.0/TUTORIAL_ERC20.md — end-to-end ERC20 walkthrough
- docs/v1.0/TUTORIAL_DEX.md — end-to-end DEX walkthrough
- docs/v1.0/README.md — v1.0 feature overview and full pipeline walkthrough
- docs/v1.0/CHANGELOG.md — what changed from v0.30 to v1.0
- docs/DEVELOPER_GUIDE.md — CLI reference, Python API, TypeScript SDK, type mapping
XTract/
xtract/ # Python package
transpiler.py # Solidity parser and Rust emitter
cli.py # CLI entry point (transpile / build / wallet / deploy)
build.py # mxpy contract build wrapper
wallet.py # BIP39 wallet generation
deploy.py # Contract deployment via multiversx-sdk
sdk/ # TypeScript SDK (bundled into xtract-cli npm package)
tests/ # Python unit tests
test_cases/ # Solidity inputs and expected Rust outputs (50 contracts)
docs/ # Documentation
.github/workflows/ # CI configuration
package.json # xtract-cli npm package
pyproject.toml # Python packaging config
XTract follows a clear pipeline from Solidity source code to MultiversX Rust:
Solidity Source (.sol)
↓ xtract MyContract.sol
┌─────────────────────┐
│ Transpiler │
│ (parse + emit) │
└──────────┬──────────┘
↓
Rust Source (.rs)
↓ xtract build ./my_contract/
┌─────────────────────┐
│ mxpy contract │
│ build │
└──────────┬──────────┘
↓
WASM + ABI
↓ xtract deploy ...
┌─────────────────────┐
│ multiversx-sdk │
│ (sign + broadcast) │
└──────────┬──────────┘
↓
Live contract on MultiversX
# Run all tests
pytest tests/ -v
# Run specific test
pytest tests/test_transpiler_core.py::test_nested_mapping_features -v
# Run with coverage
pytest tests/ --cov=xtractContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.