Skip to main content
Version: Current

Interfaces

An interface is an abstract type that specifies the behavior of types that implement the interface. Interfaces declare the required functions and fields, the access control for those declarations, and preconditions and postconditions that implementing types need to provide.

There are three kinds of interfaces:

  • Structure interfaces: implemented by structures
  • Resource interfaces: implemented by resources
  • Contract interfaces: implemented by contracts

Structure, resource, and contract types may implement multiple interfaces.

There is no support for event and enum interfaces.

Nominal typing applies to composite types that implement interfaces. This means that a type only implements an interface if it has explicitly declared the conformance, the composite type does not implicitly conform to an interface, even if it satisfies all requirements of the interface.

Interfaces consist of the function and field requirements that a type implementing the interface must provide implementations for. Interface requirements, and therefore also their implementations, must always be at least public.

Variable field requirements may be annotated to require them to be publicly settable.

Function requirements consist of the name of the function, parameter types, an optional return type, and optional preconditions and postconditions.

Field requirements consist of the name and the type of the field. Field requirements may optionally declare a getter requirement and a setter requirement, each with preconditions and postconditions.

Calling functions with preconditions and postconditions on interfaces instead of concrete implementations can improve the security of a program, as it ensures that even if implementations change, some aspects of them will always hold.

Interface Declaration

Interfaces are declared using the struct, resource, or contract keyword, followed by the interface keyword, the name of the interface, and the requirements, which must be enclosed in opening and closing braces.

Field requirements can be annotated to require the implementation to be a variable field, by using the var keyword; require the implementation to be a constant field, by using the let keyword; or the field requirement may specify nothing, in which case the implementation may either be a variable or a constant field.

Field requirements and function requirements must specify the required level of access. The access must be at least be public, so the pub keyword must be provided. Variable field requirements can be specified to also be publicly settable by using the pub(set) keyword.

Interfaces can be used in types. This is explained in detail in the section Interfaces in Types. For now, the syntax {I} can be read as the type of any value that implements the interface I.


_77
// Declare a resource interface for a fungible token.
_77
// Only resources can implement this resource interface.
_77
//
_77
pub resource interface FungibleToken {
_77
_77
// Require the implementing type to provide a field for the balance
_77
// that is readable in all scopes (`pub`).
_77
//
_77
// Neither the `var` keyword, nor the `let` keyword is used,
_77
// so the field may be implemented as either a variable
_77
// or as a constant field.
_77
//
_77
pub balance: Int
_77
_77
// Require the implementing type to provide an initializer that
_77
// given the initial balance, must initialize the balance field.
_77
//
_77
init(balance: Int) {
_77
pre {
_77
balance >= 0:
_77
"Balances are always non-negative"
_77
}
_77
post {
_77
self.balance == balance:
_77
"the balance must be initialized to the initial balance"
_77
}
_77
_77
// NOTE: The declaration contains no implementation code.
_77
}
_77
_77
// Require the implementing type to provide a function that is
_77
// callable in all scopes, which withdraws an amount from
_77
// this fungible token and returns the withdrawn amount as
_77
// a new fungible token.
_77
//
_77
// The given amount must be positive and the function implementation
_77
// must add the amount to the balance.
_77
//
_77
// The function must return a new fungible token.
_77
// The type `{FungibleToken}` is the type of any resource
_77
// that implements the resource interface `FungibleToken`.
_77
//
_77
pub fun withdraw(amount: Int): @{FungibleToken} {
_77
pre {
_77
amount > 0:
_77
"the amount must be positive"
_77
amount <= self.balance:
_77
"insufficient funds: the amount must be smaller or equal to the balance"
_77
}
_77
post {
_77
self.balance == before(self.balance) - amount:
_77
"the amount must be deducted from the balance"
_77
}
_77
_77
// NOTE: The declaration contains no implementation code.
_77
}
_77
_77
// Require the implementing type to provide a function that is
_77
// callable in all scopes, which deposits a fungible token
_77
// into this fungible token.
_77
//
_77
// No precondition is required to check the given token's balance
_77
// is positive, as this condition is already ensured by
_77
// the field requirement.
_77
//
_77
// The parameter type `{FungibleToken}` is the type of any resource
_77
// that implements the resource interface `FungibleToken`.
_77
//
_77
pub fun deposit(_ token: @{FungibleToken}) {
_77
post {
_77
self.balance == before(self.balance) + token.balance:
_77
"the amount must be added to the balance"
_77
}
_77
_77
// NOTE: The declaration contains no implementation code.
_77
}
_77
}

Note that the required initializer and functions do not have any executable code.

Struct and resource Interfaces can only be declared directly inside contracts, i.e. not inside of functions. Contract interfaces can only be declared globally and not inside contracts.

Interface Implementation

Declaring that a type implements (conforms) to an interface is done in the type declaration of the composite type (e.g., structure, resource): The kind and the name of the composite type is followed by a colon (:) and the name of one or more interfaces that the composite type implements.

This will tell the checker to enforce any requirements from the specified interfaces onto the declared type.

A type implements (conforms to) an interface if it declares the implementation in its signature, provides field declarations for all fields required by the interface, and provides implementations for all functions required by the interface.

The field declarations in the implementing type must match the field requirements in the interface in terms of name, type, and declaration kind (e.g. constant, variable) if given. For example, an interface may require a field with a certain name and type, but leaves it to the implementation what kind the field is.

The function implementations must match the function requirements in the interface in terms of name, parameter argument labels, parameter types, and the return type.


_104
// Declare a resource named `ExampleToken` that has to implement
_104
// the `FungibleToken` interface.
_104
//
_104
// It has a variable field named `balance`, that can be written
_104
// by functions of the type, but outer scopes can only read it.
_104
//
_104
pub resource ExampleToken: FungibleToken {
_104
_104
// Implement the required field `balance` for the `FungibleToken` interface.
_104
// The interface does not specify if the field must be variable, constant,
_104
// so in order for this type (`ExampleToken`) to be able to write to the field,
_104
// but limit outer scopes to only read from the field, it is declared variable,
_104
// and only has public access (non-settable).
_104
//
_104
pub var balance: Int
_104
_104
// Implement the required initializer for the `FungibleToken` interface:
_104
// accept an initial balance and initialize the `balance` field.
_104
//
_104
// This implementation satisfies the required postcondition.
_104
//
_104
// NOTE: the postcondition declared in the interface
_104
// does not have to be repeated here in the implementation.
_104
//
_104
init(balance: Int) {
_104
self.balance = balance
_104
}
_104
_104
// Implement the required function named `withdraw` of the interface
_104
// `FungibleToken`, that withdraws an amount from the token's balance.
_104
//
_104
// The function must be public.
_104
//
_104
// This implementation satisfies the required postcondition.
_104
//
_104
// NOTE: neither the precondition nor the postcondition declared
_104
// in the interface have to be repeated here in the implementation.
_104
//
_104
pub fun withdraw(amount: Int): @ExampleToken {
_104
self.balance = self.balance - amount
_104
return create ExampleToken(balance: amount)
_104
}
_104
_104
// Implement the required function named `deposit` of the interface
_104
// `FungibleToken`, that deposits the amount from the given token
_104
// to this token.
_104
//
_104
// The function must be public.
_104
//
_104
// NOTE: the type of the parameter is `{FungibleToken}`,
_104
// i.e., any resource that implements the resource interface `FungibleToken`,
_104
// so any other token – however, we want to ensure that only tokens
_104
// of the same type can be deposited.
_104
//
_104
// This implementation satisfies the required postconditions.
_104
//
_104
// NOTE: neither the precondition nor the postcondition declared
_104
// in the interface have to be repeated here in the implementation.
_104
//
_104
pub fun deposit(_ token: @{FungibleToken}) {
_104
if let exampleToken <- token as? ExampleToken {
_104
self.balance = self.balance + exampleToken.balance
_104
destroy exampleToken
_104
} else {
_104
panic("cannot deposit token which is not an example token")
_104
}
_104
}
_104
}
_104
_104
// Declare a constant which has type `ExampleToken`,
_104
// and is initialized with such an example token.
_104
//
_104
let token <- create ExampleToken(balance: 100)
_104
_104
// Withdraw 10 units from the token.
_104
//
_104
// The amount satisfies the precondition of the `withdraw` function
_104
// in the `FungibleToken` interface.
_104
//
_104
// Invoking a function of a resource does not destroy the resource,
_104
// so the resource `token` is still valid after the call of `withdraw`.
_104
//
_104
let withdrawn <- token.withdraw(amount: 10)
_104
_104
// The postcondition of the `withdraw` function in the `FungibleToken`
_104
// interface ensured the balance field of the token was updated properly.
_104
//
_104
// `token.balance` is `90`
_104
// `withdrawn.balance` is `10`
_104
_104
// Deposit the withdrawn token into another one.
_104
let receiver: @ExampleToken <- // ...
_104
receiver.deposit(<-withdrawn)
_104
_104
// Run-time error: The precondition of function `withdraw` in interface
_104
// `FungibleToken` fails, the program aborts: the parameter `amount`
_104
// is larger than the field `balance` (100 > 90).
_104
//
_104
token.withdraw(amount: 100)
_104
_104
// Withdrawing tokens so that the balance is zero does not destroy the resource.
_104
// The resource has to be destroyed explicitly.
_104
//
_104
token.withdraw(amount: 90)

The access level for variable fields in an implementation may be less restrictive than the interface requires. For example, an interface may require a field to be at least public (i.e. the pub keyword is specified), and an implementation may provide a variable field which is public, but also publicly settable (the pub(set) keyword is specified).


_20
pub struct interface AnInterface {
_20
// Require the implementing type to provide a publicly readable
_20
// field named `a` that has type `Int`. It may be a variable
_20
// or a constant field.
_20
//
_20
pub a: Int
_20
}
_20
_20
pub struct AnImplementation: AnInterface {
_20
// Declare a publicly settable variable field named `a` that has type `Int`.
_20
// This implementation satisfies the requirement for interface `AnInterface`:
_20
// The field is at least publicly readable, but this implementation also
_20
// allows the field to be written to in all scopes.
_20
//
_20
pub(set) var a: Int
_20
_20
init(a: Int) {
_20
self.a = a
_20
}
_20
}

Interfaces in Types

Interfaces can be used in types: The type {I} is the type of all objects that implement the interface I.

This is called a restricted type: Only the functionality (members and functions) of the interface can be used when accessing a value of such a type.


_70
// Declare an interface named `Shape`.
_70
//
_70
// Require implementing types to provide a field which returns the area,
_70
// and a function which scales the shape by a given factor.
_70
//
_70
pub struct interface Shape {
_70
pub fun getArea(): Int
_70
pub fun scale(factor: Int)
_70
}
_70
_70
// Declare a structure named `Square` the implements the `Shape` interface.
_70
//
_70
pub struct Square: Shape {
_70
// In addition to the required fields from the interface,
_70
// the type can also declare additional fields.
_70
//
_70
pub var length: Int
_70
_70
// Provided the field `area` which is required to conform
_70
// to the interface `Shape`.
_70
//
_70
// Since `area` was not declared as a constant, variable,
_70
// field in the interface, it can be declared.
_70
//
_70
pub fun getArea(): Int {
_70
return self.length * self.length
_70
}
_70
_70
pub init(length: Int) {
_70
self.length = length
_70
}
_70
_70
// Provided the implementation of the function `scale`
_70
// which is required to conform to the interface `Shape`.
_70
//
_70
pub fun scale(factor: Int) {
_70
self.length = self.length * factor
_70
}
_70
}
_70
_70
// Declare a structure named `Rectangle` that also implements the `Shape` interface.
_70
//
_70
pub struct Rectangle: Shape {
_70
pub var width: Int
_70
pub var height: Int
_70
_70
// Provided the field `area which is required to conform
_70
// to the interface `Shape`.
_70
//
_70
pub fun getArea(): Int {
_70
return self.width * self.height
_70
}
_70
_70
pub init(width: Int, height: Int) {
_70
self.width = width
_70
self.height = height
_70
}
_70
_70
// Provided the implementation of the function `scale`
_70
// which is required to conform to the interface `Shape`.
_70
//
_70
pub fun scale(factor: Int) {
_70
self.width = self.width * factor
_70
self.height = self.height * factor
_70
}
_70
}
_70
_70
// Declare a constant that has type `Shape`, which has a value that has type `Rectangle`.
_70
//
_70
var shape: {Shape} = Rectangle(width: 10, height: 20)

Values implementing an interface are assignable to variables that have the interface as their type.


_10
// Assign a value of type `Square` to the variable `shape` that has type `Shape`.
_10
//
_10
shape = Square(length: 30)
_10
_10
// Invalid: cannot initialize a constant that has type `Rectangle`.
_10
// with a value that has type `Square`.
_10
//
_10
let rectangle: Rectangle = Square(length: 10)

Fields declared in an interface can be accessed and functions declared in an interface can be called on values of a type that implements the interface.


_14
// Declare a constant which has the type `Shape`.
_14
// and is initialized with a value that has type `Rectangle`.
_14
//
_14
let shape: {Shape} = Rectangle(width: 2, height: 3)
_14
_14
// Access the field `area` declared in the interface `Shape`.
_14
//
_14
shape.area // is `6`
_14
_14
// Call the function `scale` declared in the interface `Shape`.
_14
//
_14
shape.scale(factor: 3)
_14
_14
shape.area // is `54`

Interface Nesting

info

🚧 Status: Currently only contracts and contract interfaces support nested interfaces.

Interfaces can be arbitrarily nested. Declaring an interface inside another does not require implementing types of the outer interface to provide an implementation of the inner interfaces.


_23
// Declare a resource interface `OuterInterface`, which declares
_23
// a nested structure interface named `InnerInterface`.
_23
//
_23
// Resources implementing `OuterInterface` do not need to provide
_23
// an implementation of `InnerInterface`.
_23
//
_23
// Structures may just implement `InnerInterface`.
_23
//
_23
resource interface OuterInterface {
_23
_23
struct interface InnerInterface {}
_23
}
_23
_23
// Declare a resource named `SomeOuter` that implements the interface `OuterInterface`.
_23
//
_23
// The resource is not required to implement `OuterInterface.InnerInterface`.
_23
//
_23
resource SomeOuter: OuterInterface {}
_23
_23
// Declare a structure named `SomeInner` that implements `InnerInterface`,
_23
// which is nested in interface `OuterInterface`.
_23
//
_23
struct SomeInner: OuterInterface.InnerInterface {}

Interface Default Functions

Interfaces can provide default functions: If the concrete type implementing the interface does not provide an implementation for the function required by the interface, then the interface's default function is used in the implementation.


_27
// Declare a struct interface `Container`,
_27
// which declares a default function `getCount`.
_27
//
_27
struct interface Container {
_27
_27
let items: [AnyStruct]
_27
_27
fun getCount(): Int {
_27
return self.items.length
_27
}
_27
}
_27
_27
// Declare a concrete struct named `Numbers` that implements the interface `Container`.
_27
//
_27
// The struct does not implement the function `getCount` of the interface `Container`,
_27
// so the default function for `getCount` is used.
_27
//
_27
struct Numbers: Container {
_27
let items: [AnyStruct]
_27
_27
init() {
_27
self.items = []
_27
}
_27
}
_27
_27
let numbers = Numbers()
_27
numbers.getCount() // is 0

Interfaces cannot provide default initializers or default destructors.

Only one conformance may provide a default function.

Nested Type Requirements

info

🚧 Status: Currently only contracts and contract interfaces support nested type requirements.

Interfaces can require implementing types to provide concrete nested types. For example, a resource interface may require an implementing type to provide a resource type.


_22
// Declare a resource interface named `FungibleToken`.
_22
//
_22
// Require implementing types to provide a resource type named `Vault`
_22
// which must have a field named `balance`.
_22
//
_22
resource interface FungibleToken {
_22
pub resource Vault {
_22
pub balance: Int
_22
}
_22
}
_22
// Declare a resource named `ExampleToken` that implements the `FungibleToken` interface.
_22
//
_22
// The nested type `Vault` must be provided to conform to the interface.
_22
//
_22
resource ExampleToken: FungibleToken {
_22
pub resource Vault {
_22
pub var balance: Int
_22
init(balance: Int) {
_22
self.balance = balance
_22
}
_22
}
_22
}