Skip to main content

Cross File Support

Skittles supports sharing types, functions, and constants across multiple contract files. Organize your project just like you would any TypeScript application — define types in one file and import them wherever you need them.

Shared Types

Type aliases (structs), interfaces (contract interfaces), and enums defined in one file can be used in any other contract file:

contracts/types.ts
import { address } from "skittles";

export enum VaultStatus {
Active,
Paused,
}

export type StakeInfo = {
amount: number;
timestamp: number;
account: address;
};

export interface IStaking {
getStakeInfo(account: address): StakeInfo;
}
contracts/Staking.ts
import { VaultStatus, StakeInfo } from "./types";

export class Staking {
public status: VaultStatus;

public getStakeInfo(account: address): StakeInfo {
let info: StakeInfo = {
amount: this.deposits[account],
timestamp: this.depositTimestamps[account],
account: account,
};
return info;
}
}

Imports work just like standard TypeScript. Define your shared types in one file, import them in your contracts, and Skittles handles the rest.

Shared Functions

Functions declared at file level (outside any class) are available to all contracts:

contracts/utils.ts
function calculateFee(amount: number, bps: number): number {
return (amount * bps) / 10000;
}
contracts/Vault.ts
export class Vault {
withdraw(amount: number): void {
let fee: number = calculateFee(amount, 50);
// ...
}
}

Arrow functions work the same way:

const calculateFee = (amount: number, bps: number): number => {
return (amount * bps) / 10000;
};

Shared Constants

Constants declared at file level can be shared across your contracts:

contracts/constants.ts
const MAX_FEE = 500;
const FEE_DENOMINATOR = 10000;
contracts/Vault.ts
export class Vault {
withdraw(amount: number): void {
let fee: number = (amount * MAX_FEE) / FEE_DENOMINATOR;
}
}

How It Works

Skittles automatically scans all files in your contracts directory and makes shared definitions available to any contract that uses them. Only the types, enums, functions, and constants that a contract actually references are included in its generated Solidity — unused definitions are excluded to keep the compiled output lean.

External Contract Calls

Smart contracts often need to interact with other deployed contracts. Skittles supports this through interfaces and the Contract<T>() helper.

Define an Interface

First, define a TypeScript interface for the external contract:

contracts/IToken.ts
import { address } from "skittles";

export default interface IToken {
name: string;
totalSupply: number;
balanceOf(account: address): number;
transfer(to: address, amount: number): boolean;
}

Call the External Contract

Use Contract<T>(address) to create a typed reference to a deployed contract:

contracts/Vault.ts
import { address, msg, Contract } from "skittles";
import IToken from "./IToken";

export class Vault {
private token: IToken;

constructor(tokenAddress: address) {
this.token = Contract<IToken>(tokenAddress);
}

public getTokenName(): string {
return this.token.name;
}

public getBalance(account: address): number {
return this.token.balanceOf(account);
}

public withdraw(to: address, amount: number): void {
this.token.transfer(to, amount);
}
}

Interface properties (like name and totalSupply) compile to view getter calls. You can access them with natural property syntax — no parentheses needed:

interface IToken {
function name() external view returns (string memory);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
}

contract Vault {
IToken internal token;

constructor(address tokenAddress) {
token = IToken(tokenAddress);
}

function getTokenName() public view virtual returns (string memory) {
return token.name();
}

function getBalance(address account) public view virtual returns (uint256) {
return token.balanceOf(account);
}

function withdraw(address to, uint256 amount) public virtual {
token.transfer(to, amount);
}
}

Interface as Function Parameter

You can also pass contract references as function parameters:

public getBalance(token: IToken, account: address): number {
return token.balanceOf(account);
}

Cache Invalidation

If you change shared types, functions, or constants, all contracts that use them are automatically recompiled. See Incremental Compilation for details.