Custom Imports
If you have a function that is reusable, you can store it in a
separate .cairo file and then import it. This can be helpful to
make contracts more readable and modular.
Consider an application that has an addition function.
- contracts/
    - custom_imports.cairo
    - utils/
        - math.cairo
%lang starknet
%builtins range_check
# contracts/custom_imports.cairo
# Import from teh cairo-lang package
from starkware.cairo.common.cairo_builtins import HashBuiltin
# Import a function from a custom local file.
from contracts.utils.math import add_two, get_modulo
# This is the main contract file that will be deployed.
@view
func get_calculations{
        range_check_ptr
    }(
        first : felt,
        second : felt
    ) -> (
        sum : felt,
        modulo : felt
    ):
    # Two custom operations.
    let (sum) = add_two(first, second)
    let (modulo) = get_modulo(first, second)
    return (sum, modulo)
end
Save the contract above as contracts/custom_imports.cairo
Now save the module to be imported (below) as contracts/utils/math.cairo.
Nile will deploy the main contract and bring the required functions
in during compilation. Only one contract is deployed.
%lang starknet
# This function is from the common library.
# It can live here, reducing the clutter of an application.
from starkware.cairo.common.math import unsigned_div_rem
# This function is imported by 'custom_import.cairo'
# Equivalent to placing this function in that file.
func add_two(
        a : felt,
        b : felt
    ) -> (
        sum : felt
    ):
    let sum = a + b
    return (sum)
end
# This function performs the modulo operation.
func get_modulo{
        range_check_ptr
    }(
        a : felt,
        b : felt
    ) -> (
        result : felt
    ):
    let (dividend, remainder) = unsigned_div_rem(a, b)
    # The dividend is not used, and the following is equivalent:
    # let (_, remainder) = unsigned_div_rem(a, b)
    return (remainder)
end
Compile
Compile
nile compile
Or compile this specific contract
nile compile contracts/custom_imports.cairo
Test
Make a new file called test_custom_imports.py and populate it:
import pytest
import asyncio
from starkware.starknet.testing.starknet import Starknet
@pytest.fixture(scope='module')
def event_loop():
    return asyncio.new_event_loop()
@pytest.fixture(scope='module')
async def contract_factory():
    starknet = await Starknet.empty()
    # Note how the contracts/utils/math.cairo file is not needed here.
    contract = await starknet.deploy("contracts/custom_imports.cairo")
    return starknet, contract
@pytest.mark.asyncio
async def test_contract(contract_factory):
    starknet, contract = contract_factory
    num_1 = 10
    num_2 = 3
    # Read from contract
    response = await contract.get_calculations(10, 3).call()
    # Check the results, addressing each returned value
    # by its name defined in the contract return statement.
    assert response.result.sum == num_1 + num_2
    assert response.result.modulo == num_1 % num_2
Run the test
pytest tests/test_custom_imports.py
Local Deployment
Deploy to the local devnet.
nile deploy custom_imports --alias custom_imports
Interact
Read
nile call custom_imports get_calculations 10 3
Returns: 13 1
Public deployment
Will default to the Goerli/alpha testnet until mainnet is available.
nile deploy custom_imports --alias custom_imports --network mainnet
Returns:
🚀 Deploying custom_imports
🌕 artifacts/custom_imports.json successfully deployed to 0x0030cfe7438b17ccf64584e4f76f038c1f7f647dc85eedf4be1b175c8d7a7d2b
📦 Registering deployment as custom_imports in mainnet.deployments.txt
Deployments can be viewed in the voyager explorer https://voyager.online