Skip to content

cargo test build fails when sol_interface! generated interfaces are used in unit tests #301

@chris-ricketts

Description

@chris-ricketts

The use of sol_interface! generated interfaces causes an undefined symbols linker error when built for native arch, such as when running unit tests.

As a user, I would expect the interfaces generated by sol_interface! to be testable with TestVM::mock_*call functions.

src/lib.rs

#![cfg_attr(not(any(test)), no_main)]
extern crate alloc;

use alloy_primitives::{Address, hex};
use stylus_sdk::prelude::*;

sol_interface! {
    interface IAdder  {
        function add(uint64 a, uint64 b) external view returns (uint128);
    }
}

fn add_calldata(a: u64, b: u64) -> Vec<u8> {
    [
        [110u8, 44u8, 115u8, 45u8].as_slice(), // keccak(b"add(uint64,uint64)")[..4],
        [0u8; 24].as_slice(),                  // padding
        a.to_be_bytes().as_slice(),
        [0u8; 24].as_slice(),                  // padding
        b.to_be_bytes().as_slice(),
    ]
    .concat()
}

#[storage]
#[entrypoint]
pub struct MyContract {}

#[public]
impl MyContract {
    // cannot be called in unit tests due to use of undefined symbols (e.g. _static_call) linker error
    pub fn call_adder_iface(&self, adder: IAdder, a: u64, b: u64) -> u128 {
        adder.add(self, a, b).unwrap()
    }

    // can be called in unit tests
    pub fn call_adder_low_level(&self, adder: Address, a: u64, b: u64) -> u128 {
        let return_data = self
            .vm()
            .static_call(&calls::context::Call::new(), adder, &add_calldata(a, b))
            .expect("valid contract call");

        if return_data.len() != 32 {
            panic!("unexpected return data: {}", hex::encode(return_data))
        }

        return_data[16..]
            .try_into()
            .map(u128::from_be_bytes)
            .expect("32 bytes returned")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use stylus_sdk::{abi, testing::*};

    #[test]
    fn test_contract() {
        let vm = TestVM::default();

        let c = MyContract::from(&vm);

        vm.mock_static_call(Address::ZERO, add_calldata(10, 5), Ok(abi::encode(&15u128)));

        assert_eq!(c.call_adder_low_level(Address::ZERO, 10, 5), 15);

        // if you uncomment the line below, `cargo t` will fail to build
        // c.call_adder_iface(IAdder::new(Address::ZERO), 10, 5);
    }
}

Cargo.toml

[package]
name = "sol-interface-breaks-test-build"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["lib", "cdylib"]

[dependencies]
alloy-primitives = "=0.8.20"
stylus-sdk = "0.9.0"

[dev-dependencies]
alloy-primitives = { version = "=0.8.20", features = [ "tiny-keccak" ] }
stylus-sdk = { version = "0.9.0", features = ["stylus-test"] }

Linker error (aarch64-darwin rustc 1.88.0):

  = note: Undefined symbols for architecture arm64:
            "_call_contract", referenced from:
                stylus_sdk::call::raw::RawCall::call::h4525d563da11d3fc in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.1.rcgu.o)
               (maybe you meant: _$LT$stylus_sdk..host..WasmVM$u20$as$u20$stylus_core..host..UnsafeCallAccess$GT$::static_call_contract::ha04b0544e53e1858, _$LT$stylus_sdk..host..WasmVM$u20$as$u20$stylus_core..host..UnsafeCallAccess$GT$::delegate_call_contract::h1235a25d88076e86 , _$LT$stylus_test..vm..TestVM$u20$as$u20$stylus_core..host..UnsafeCallAccess$GT$::static_call_contract::h7cd318da66d7c6d5 , _$LT$stylus_test..vm..TestVM$u20$as$u20$stylus_core..host..UnsafeCallAccess$GT$::delegate_call_contract::hc703e24d77cca0cd )
            "_delegate_call_contract", referenced from:
                stylus_sdk::call::raw::RawCall::call::h4525d563da11d3fc in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.1.rcgu.o)
            "_read_return_data", referenced from:
                stylus_sdk::contract::read_return_data::hbaf2efd5885a2430 in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.0.rcgu.o)
            "_return_data_size", referenced from:
                stylus_sdk::contract::return_data_len::h00909092eca5bd34 in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.0.rcgu.o)
            "_static_call_contract", referenced from:
                stylus_sdk::call::raw::RawCall::call::h4525d563da11d3fc in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.1.rcgu.o)
            "_storage_flush_cache", referenced from:
                stylus_sdk::storage::StorageCache::flush::hf25beac3b5e6f57a in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.0.rcgu.o)
                stylus_sdk::storage::StorageCache::clear::hf4900a2d38c714f4 in libstylus_sdk-f90d910a78be94f8.rlib(stylus_sdk-f90d910a78be94f8.stylus_sdk.99c5cd16912e6307-cgu.0.rcgu.o)
          ld: symbol(s) not found for architecture arm64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions