# How to Use ABI Encoding and Decoding for Contract Interaction in Sway

> Learn ABI encoding and decoding in Sway for seamless contract interaction. Sway automatically handles data serialization for calls logs and SDK communication.

- Repository: [Fuel Labs/sway](https://github.com/FuelLabs/sway)
- Tags: how-to-guide
- Published: 2026-03-04

---

**Sway automatically generates `abi_encode` and `abi_decode` implementations for every type appearing in a contract's public interface, handling calldata serialization for contract calls, transaction logs, and off-chain SDK communication without manual byte manipulation.**

The FuelLabs/sway compiler eliminates boilerplate serialization by auto-implementing encoding traits for all contract-visible types. When you interact with contracts—whether through the `abi` cast in on-chain scripts or the Rust SDK off-chain—the compiler inserts precise byte-layout operations defined by the Fuel ABI specification. This guide explains the encoding mechanism, its three primary use cases, and the exact source locations where this serialization logic resides.

## How ABI Encoding Works in Sway

Sway’s compiler analyzes type signatures at compile time and generates optimized serialization routines. This process ensures that data sent between contracts, scripts, and off-chain clients follows a consistent binary layout.

### Compiler-Generated Trait Implementations

For every struct, enum, array, or vector that appears in a contract’s public interface, the compiler auto-implements encoding logic in [`sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs). The function [`generate_abi_encode_struct_body`](https://github.com/FuelLabs/sway/blob/master/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs#L88-L99) constructs the encoder body by emitting call chains like `self.field.abi_encode(buffer)` for each struct field.

The generated methods receive a mutable `Buffer` and return it, allowing the compiler to concatenate encodings efficiently without intermediate allocations. This auto-implementation satisfies the `AbiEncode` trait requirement that the compiler enforces for all public function arguments and return values.

### The Four-Step Encoding Algorithm

According to the `sway-core` source code, the compiler follows this precise pipeline when generating encoding instructions:

1. **Type inspection** – The compiler resolves the concrete `TypeId` of each argument to determine its memory layout.
2. **Size hint calculation** – `TypeInfo::abi_encode_size_hint` (defined in [`sway-core/src/type_system/info.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/type_system/info.rs)) computes the exact buffer size for primitive types, or falls back to “potentially infinite” for reference-containing types like dynamic vectors.
3. **Code generation** – For complex types, the compiler emits a `match` expression that writes a discriminator tag followed by the inner value (enums), or sequential field encodings (structs).
4. **Buffer concatenation** – Each generated method appends bytes to the mutable `Buffer`, which is then passed to the Fuel VM as calldata or log data.

## Three Critical Use Cases for ABI Encoding

The auto-generated encoding logic serves three distinct interaction patterns in the Sway ecosystem.

### Contract Calls via the `ContractCaller` Type

When you cast a contract address to an ABI interface using `abi(AbiName, address)`, the compiler returns a `ContractCaller<AbiName>` struct. Each method call on this struct triggers automatic encoding:

```sway
abi Wallet;

fn main(wallet_addr: ContractId, amount: u64) -> u64 {
    let wallet = abi(Wallet, wallet_addr);
    wallet.deposit(amount);  // amount is automatically abi_encode'd
    wallet.withdraw(amount)
}

```

The compiler expands `wallet.deposit(amount)` into a low-level call that serializes the `u64` using the struct generated in [`generate_abi_encode_struct_body`](https://github.com/FuelLabs/sway/blob/master/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs#L88-L99), prepends the function selector, and sends the resulting byte vector to the contract.

### Transaction Logging with `__log`

The `__log<T>` intrinsic requires that `T` implement `AbiEncode`. During semantic analysis, the compiler transforms `__log(x)` into `encode(x)` via the `wrap_logged_expr_into_encode_call` function in [`sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs):

```sway
fn foo(x: u64) {
    __log(x);  // Compiler inserts abi_encode call for x
}

```

This allows any encodable type—whether a primitive or a complex struct—to be serialized into the transaction receipt logs for debugging or event indexing.

### Off-Chain SDK Encoding

The Rust `fuels` SDK mirrors the on-chain encoding logic to ensure compatibility. The `ABIEncoder` (found in the SDK harness tests at [`test/src/sdk-harness/test_projects/low_level_call/mod.rs`](https://github.com/FuelLabs/sway/blob/main/test/src/sdk-harness/test_projects/low_level_call/mod.rs)) converts Rust values into Fuel ABI-encoded byte vectors:

```rust
use fuels::core::codec::{ABIEncoder, EncoderConfig};

let selector = encode_fn_selector("transfer(u64)").to_vec();
let calldata = ABIEncoder::new(EncoderConfig::default())
    .encode(&[amount.into_token()])
    .unwrap();

```

The SDK uses `Tokenizable::into_token` to bridge Rust types into the encoder, producing calldata that matches exactly what the on-chain `abi_decode` expects when the contract method is invoked.

## End-to-End Implementation Examples

These practical examples demonstrate the encoding flow from contract definition to off-chain interaction.

### On-Chain Contract Definition and Interaction

Define a contract with storage-mutating functions:

```sway
abi Wallet {
    #[storage(read, write)]
    fn deposit(amount: u64);
    #[storage(read, write)]
    fn withdraw(amount: u64) -> u64;
}

contract;

impl Wallet for Contract {
    fn deposit(amount: u64) {
        self.balance += amount;
    }

    fn withdraw(amount: u64) -> u64 {
        let bal = self.balance;
        if amount > bal {
            revert("Insufficient funds");
        }
        self.balance = bal - amount;
        amount
    }
}

```

Call this contract from a script, letting the compiler handle encoding:

```sway
abi Wallet;

fn main(wallet_addr: ContractId, amount: u64) -> u64 {
    let wallet = abi(Wallet, wallet_addr);
    wallet.deposit(amount);
    wallet.withdraw(amount)
}

```

### Off-Chain Rust SDK Calldata Construction

Use the generated bindings and encoder for type-safe off-chain calls:

```rust
use fuels::{prelude::*, core::codec::ABIEncoder};

abigen!(WalletContract, "out/wallet-abi.json");

#[tokio::main]
async fn main() {
    let wallet_id = ContractId::from_str("0x1234...").unwrap();
    let provider = Provider::launch_custom(Some(1), Some(1), Some(1_000_000)).await.unwrap();
    let wallet = WalletContract::new(wallet_id, provider);

    // Manual encoding matches the on-chain abi_encode logic
    let selector = encode_fn_selector("deposit(u64)").to_vec();
    let calldata = ABIEncoder::default()
        .encode(&[42_u64.into_token()])
        .unwrap();

    let _ = provider
        .contract_call(wallet_id, selector, calldata)
        .await
        .unwrap();
}

```

### Structured Debugging with Encoded Logs

Emit encodable events directly from contract logic:

```sway
struct DepositEvent {
    sender: Address,
    amount: u64,
}

fn log_deposit(sender: Address, amount: u64) {
    let event = DepositEvent { sender, amount };
    __log(event);  // Automatically serialized via abi_encode
}

```

## Core Source Files and Architecture

The following files in the `FuelLabs/sway` repository implement the encoding pipeline:

- [`sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs) – Auto-generates `abi_encode` and `abi_decode` implementations for structs, enums, and collections.
- [`sway-core/src/type_system/info.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/type_system/info.rs) – Provides `abi_encode_size_hint` for buffer pre-allocation calculations.
- [`sway-core/src/abi_generation/fuel_abi.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/abi_generation/fuel_abi.rs) – Generates the JSON ABI metadata consumed by off-chain tools and the `abigen!` macro.
- [`sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs) – Contains `wrap_logged_expr_into_encode_call` for the `__log` intrinsic transformation.
- [`test/src/sdk-harness/test_projects/low_level_call/mod.rs`](https://github.com/FuelLabs/sway/blob/main/test/src/sdk-harness/test_projects/low_level_call/mod.rs) – Demonstrates SDK-side encoding with `ABIEncoder` and `fn_selector!` macros.

## Summary

- **Automatic implementation** – The Sway compiler generates `abi_encode` and `abi_decode` methods for every type in [`sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs), eliminating manual serialization.
- **Contract interaction** – The `abi(AbiName, address)` cast produces a `ContractCaller` that automatically encodes arguments using the generated methods before sending calldata to the Fuel VM.
- **Debug logging** – The `__log` intrinsic relies on the same encoding pipeline, transforming logged values into ABI-encoded byte arrays via `wrap_logged_expr_into_encode_call`.
- **Cross-language compatibility** – The Rust SDK’s `ABIEncoder` uses identical logic to the on-chain encoder, ensuring that off-chain `calldata` matches contract expectations.

## Frequently Asked Questions

### How does Sway generate encoding logic for custom structs?

The compiler analyzes struct definitions during semantic analysis and invokes `generate_abi_encode_struct_body` in [`sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs) to emit a method that sequentially encodes each field into a mutable `Buffer`.

### What is the difference between on-chain and off-chain ABI encoding in Sway?

On-chain encoding happens automatically when calling methods through the `abi` cast, while off-chain encoding requires explicit use of the `ABIEncoder` in the Rust SDK. Both use the same binary layout defined by the Fuel ABI specification and generated metadata from [`fuel_abi.rs`](https://github.com/FuelLabs/sway/blob/main/fuel_abi.rs).

### Why does the `__log` intrinsic require types to implement `AbiEncode`?

The `__log<T>` intrinsic serializes values into transaction receipt logs, which requires a consistent byte representation. The compiler enforces `T: AbiEncode` and transforms the call into an `encode` operation via `wrap_logged_expr_into_encode_call` to ensure the data is correctly serialized for the VM.

### Where does the compiler calculate buffer sizes for encoding?

The `TypeInfo::abi_encode_size_hint` method in [`sway-core/src/type_system/info.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/type_system/info.rs) computes exact byte sizes for primitive types and provides fallback logic for dynamically-sized references, enabling efficient buffer pre-allocation during code generation.