How to Use External Contract Calls in Sway: ABI and Proxy Patterns Explained

External contract calls in Sway are implemented through ABI-based typed calls for standard interactions or run_external for proxy patterns that execute external bytecode within the caller's storage context.

Sway, the smart contract language for the Fuel blockchain, provides two distinct mechanisms for external contract calls. According to the FuelLabs/sway source code, developers can use ABI-based calls for type-safe cross-contract communication or run_external for upgradeable proxies that require shared storage contexts.

ABI-Based External Contract Calls

The standard method for external contract calls in Sway uses Application Binary Interface (ABI) definitions. This approach, documented in docs/book/src/blockchain-development/calling_contracts.md, provides compile-time type safety and generates Fuel VM CALL instructions with isolated storage contexts.

Setting Up Contract Dependencies

To call an external contract, first declare it as a dependency in your Forc.toml:

[contract-dependencies]
contract_a = { path = "../contract_a" }

When you build with forc, it automatically generates the ABI trait from the dependency's interface.

Implementing the Call

Import the generated ABI and create an instance using the target ContractId:

use contract_a::ContractA;

const CONTRACT_ID = 0x79fa8779bed2f36c3581d01c79df8da45eee09fac1fd76a5a656e16326317ef0;

fn make_call() {
    let target = abi(ContractA, CONTRACT_ID);
    let result = target.receive(true, 3);
}

The compiler translates target.receive() into a Fuel VM CALL instruction that forwards gas and assets while isolating the callee's storage context. The call respects the Checks-Effects-Interactions (CEI) pattern, with compiler warnings for storage writes following external calls.

Using run_external for Proxy and Upgradeable Contracts

For scenarios requiring shared storage contexts, such as upgradeable proxies, Sway provides run_external in sway-lib-std/src/execution.sw.

How run_external Works

Unlike ABI calls, run_external loads the target contract's bytecode and executes it within the caller's storage context. The function signature in the standard library shows it never returns (!), effectively replacing the current execution context:

pub fn run_external(target: ContractId) -> !;

This mirrors low-level jmp_mem semantics and enables proxy patterns where the implementation contract manipulates the proxy's storage directly.

Upgradeable Proxy Example

The examples/upgradeable_proxy/proxy/src/main.sw demonstrates a complete implementation:

contract;

use std::execution::run_external;

abi Proxy {
    #[storage(write)]
    fn set_target_contract(id: ContractId);
    #[storage(read)]
    fn double_input(_value: u64) -> u64;
}

#[namespace(my_storage_namespace)]
storage {
    target_contract: Option<ContractId> = None,
}

impl Proxy for Contract {
    #[storage(write)]
    fn set_target_contract(id: ContractId) {
        storage.target_contract.write(Some(id));
    }

    #[storage(read)]
    fn double_input(_value: u64) -> u64 {
        let target = storage.target_contract.read().unwrap();
        run_external(target)
    }
}

The corresponding implementation in examples/upgradeable_proxy/implementation/src/main.sw writes to the proxy's storage:

contract;

abi Implementation {
    #[storage(write)]
    fn double_input(value: u64) -> u64;
}

storage {
    value: u64 = 0,
}

impl Implementation for Contract {
    #[storage(write)]
    fn double_input(value: u64) -> u64 {
        let new_value = value * 2;
        storage.value.write(new_value);
        new_value
    }
}

When the proxy's double_input is called, run_external transfers execution to the implementation contract, which doubles the input and writes the result into the proxy's value field.

Testing External Contract Calls

Unit tests in Sway can invoke external contracts by declaring them as dependencies. The test harness automatically injects a CONTRACT_ID constant:

#[test]
fn test_external_call() {
    let external = abi(ExternalContract, CONTRACT_ID);
    let result = external.do_something {}();
    assert(result);
}

This pattern is documented in docs/book/src/testing/unit-testing.md.

Summary

  • ABI-based calls provide type-safe external contract calls in Sway using generated traits and the abi() constructor, compiling to Fuel VM CALL instructions with isolated storage contexts.
  • run_external enables proxy and upgradeable patterns by loading external bytecode into the caller's storage context, implemented in sway-lib-std/src/execution.sw.
  • Both methods support the Checks-Effects-Interactions (CEI) pattern, with compiler warnings for storage writes following external calls.
  • Unit tests can invoke external contracts using the CONTRACT_ID constant injected by the test harness.

Frequently Asked Questions

What is the difference between ABI calls and run_external in Sway?

ABI calls require the external contract's interface definition and create an isolated execution context where the callee's storage is separate from the caller's. In contrast, run_external loads the target contract's bytecode and executes it within the caller's storage context, making it suitable for proxy patterns where the implementation must manipulate the proxy's state directly.

How do I handle storage when using run_external for upgradeable contracts?

When using run_external, the target contract operates on the proxy's storage context rather than its own. You should namespace your storage in the proxy using the #[namespace(...)] attribute to avoid collisions, and ensure the implementation contract's storage layout remains compatible across upgrades since it will be writing directly to the proxy's storage slots.

Can I call external contracts from unit tests in Sway?

Yes, unit tests can call external contracts by adding them as contract-dependencies in your Forc.toml. The test harness automatically injects a CONTRACT_ID constant that you can use with the abi() constructor to create a typed interface to the external contract within your test functions.

What file contains the run_external implementation in the Sway standard library?

The run_external function is implemented in sway-lib-std/src/execution.sw within the FuelLabs/sway repository. This file contains the low-level function definition that loads external contract bytecode and transfers execution context without returning.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →