neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
use super::*;

#[test]
fn non_builtin_libraries_are_merged_for_internal_calls() {
    let source = r#"
    pragma solidity ^0.8.19;

    library MathLib {
        function double(uint256 x) internal pure returns (uint256) {
            return x * 2;
        }
    }

    contract UsesLib {
        function f(uint256 x) public pure returns (uint256) {
            return MathLib.double(x);
        }
    }
    "#;

    let artifacts = compile_contracts(source, false, 2).expect("compilation failed");
    assert_eq!(artifacts.len(), 1);
}

#[test]
fn non_builtin_libraries_allow_external_static_calls() {
    let source = r#"
    pragma solidity ^0.8.34;

    library MathLib {
        function bump(uint256 x) external pure returns (uint256) {
            return x + 1;
        }
    }

    contract UsesLib {
        function f(uint256 x) public pure returns (uint256) {
            return MathLib.bump(x);
        }
    }
    "#;

    let artifacts = compile_contracts(source, false, 2).expect("compilation failed");
    assert_eq!(artifacts.len(), 1);
}

#[test]
fn non_builtin_libraries_allow_external_member_calls_via_using() {
    let source = r#"
    pragma solidity ^0.8.34;

    library MathLib {
        function bump(uint256 self, uint256 delta) external pure returns (uint256) {
            return self + delta;
        }
    }

    contract UsesLib {
        using MathLib for uint256;

        function f(uint256 x) public pure returns (uint256) {
            return x.bump(1);
        }
    }
    "#;

    let artifacts = compile_contracts(source, false, 2).expect("compilation failed");
    assert_eq!(artifacts.len(), 1);
}

#[test]
fn builtin_libraries_are_not_merged_into_contract_metadata() {
    let source = r#"
    pragma solidity ^0.8.19;

    library Syscalls {
        // Even if a Solidity source provides an implementation, `Syscalls.*` is treated as a
        // compiler intrinsic and should not be merged into the user contract.
        function getTime() internal view returns (uint256) {
            return 123;
        }
    }

    contract UsesSyscalls {
        function nowTs() public view returns (uint256) {
            return Syscalls.getTime();
        }
    }
    "#;

    let artifacts = compile_contracts(source, false, 2).expect("compilation failed");
    assert_eq!(artifacts.len(), 1);

    let method_names: Vec<_> = artifacts[0]
        .metadata
        .methods
        .iter()
        .map(|m| m.name.as_str())
        .collect();

    assert!(
        method_names.contains(&"nowTs"),
        "expected user method to remain present"
    );
    assert!(
        !method_names.contains(&"getTime"),
        "expected Syscalls.getTime to not be merged into contract metadata"
    );
}

#[test]
fn using_for_typed_receiver_enforces_type_compatibility() {
    let source = r#"
    pragma solidity ^0.8.19;

    library UintLib {
        function bump(uint256 self, uint256 delta) internal pure returns (uint256) {
            return self + delta;
        }
    }

    contract UsesTypedUsing {
        using UintLib for uint256;

        function ok(uint256 x) public pure returns (uint256) {
            return x.bump(1);
        }

        function bad(bool flag) public pure returns (uint256) {
            return flag.bump(1);
        }
    }
    "#;

    let err = compile_contracts(source, false, 2).expect_err("expected receiver type mismatch");
    match err {
        CompileError::Ir(messages) => {
            assert!(
                messages
                    .iter()
                    .any(|diag| diag.message.contains("not available for receiver type")),
                "unexpected diagnostics: {messages:?}"
            );
        }
        other => panic!("unexpected error variant: {other:?}"),
    }
}

#[test]
fn using_function_list_restricts_member_names() {
    let source = r#"
    pragma solidity ^0.8.19;

    library MathLib {
        function add(uint256 self, uint256 rhs) internal pure returns (uint256) {
            return self + rhs;
        }

        function sub(uint256 self, uint256 rhs) internal pure returns (uint256) {
            return self - rhs;
        }
    }

    contract UsesFunctionList {
        using {MathLib.add} for uint256;

        function ok(uint256 x) public pure returns (uint256) {
            return x.add(1);
        }

        function bad(uint256 x) public pure returns (uint256) {
            return x.sub(1);
        }
    }
    "#;

    let err = compile_contracts(source, false, 2).expect_err("expected function-list rejection");
    match err {
        CompileError::Ir(messages) => {
            assert!(
                messages.iter().any(|diag| diag
                    .message
                    .contains("not allowed by `using {...} for ...` function lists")),
                "unexpected diagnostics: {messages:?}"
            );
        }
        other => panic!("unexpected error variant: {other:?}"),
    }
}

#[test]
fn member_style_library_calls_require_using_directive() {
    let source = r#"
    pragma solidity ^0.8.19;

    library MathLib {
        function add(uint256 self, uint256 rhs) internal pure returns (uint256) {
            return self + rhs;
        }
    }

    contract MissingUsing {
        function bad(uint256 x) public pure returns (uint256) {
            return x.add(1);
        }
    }
    "#;

    let err = compile_contracts(source, false, 2).expect_err("expected missing-using failure");
    match err {
        CompileError::Ir(messages) => {
            assert!(
                messages.iter().any(|diag| diag
                    .message
                    .contains("requires an explicit `using` directive")),
                "unexpected diagnostics: {messages:?}"
            );
        }
        other => panic!("unexpected error variant: {other:?}"),
    }
}