To call another contract you declare an `interface` describing its external functions, then wrap the target address in that interface's name to get a typed handle.
interface ERC20:
def transfer(to: address, amount: uint256) -> bool: nonpayable
def balanceOf(owner: address) -> uint256: view
@external
def pay(token: address, to: address, amount: uint256):
assert ERC20(token).transfer(to, amount), "transfer failed"Declaring the correct state mutability (`view`, `nonpayable`, `payable`) on each interface function matters — it affects both correctness and the gas estimation the caller performs.