Skip to content

Traits

Traits define interfaces that contracts must implement. They are used for contract interoperability and SIP (Stacks Improvement Proposal) compliance.

Trait Definitions

A trait declares a set of function signatures without implementations:

trait Token {
    transfer(from: principal, to: principal, amount: uint): Response<bool, uint>;
    get_balance(account: principal): uint;
    get_total_supply(): uint;
}

Generated Clarity:

(define-trait Token
  ((transfer (principal principal uint) (response bool uint))
   (get-balance (principal) uint)
   (get-total-supply () uint)))

Trait Function Signatures

Each entry in a trait specifies: - Function name - Parameter types - Return type

Traits cannot include variable or map declarations -- they define behavior only.

Implementing Traits

Use @implements (or implements keyword) to declare that a contract satisfies a trait:

@implements(Token)
contract MyToken {
    @public
    function transfer(from: principal, to: principal, amount: uint): Response<bool, uint> {
        return ok(true);
    }

    @readonly
    function get_balance(account: principal): uint {
        return 0u;
    }

    @readonly
    function get_total_supply(): uint {
        return total_supply;
    }
}

Generated Clarity:

(impl-trait .Token)

(define-public (transfer (from principal) (to principal) (amount uint))
  (ok true))

(define-read-only (get-balance (account principal))
  u0)

(define-read-only (get-total-supply)
  (var-get total-supply))

Trait Compliance Checking

The semantic analyzer verifies that implementations satisfy their traits:

Missing Methods

trait Countable {
    increment(): Response<uint, uint>;
    get_count(): uint;
}

@implements(Countable)
contract Counter {
    @public
    function increment(): Response<uint, uint> {
        return ok(1u);
    }
    // Error: Missing trait method 'get_count'
}

Signature Mismatch

The analyzer checks that parameter types and return types match exactly:

trait Token {
    transfer(to: principal, amount: uint): Response<bool, uint>;
}

@implements(Token)
contract BadToken {
    @public
    function transfer(to: principal, amount: int): Response<bool, uint> {
        // Error: Parameter type mismatch - expected uint, got int
        return ok(true);
    }
}

SIP Compliance

Stacks Improvement Proposals define standard traits for common contract patterns:

SIP-009 (NFT Standard)

trait SIP009 {
    get_last_token_id(): Response<uint, uint>;
    get_token_uri(id: uint): Response<Optional<string>, uint>;
    get_owner(id: uint): Response<Optional<principal>, uint>;
    transfer(id: uint, sender: principal, recipient: principal): Response<bool, uint>;
}

SIP-010 (Fungible Token Standard)

trait SIP010 {
    transfer(amount: uint, sender: principal, recipient: principal, memo: Optional<buffer<34>>): Response<bool, uint>;
    get_name(): Response<string, uint>;
    get_symbol(): Response<string, uint>;
    get_decimals(): Response<uint, uint>;
    get_balance(account: principal): Response<uint, uint>;
    get_total_supply(): Response<uint, uint>;
    get_token_uri(): Response<Optional<string>, uint>;
}

Implementing SIP-010

@implements(SIP010)
contract MyToken {
    const TOKEN_NAME: string = "My Token";
    const TOKEN_SYMBOL: string = "MTK";
    const DECIMALS: uint = 6u;

    map balances<principal, uint>;
    let total_supply: uint = 0u;

    @public
    function transfer(amount: uint, sender: principal, recipient: principal, memo: Optional<buffer<34>>): Response<bool, uint> {
        if (sender != tx-sender) {
            return err(ERR_UNAUTHORIZED);
        }
        // ... transfer logic
        return ok(true);
    }

    @readonly
    function get_name(): Response<string, uint> {
        return ok(TOKEN_NAME);
    }

    @readonly
    function get_symbol(): Response<string, uint> {
        return ok(TOKEN_SYMBOL);
    }

    @readonly
    function get_decimals(): Response<uint, uint> {
        return ok(DECIMALS);
    }

    @readonly
    function get_balance(account: principal): Response<uint, uint> {
        return ok(balances.get(account) ?? 0u);
    }

    @readonly
    function get_total_supply(): Response<uint, uint> {
        return ok(total_supply);
    }

    @readonly
    function get_token_uri(): Response<Optional<string>, uint> {
        return ok(none);
    }
}

Multiple Traits

A contract can implement multiple traits:

@implements(SIP010)
@implements(Ownable)
contract ManagedToken {
    // Must satisfy both SIP010 and Ownable methods
}

Trait as Parameter Types

Traits can be used as parameter types for inter-contract calls:

@public
function swap(token_a: <SIP010>, token_b: <SIP010>, amount: uint): Response<bool, uint> {
    // Call methods on any contract implementing SIP010
    return ok(true);
}

Next Steps