Interfaces with Pact
Last updated
Last updated
Throughout this tutorial you’ll learn why interfaces are valuable and how to implement them with Pact.
Key Takeaway
Interfaces allow you to provide access to the functions within one module to another module. They are defined using the interface
statement and can be accessed from within modules using the implement
statement.
Subscribe to our YouTube Channel to access the latest Pact tutorials.
Before getting into interfaces with Pact, I’ll briefly review what interfaces are in general.
Interfaces are the point where two systems meet and interact. This definition includes people, organizations, electricity, or any other interactions you can think of. For example, a light socket is a type of interface that gives you access to electricity.
In programming, interfaces work similarly, but are used to allow interaction between programs.
This isn’t specific to Pact, so if you are familiar with other programming languages, you have likely come across this idea already. If you are unfamiliar with interfaces, you may have at least a basic understanding of what an API is (Application Programming Interface).
As you might know, many programs have APIs that allow you to build programs that give access to specific data. For example, you could use Facebook APIs, Twitter APIs, Google APIs, or any other API to add functionality to your application that you wouldn’t have otherwise.
Aside from that, you may be familiar with UI, a User Interface. These types of interfaces give users access to elements of a program without using code at all.
There are many types of interfaces that have both high level and low level use cases in programming.
In this tutorial, you’ll focus specifically on building interfaces using Pact that allow users to create interactions between modules.
An interface, as defined in Pact, is a collection of models used for formal verification, constant definitions, and typed function signatures. They contain API specifications and data definitions for smart contracts.
They include each of the following elements:
Function Specifications
Constant Values
Models
Using these three elements, you can both declare and create interface.
Declare the Interface To declare an interface, use the statement interface
followed by the name of the interface.
Import a module inside an interface You can also import definitions from modules with a use
statement.
Import Module with Use Use this module from within an interface.
This allows for some interesting functionality within your interface.
Access the Interface from a Module After declaring an interface, you can access if from a module using the implements
statement.
Interfaces allow modules to communicate information between one another.
Modules and interfaces look very similar to one another, making them simple to program, but there are some key distinctions and ideas that are worth noting.
Interfaces Cannot be Upgraded Interfaces cannot be upgraded and no function implementations exist in an interface aside from constant data.
Constant Imports The constants of an interface can be imported with use
. Use
is not the same as implements
and you’ll see some of these important differences throughout the demonstration.
Conflicting Module Functions Multiple interfaces may be implemented by a single module. If there are conflicting function names among multiple interfaces, then the two interfaces are incompatible. In these cases you need to either inline the code you want, or redefine the interfaces to resolve the conflict.
Unique Interface Names Interface names must be unique within a namespace.
Accessing Interfaces Constants declared in an interface can be accessed directly by their fully qualified name.
This makes it so that they do not have the same naming constraints as function signatures.
Module Declarations Additionally, interfaces may make use of module declarations. This allows interfaces to import members of other modules. For that reason, interface signatures can be defined in terms of table types defined in an imported module.
Using the basic ideas described above, you can create more complex interactions by defining interfaces modules along with the implements
and use statements
.
Declare an Interface To start, the example below declares an interface named my-interface including a function named hello-number.
Implement the Interface Next, define a module that implements my-interface and makes use of the hello-number function.
Notice also, that within the module, a function named square-three is defined. This function makes use of a constant defined within the interface named SOME_CONSTANT having imported it with the use statement..
Formal verification is implemented at multiple levels within an interface in order to provide an extra level of security. Similar to modules, models may be declared within the body or function level of an interface. Models may be declared either within the body of the interface or at the function level in the same way that one would declare them in a module, with the exception of schema invariants.
You can't declare tables or schema in an interface, because there's no abstract "table" or "schema" that you can define or "implement" in a meaningful way. It also couples a module too tightly with a particular interface. You can, however, import a module and use the declared tables and schema in that module as types in the function signatures, or in models.
Models that you specify in an interface will be added with additional models you declare in a module. This allows you to layer more constraints on as you wish per your business needs in your module.
For the rest of this tutorial, you’ll take a closer look interfaces using a version of the coin contract from previous tutorials. You’ll create an interface to the coin contract that allows its functions to be accessed by other modules.
Chainweb Repo
Rather than using the tutorials GitHub repo like previously, you’ll find this project within the chainweb-node repo here. Chainweb is Kadena’s public blockchain platform. We haven’t discussed this in previous tutorials, but we will cover this in more detail in later tutorials. You don’t need to know what this is for this tutorial, but if you’d like, you can learn more about the basics of Chainweb here.
To get started with the demonstration, clone the project and open it in Atom.
Clone the project
Enter the Project File
Open in Atom
Looking at the interface, you’ll see that it includes 5 functions. These functions contain inputs and models allowing for the basic functionality of managing coins to be used by other modules.
Let’s take a closer look at this code to get a better idea of how it works.
Define the Interface
First, define an interface using the interface statement followed by the name of the interface.
Within this interface, you’ll create each of the functions.
Create-account takes inputs account and guard.
Within this function, a model is defined checking that the account is not an empty string. This will ensure that each account is given a name.
Transfer allows users to transfer value between a sender and a receiver.
It checks that the amount is greater than 0 and that the sender is not also the receiver before completing the transfer.
The function transfer
transfers coins to a known account. If that account doesn’t exist then the coins end up lost in the abyss with no way to retrieve them. For that reason, you may want to instead use the following function, transfer and create.
Transfer and create
supplies a guard for the receiving account. If the account exists, it checks to see if the guards match. If they do, it will complete the transfer, otherwise the transaction will fail. If the account does not exist, then the account will be created and the coin will be transferred to the new account.
Safe vs. Unsafe Transfers
The previous functions distinguish between "unsafe" and “safe” transfers. With unsafe transfers, your tokens could potentially get lost in the crypto abyss. With “safe” transfers, your tokens always end up in some account, otherwise the transaction fails and you are refunded.
The function account-balance takes an account string and returns the balance of the account.
Coinbase allows users to mint tokens to an address by defining the address, address-guard, and amount.
What does Coinbase mean?
The Coinbase transaction, or Generation transaction, is a special transaction. It specifically refers to a transaction that creates coins from nothing. In certain blockchain protocols, it is the reward that miner gets for successfully mining a block. It’s also the name of a popular digital asset exchange company named Coinbase.
Having created each of these functions within the interface, you can now create modules that use them for whatever purpose they might serve within your application.
That wraps up this introduction to Interfaces with Pact!
Throughout this tutorial you learned why interfaces are valuable and how to implement them with Pact.
We introduced interfaces and described how they are different than modules. From there you learned how to declare modules, how to work with interfaces, and viewed a demo app that put these basic ideas to use.
Take some time now to explore these ideas further, study the code demonstration, and try building an interface for yourself to get a better idea of how you can use this idea in your future applications.