Deploy a Contract

This guide walks through deploying a Solidity contract to Xenea with Foundryarrow-up-right, then verifying the source code on XENEA Scan.

Xenea currently provides the Ubusuna Testnet. Additional networks will follow the same workflow. Look up the chain ID, RPC URL, and explorer URL in Network Resources. Get testnet tokens from Claim Ubusuna Testnet XENE. This guide uses the xenea_ubusuna alias in its examples.


Prerequisites


Scaffold a project

Initialize a new Foundry project:

forge init xenea-dapp-sandbox
cd xenea-dapp-sandbox

This generates a sample src/Counter.sol, script/Counter.s.sol, and test/Counter.t.sol. This guide uses the generated Counter contract as the deployment target.


Configure Foundry

Edit foundry.toml to add a Xenea profile and one or more RPC endpoints:

[profile.default]
src = "src"
out = "out"
libs = ["lib"]
evm_version = "paris"

[rpc_endpoints]
xenea_ubusuna = "https://rpc-ubusuna.xeneascan.com/"
# xenea_mainnet = "<RPC_URL>"

Throughout the rest of this guide, xenea_ubusuna is used as the example alias. Replace it with whichever entry you want to deploy to.

⚠️ Set evm_version = "paris". Xenea runs the Paris EVM. Without this setting, Solidity 0.8.20+ defaults to Shanghai and emits the PUSH0 opcode, and your deployment will revert.


Prepare a deployer wallet

Import your private key into a Foundry keystore (encrypted on disk):

You'll be prompted for the private key (a 0x-prefixed 64-char hex string) and a password to encrypt the keystore. The keystore is saved to ~/.foundry/keystores/xenea-deployer.

Get the address:

Check the balance on your target network:


Build and test


Deploy

Deploy Counter with forge create:

⚠️ Always pass --legacy on Xenea. Xenea uses legacy (Type 0) transactions. Forge commands that broadcast transactions — forge create, forge script, cast send — need this flag.

You'll be prompted for the keystore password. On success you'll see:

Save the Deployed to address — you'll need it for the next steps.

Using forge script instead

If your deployment involves multiple contracts or post-deploy calls, a Script contract is the standard Foundry approach. On Xenea, pass --legacy and --slow so broadcasts wait for each receipt before moving on:


Confirm deployment

Check that bytecode exists at the deployed address:

A long 0x… blob confirms the contract is on-chain. A bare 0x usually means the contract is not present at that address on the selected network.


Interact with the contract

Use cast call for read-only functions and cast send for state-changing ones:

💡 If cast send hangs while waiting for a receipt, add --async to return immediately with the tx hash, then confirm later with cast receipt <hash> --rpc-url xenea_ubusuna.


Verify the contract

XENEA Scan accepts verification through the web UI using Standard JSON Input generated by Foundry.

1. Generate Standard JSON Input

--show-standard-json-input writes the solc Standard JSON Input to stdout without making a network request.

2. Look up the compiler version

Open out/Counter.sol/Counter.json in your editor and search for compiler.version.

Copy the value exactly. For example:

You will enter this on the explorer as v0.8.31+commit.f0907708.

3. Submit on XENEA Scan

Open the dedicated Verify Contract page for Ubusuna Testnet:

https://ubusuna.xeneascan.com/verify-contractarrow-up-right

Choose Via Standard Input JSON and fill in:

Field
Value

Contract Address

<DEPLOYED_ADDRESS>

Contract Name

Counter

Compiler

the version from step 2 (e.g. v0.8.31+commit.f0907708)

Standard Input JSON

upload counter-standard-input.json

Click Verify & Publish. After success, the contract page on XENEA Scan will show the source code, ABI, and Read/Write Contract UI.

Use src/Counter.sol:Counter only in the Foundry command. On the explorer form, enter Counter.


Troubleshooting

cast code returns 0x

The contract is not present at that address on the selected network. Check these first:

  • You passed --legacy on broadcast commands

  • Your deployer has enough native tokens

  • Your RPC alias points to the intended network

  • You are checking the correct deployed address

Deployment reverts with opcode errors

Set evm_version = "paris" in foundry.toml. Solidity 0.8.20+ otherwise emits PUSH0, which Xenea does not support.

Verification fails on the explorer

Check that these values match your build exactly:

  • Contract name, such as Counter

  • Compiler version from out/Counter.sol/Counter.json

  • Standard JSON Input generated from the same build

If the compiler version does not match the build you want to verify, rebuild the project with the intended compiler version and generate the Standard JSON Input again.


Appendix: Command cheatsheet

Replace xenea_ubusuna with whichever [rpc_endpoints] alias you registered for your target network.

Command
Purpose

forge build

Compile contracts

forge test

Run local tests

forge create <path:Contract> --rpc-url xenea_ubusuna --account <name> --legacy --broadcast

Deploy a single contract

forge script <path>:<Script> --rpc-url xenea_ubusuna --account <name> --legacy --broadcast --slow

Run a deployment script

forge verify-contract <addr> <path:Contract> --show-standard-json-input > out.json

Generate Standard JSON Input for manual verification

cast call <addr> "fn()(ret)" --rpc-url xenea_ubusuna

Read a view/pure function

cast send <addr> "fn(args)" <values> --rpc-url xenea_ubusuna --account <name> --legacy

Send a state-changing tx

cast receipt <txhash> --rpc-url xenea_ubusuna

Fetch a transaction receipt

cast code <addr> --rpc-url xenea_ubusuna

Check deployed bytecode

cast balance <addr> --rpc-url xenea_ubusuna --ether

Check balance

Last updated