# Deploy a Contract

This guide walks through deploying a Solidity contract to **Xenea** with [Foundry](https://book.getfoundry.sh/), 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](https://docs.xenea.io/development/network-resources). Get testnet tokens from [Claim Ubusuna Testnet XENE](https://docs.xenea.io/development/claim-ubusuna-testnet-xene). This guide uses the `xenea_ubusuna` alias in its examples.

***

### Prerequisites

* [Foundry](https://book.getfoundry.sh/getting-started/installation) installed
* Native token balance in your deployer wallet on the target network (testnets provide a faucet — see [Claim Ubusuna Testnet XENE](https://docs.xenea.io/development/claim-ubusuna-testnet-xene))

***

### Scaffold a project

Initialize a new Foundry project:

```bash
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:

```toml
[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):

```bash
cast wallet import xenea-deployer --interactive
```

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:

```bash
cast wallet address --account xenea-deployer
```

Check the balance on your target network:

```bash
cast balance <YOUR_ADDRESS> --rpc-url xenea_ubusuna --ether
```

***

### Build and test

```bash
forge build
forge test -vvv
```

***

### Deploy

Deploy `Counter` with `forge create`:

```bash
forge create src/Counter.sol:Counter \
  --rpc-url xenea_ubusuna \
  --account xenea-deployer \
  --legacy \
  --broadcast
```

> ⚠️ **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:

```
Deployer: 0x7D34...cD2
Deployed to: 0x073e...8185
Transaction hash: 0xb877...a481
```

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:

```bash
forge script script/Counter.s.sol:CounterScript \
  --rpc-url xenea_ubusuna \
  --account xenea-deployer \
  --sender <YOUR_ADDRESS> \
  --legacy \
  --broadcast \
  --slow
```

***

### Confirm deployment

Check that bytecode exists at the deployed address:

```bash
cast code <DEPLOYED_ADDRESS> --rpc-url xenea_ubusuna
```

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:

```bash
export ADDR=<DEPLOYED_ADDRESS>

# Read (free, no gas)
cast call $ADDR "number()(uint256)" --rpc-url xenea_ubusuna

# Write (consumes gas, needs --legacy)
cast send $ADDR "increment()" \
  --rpc-url xenea_ubusuna \
  --account xenea-deployer \
  --legacy

# Read again — value should have incremented
cast call $ADDR "number()(uint256)" --rpc-url xenea_ubusuna
```

> 💡 **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

```bash
forge verify-contract \
  <DEPLOYED_ADDRESS> \
  src/Counter.sol:Counter \
  --show-standard-json-input > counter-standard-input.json
```

`--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:

```json
"compiler":{"version":"0.8.28+commit.7893614a"}
```

You will enter this on the explorer as `v0.8.28+commit.7893614a`.

#### 3. Submit on XENEA Scan

Open the deployed contract address in the explorer for your target network, then start contract verification from the explorer UI.

If you need the direct path, the current URL pattern is:

```
<EXPLORER_URL>/address/<DEPLOYED_ADDRESS>/contract_verifications/new
```

Choose **Via Standard Input JSON** and fill in:

| Field               | Value                                                    |
| ------------------- | -------------------------------------------------------- |
| Contract Name       | `src/Counter.sol:Counter`                                |
| Compiler            | the version from step 2 (e.g. `v0.8.28+commit.7893614a`) |
| 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.

***

### 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 `src/Counter.sol:Counter`
* Compiler version from `out/Counter.sol/Counter.json`
* Standard JSON Input generated from the same build

***

### 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                                        |
