Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.
This repository was archived by the owner on Jan 9, 2026. It is now read-only.

Module references #12

@sirlensalot

Description

@sirlensalot

Problem Statement

Pact lacks a mechanism for polymorphic dynamic dispatch: all dispatch in Pact is resolved at compile/install time, and polymorphic dispatch is not offered at all. While this is not a significant limitation for many smart contract applications, and indeed "static linking" has safety benefits, certain kinds of apps need to interact with other applications dynamically.

Consider a Uniswap-style exchange that interacts with fungible-v2 tokens. On the EVM, Uniswap offers the ability to add new pairs on-demand, as a business-critical feature. In Pact, dynamic interaction with other contracts is not possible.

A work-around could employ a mechanism, such as a "fungible registry" that would provide indirection along the lines of dispatching on token symbol to fungible-v2 methods like transfer. A new token would simply require a modification referencing the new token's code and an update to the code on the blockchain. Such a registry could service any number of applications seeking to access fungible tokens.

Nonetheless, in addition to updating the registry, the client software, in this example the exchange, would have to be re-uploaded simply to link to the new registry version. This is because Pact intentionally does not automatically upgrade downstream dependencies, as an safety feature for code stability and resilience.

However, the registry code itself would reveal another problem: the lack of a polymorphic dispatch mechanism. The registry would have to manually case on symbol for each operation, resulting in massive duplicative calls to transfer and other methods. Code proliferates with the potential for bugs.

A solution must allow for dynamic dispatch to a module, and polymorphic access to the methods of implemented interfaces.

Proposal Draft: Module References

Module references reify a Pact module into a concrete datatype. For purposes of this draft we will say it's type is ref, but it might benefit from a different name. Here is a function signature that accepts a module ref:

(defun add-fungible ( symbol:string fungible:ref ) )

ref by itself is not very useful as it gives no indication what interface the module implements. A simple re-purposing of Pact's user-schema type syntax could indicate a single supported interface:

(defun add-fungible ( symbol:string fungible:ref{fungible-v2} ) )

If refs were to support multiple interfaces, this syntax would be insufficient. However an argument can be made for only supporting single interfaces to make roles clear. Consider an ancilliary interface to fungible-v2 that specifies whitelisting, a signature would expect two arguments:

(defun add-whitelist-fungible ( symbol:string fungible:ref{fungible-v2} wlist:ref{whitelist-v1} ) )

Usage

The simplest way to use a module ref is for it to "stand in" for standard Pact unqualified namespace syntax:

  (wlist.approve "Alice" 100.0)
  (fungible.transfer "Alice" "Bob" 10.0)

This of course will complicate resolution. Presumably reference arguments would shadow unqualified namespaces.

Instantiation

The most straightforward way to provide a module reference would be to reference the module itself as a bareword:

  (exchange.add-fungible "KDA" coin)

In the case of a module that implements two interfaces, it would just be used twice:

  (exchange.add-whitelist-fungible "kDOGE" user.kdogecoin user.kdogecoin)

At least in the root namespace, this would cause name clashes with existing Pact code which is free to reuse module names. For instance the coin contract can use a variable called coin with no ambiguity.

This also impacts resolution as it would introduce [namespace.]module references in Pact code as concrete datatypes. An alternate approach would be to explicitly declare module references, with perhaps a defref form:

(module kdogecoin GOV
  (implements fungible-v2)
  (implements whitelist-v1)
  (defref FUNGIBLE fungible-v2)
  ...
)

Or, an implements declaration could be extended to declare the ref:

  (implements fungible-v2 as FUNGIBLE)

The advantage of this is simply to provide a context for resolution, as opposed to special syntactic support for recognizing module refs. It also avoids the name clash issues.

Additional requirements

A module reference MUST be storable in the database, in order for something like add-fungible to work as intended.

Further considerations

Safety. Pact's existing system of static linking provides strong guarantees:

  1. Turing incompleteness. Pact statically guarantees that no code can recurse. This also has performance benefits as the runtime is always walking a fixed tree of outcomes.
  2. Dependency safety. Static linking guarantees that "the code you upload (and sign) is the code you run". Dynamic linking implies that new code can be executed at any time.
  3. Admittability to formal verification. Pact's static, terminating trees are very easy to verify. Interfaces do support properties, so there is some ability to leverage those in a verification environment. However it is simply impossible to make guarantees about arbitrary code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions