Skip to main content

Template

What is a Template?

A Template in Luna is akin to an architectural plan for a house. While a Contract is the actual house built based on that plan, a Template is the reusable blueprint that the house can be built from.

Various elements like Participants, Parameters and Clauses are predefined in a Template. These elements work together to form the Template, allowing you to quickly scale to multiple Contracts that adhere to the same basic rules but have their own specific details.

Why is the Concept of a Template Needed?

The utility of a Template lies in its ability to standardize and scale. In financial services, many deals, instruments or contracts share a common structure but differ in the finer details.

A Template allows you to capture this commonality, making it easier to generate new Contracts by only adjusting the values that differ, such as counterparties, interest rates or loan amounts.

Example: Revolving Credit Facilities in Private Credit

Consider a private credit firm offering revolving credit facilities to various businesses. The foundational rules — like how interest accrues or what constitutes a default — are consistent across different businesses. However, each business might have a different credit rules or interest rate structure.

Here, a Template serves as the pro-forma structure, outlining the general rules and structure of a revolving credit facility. For each business that needs a facility, a new Contract is "built" off this Template by filling in the specific details like credit limit and interest rate. This approach ensures both consistency and efficiency.


Components of a Template

Clauses

Contracts run logic contained in Clauses - which are documented in detail on the next page

Participant Roles

Participants represent entities that can participate in a Contract. They may represent a single person or a group of people like a company or other group.

When a Participant is involved in a Contract they may take on one or more Roles within a Contract. A Role is a way of referring to a specific Participant onto a Contract.

For example a loan might contain two Roles, a Lender and a Borrower. The Borrower may be a Participant representing an individual customer and the Lender might represent a company.

With Roles established we can refer to the Participant via the Role for the remainder of the Template.

from luna import Template, Role, clause, Context
from luna.accounting.basic_account_chart import initialise_basic_account_chart

class Example(Template):
lender = Role()
borrower = Role()
servicer = Role(count=2) # roles can have more than one participant

@clause(on_init=True)
def initialise_investor_bsa(self, ctx: Context):
created_charts = initialise_basic_account_chart(
ctx,
operator=self.lender,
currency_unit="GBP",
)
...

Parameters

Parameters are data inputs that vary between Contracts that result from a Template. For example: the interest rate on a personal loan, or the monthly charge on a phone contract. Parameters are static and their values are set when a Contract is created from a Template.

Parameters are static for the duration of a Contract and cannot be updated / overridden by a Contract.

Parameter Types

When you write a Template you specify Parameters and their associated types. Parameters must be one of the following types:

  • Integer
  • String
  • Boolean
  • Decimal
  • List (optionally typed based on content)
  • Dictionary
  • Set
  • Datetime
  • Date
  • Time Delta
  • LunaModel - explained in detail here

Examples of Parameters of each available type:

from datetime import date, datetime, timedelta
from decimal import Decimal
from typing import List
from luna import Template, Parameter, clause, Context
from luna.types import LunaModel

class SomeLunaModel(LunaModel):
foo: str
bar: Decimal


class Example(Template):
param_int = Parameter(type_=int, description="some integer param")
param_str = Parameter(type_=str, description="some string param")
param_bool = Parameter(type_=bool, description="some boolean param")
param_dec = Parameter(type_=Decimal, description="some decimal param")
param_list = Parameter(type_=List[str], description="some list param")
param_dict = Parameter(type_=dict, description="some dictionary param")
param_set = Parameter(type_=set, description="some set param")
param_datetime = Parameter(type_=datetime, description="some datetime param")
param_date = Parameter(type_=date, description="some date param")
param_timedelta = Parameter(type_=timedelta, description="some time delta param")
param_model = Parameter(type_=SomeLunaModel, description="some LunaModel param")

@clause()
def my_clause(self, ctx: Context):
if self.param_bool:
do_something(
self.param_int +1,
[x for x in self.param_list if x.starts],
self.param_model.foo,
self.param_model.bar
)

Variables

Variables are used in a Template for storing and manipulating data. They are used when values are required to be persisted, for example across different Clauses or for record keeping purposes.

You can define Variables in any format you like (unlike Parameters, which are restricted as per above), and use them for any logic or manipulation you want.

Some examples of Variables being declared:

from luna import Template, Variable

class Example(Template):
my_var1 = Variable(type_=int, value=1)
my_var2 = Variable(type_=str, value="this is a variable")
my_var3 = Variable(type_=bool, value=True)
my_var4 = Variable(type_=float, value=3.14)
param_model = Variable(type_=List[SomeLunaModel], value=[])

You must specify their initial value, but this can be None which is the overwritten later - e.g. in a Clause.

my_var5 = Variable(type_=int, value=None)

Their value can also be set from a Python function:

def create_value(x,y):
return x + y

my_var6 = Variable(type_=int, value=create_value(2,7))

You can use any Python library as part of your Variable declaration:

from decimal import Decimal
import numpy
import torch

my_var7 = Variable(type_=Decimal, value=3.14)
my_var8 = Variable(type_=numpy.array, value=numpy.array([1,2,3,4,5]))
my_var9 = Variable(type_=torch.tensor, value=torch.tensor[[[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]],
[[10, 11, 12, 13, 14],[15, 16, 17, 18, 19]],
[[20, 21, 22, 23, 24],[25, 26, 27, 28, 29]],])

Constants

A Constant is like a Variable, but it cannot change.

It can be set in the Template (so all contracts have the same value), or it can be set once (and only once) at Contract initialisation.

from luna import Template, Constant, Context, clause

class Example(Template):
const = Constant(type_=int)
given_const = Constant(type_=str, value="foo")

@clause(on_init=True)
def set_constant(self):
self.const = 14

@clause()
def my_clause(self, ctx: Context):
do_something(
self.const +1,
self.given_const
)

Linked Templates

What are Linked Templates?

Linked Templates in Luna facilitate the creation of interdependent contracts. You create these links at the contract definition stage during the template creation.

*Example: Master Service Agreement Consider a Master Service Agreement (MSA) as the primary template. You could link this MSA to various other contract templates, such as specific service agreements, partnership contracts, or supply agreements. As children of the MSA template, each subsidiary contract inherits key terms and conditions from the MSA. Any updates or changes to the MSA template automatically update all linked subsidiary contracts, ensuring uniformity and compliance across all related agreements.

Why are they useful?

You define the links between templates using the Luna contract language, allowing for flexible relationships between templates. This flexibility proves useful for complex contractual relationships where data sharing is integral.

In the example below, we show two Templates which are linked and allow information sharing between them. Specifically, the child template (TemplateB) can

Linked Templates enhance the contract creation process for agreements that have complex abstractions, or where composability is important. They promote consistency across these contracts and automate the generation of related contracts, thereby reducing manual effort and the risk of errors.

See also the Linked Contracts section in the Contracts page


class TemplateA(Template):
var = Variable(type_=int, value=1)
children = Variable(type_=list, value=[])

@clause()
def my_children(self):
self.children = self.child_contracts.all()

class TemplateB(Template):
template_a = Link(template=TemplateA)
var = Variable(type_=int, value=1)
parents = Variable(type_=list, value=[])

@clause()
def my_parents(self):
self.parents = self.parent_contracts.all()

@clause()
def myclause(self):
self.var += self.template_a.contract.var

Secrets

 Secrets Management

Secrets can be managed directly via the API or via the settings tab in the UI.

Contract Integration

Once a secret exists in your organization you can reference its value directly in your templates. The value will be securely injected at runtime.

import requests

class TemplateSecret(Template):
API_KEY = Secret(name="My API Key") # this is the name of the secret shown via the API, or management console

@clause(occurrences=1)
def use_secret(self):
response = requests.get("https://my.api.com", headers={"Authorization": self.API_KEY})

Security

Secrets are securely stored in AWS Secrets Manager and can only be retrieved by authorized contracts.