Debt Algorand Standard Application
version = "0.21.0"

A standard for debt instruments tokenization on Algorand
The Debt Algorand Standard Application (D-ASA) is a standard for tokenizing debt instruments on the Algorand Virtual Machine.
It provides a framework for arranging the contract, configuring its role-based access control, issuing and distributing it on the primary market, executing cash flows, exchanging it on the secondary market, and querying information about the debt instrument.
The specification complies with the Algorithmic Contract Types Unified Standards (ACTUS) for the definition of the contracts.
D-ASA is, in essence, a full tokenization framework for ACTUS-compliant debt instruments, issued and executed on the Algorand Virtual Machine.
The specification allows the tokenization of various debt instruments, such as bonds, loans, commercial papers, mortgages, etc.
The reference implementation of some fixed income contracts is provided.
This document is a technical specification, it is not intended to be a legal or a financial document.
Contents
Contents are organized in three hierarchical levels (see the navigation sidebar on the left):
Part
└── 1. Chapter
└── 1.1. Section
└── 1.1.1. Sub-section
The navigation sidebar can be folded up to the Chapter level by clicking the folding icon (>), next to the level name.
Contributing
The D-ASA is free and open source.
The source code is released on the official GitHub repository.
External contributions are welcome, the project relies on the community to improve and expand.
Issues and feature requests can be submitted on the GitHub issues page.
If you would like to contribute, please read the guidelines and consider submitting a pull request.
License
The D-ASA source and documentation are released under the AGPL-3.0 license.
Motivation
Debt instruments represent one of the biggest asset classes (along with equities, commodities, and real estate). Debt instruments are investment contracts between borrowers and lenders, used to raise capital with binding obligations between the parties, who agree on the payoff and the cash flows (payments schedule, interest rates, maturity, etc.).
The definition of a comprehensive specification for the tokenization of debt instruments benefits several players of the traditional value chain, such as issuers, arrangers, asset managers, risk managers, lenders, payment agents, transfer agents, etc.
The Debt Algorand Standard Application turns a traditional debt instrument into a deterministic ACTUS financial contract, executed on the Algorand Virtual Machine (AVM).
The machine-readable and executable contract removes existing frictions over the debt instruments lifecycle and reconciliation, enabling use cases such as truly atomic delivery-vs-payment (with instant finality and no counterparty risk), deterministic cash flows analysis (when, how much, to whom), and easier quantitative risk management.
Definitions
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
The data types (like uint64, byte[], etc.) in this document are to be interpreted
as specified in ARC-4.
Acronyms in square brackets like \( [THIS] \) are to be interpreted as defined in the ACTUS dictionary.
Notes like this are non-normative
Non-normative sections mainly contextualize the specification for technical readers unfamiliar with the financial concepts described.
Tip
Sections like this are examples aiming to clarify the specifications.
Sections like this are either pseudo-code or formal examples.
Math Symbols
For a correct rendering of mathematical symbols and formulas, it is recommended
to right-click on the symbol below, and select Math Settings -> Math Renderer -> Common HTML from the drop-down menu.
$$ \mathcal{C} $$
Once MathJax rendering is correctly set, you should see a calligraphic “C”.
Overview
A Debt Algorand Standard Application (D-ASA) is an Algorand application that executes a fixed-income ACTUS contract on the AVM.
A conforming D-ASA MUST process a debt instrument through the following stages:
-
Define the debt instrument as an ACTUS contract.
-
Normalize the ACTUS contract into AVM-compatible integers, state, and schedule pages.
-
Execute the normalized contract on the AVM through explicit ABI methods.
The D-ASA therefore uses the AVM as the execution layer of ACTUS. The canonical contract interface is the normalized ACTUS interface defined in this specification.
This specification is structured in four layers:
-
RBAC: identifies operational actors and their authorities.
-
ACTUS Kernel: defines the normalized contract terms, schedule, and lifecycle state machine.
-
Accounting: defines holder positions, unit balances, checkpoints, and claims.
-
Execution: executes the ACTUS cashflows and tokenized contract transfers.
-
Payment Agent: defines funding and withdrawal of due ACTUS cashflows.
-
Transfer Agent: defines primary distribution and secondary transfer execution.
-
flowchart TD RBAC["Layer 1: RBAC<br/>Operational actors & authorities"] KERNEL["Layer 2: ACTUS Kernel<br/>Contract terms, schedule & state machine"] ACCOUNTING["Layer 3: Accounting<br/>Positions, balances, checkpoints & claims"] PAYMENT["Layer 4a: Payment Agent<br/>Funding & withdrawal of cashflows"] TRANSFER["Layer 4b: Transfer Agent<br/>Primary distribution & secondary transfers"] RBAC --> KERNEL KERNEL --> ACCOUNTING ACCOUNTING --> PAYMENT ACCOUNTING --> TRANSFER style RBAC fill:#bbdefb,stroke:#1976d2,stroke-width:2px,color:#000 style KERNEL fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000 style ACCOUNTING fill:#c8e6c9,stroke:#388e3c,stroke-width:2px,color:#000 style PAYMENT fill:#ffccbc,stroke:#d84315,stroke-width:2px,color:#000 style TRANSFER fill:#ffccbc,stroke:#d84315,stroke-width:2px,color:#000
Conformance
A conforming implementation:
-
MUST implement the public ABI described in the Interfaces section;
-
MUST accept normalized ACTUS terms, an initial kernel state, and a paged execution schedule;
-
MUST execute ACTUS non-cash and cash events through the kernel and agent interfaces;
-
MUST follow the ACTUS compliance profile defined in the Contract section.
ACTUS compliance
D-ASA is designed to be ACTUS-compliant, with three minor deviations required by AVM constraints:
| ACTUS | D-ASA | |
|---|---|---|
| Time format | ISO 8601 | UNIX |
| Time precision | Millisecond \( 10^{-3} [s] \) | Second \( [s] \) |
| Arithmetic | Floating-point | Fixed-point |
The ACTUS compliance has not yet been certified by an official ACTUS standardization body.
Trust Model
The trust model defines who is allowed to configure, operate, suspend, and observe the D-ASA lifecycle.
The canonical role set and the RBAC enforcement rules are specified in the following pages.
Roles
D-ASA defines custom roles and permissions for the entities involved in the debt instrument.
The D-ASA MUST define contract roles \( [CNTRL] \).
The D-ASA MUST identify operational authorities with reserved role identifiers.
The contract roles MUST be associated with Algorand Addresses through a role key of the form:
[R:||<role key>||#||<role address>]
Where || denotes concatenation.
The current reference implementation uses the following role set:
| Key | ID | Role | Scope |
|---|---|---|---|
ARR | 20 | Arranger | Owns contract creation, configuration, schedule upload, and upgrade authority |
OPD | 25 | Op Daemon | Optional automation address for payment execution workflows |
MNG | 40 | Account Manager | Opens holder accounts |
PYD | 50 | Primary Dealer | Allocates units during primary distribution |
TRS | 60 | Trustee | Sets or clears the contract default-performance flag |
AUT | 70 | Authority | Suspends the contract or individual accounts |
MOC | 80 | Observer | Applies rate-reset events that depend on observed data |
Tip
The Arranger with Algorand Address
XYZis identified asR:ARR#XYZ.
Role model
The Arranger and Op Daemon are single-address global roles.
The Account Manager, Primary Dealer, Trustee, Authority, and Observer roles are time-bounded assignments. A role assignment is active only if:
role_validity_start <= current_block_timestamp <= role_validity_end
Every stored assignment window MUST be strictly ordered:
role_validity_start < role_validity_end
The D-ASA MUST reject actions that require an inactive role assignment.
Time-bounded role addresses MUST NOT be the global zero address.
Responsibilities
Issuer (Borrower)
Issuers are individuals, companies, institutions, governments, or other entities who borrow capital by issuing a debt.
The Issuer \( [RPL] \) is a meta-role in the D-ASA role model, they have no specific permissions.
Arranger
Arrangers are legal entities authorized to arrange debt instruments on behalf of the issuers.
The Arranger owns an Algorand Address.
That address MUST NOT be the Algorand global zero address.
The Arranger MUST be able to:
- create the contract;
- upload normalized ACTUS terms and schedule pages;
- execute
IED; - append or apply arranger-controlled observed events;
- rotate the arranger address;
- assign and revoke time-bounded roles;
- update the application.
Op Daemon
The Op Daemon is an optional execution helper. If configured, the Op Daemon MAY trigger due cashflow funding together with the Arranger and trigger holder cashflow claims in addition to the holder or payment address.
Account Manager
The Account Manager owns an Algorand Address.
The Account Manager MUST be able to open holder accounts. The role does not control payments or transfers.
The right to open lender accounts can be granted to different entities, such as KYC providers or banks.
Primary Dealer
The Primary Dealer MUST be able to reserve units during primary distribution
before IED.
Trustee
The Trustee role MUST control the contract performance \( [PRF] \) (see Performance section for further details).
In the current reference implementation, an active Trustee can set or clear the
contract-level defaulted performance flag with rbac_contract_default.
This performance flag is distinct from the kernel lifecycle status.
Authority
The Authority owns an Algorand Address.
The Authority MUST be able to suspend the contract and individual holder accounts.
Observer
Debt instruments may rely on external data, such as interest rates, covenant breaches, etc., provided by trusted oracles.
The Observer \( [MOC] \) owns an Algorand Address.
The Observer MUST be able to apply due rate-reset events (RR, RRF) whose
execution depends on observed external values.
Role-Based Access Control
The D-ASA role model MUST gate every privileged ABI method.
Role administration
The Arranger MUST control role administration through:
rbac_assign_rolerbac_revoke_rolerbac_get_role_validityrbac_get_address_roles
Role assignments MUST be validated against their stored time window at execution time. A role that exists but is outside its validity interval MUST be treated as inactive.
When assigning a time-bounded role, the target address MUST NOT be the global
zero address and the supplied validity interval MUST satisfy
role_validity_start < role_validity_end.
Arrangement
The Arranger MUST retain the authority to:
- rotate the arranger address with
rbac_rotate_arranger; - set the optional operation daemon with
rbac_set_op_daemon; - update the application with
contract_update.
The arranger slot MUST NOT be set to the Algorand global zero address during contract creation or later rotation, because doing so would lock out arranger-only operations.
Suspension
Debt instruments are regulated under different legal frameworks and their jurisdictions.
The D-ASA ensures efficient execution of the debt instrument in the “best case scenarios”, where it offers the highest improvements in cost and time efficiency if compared to the traditional, manual, and labor-intensive contracts.
The D-ASA provides methods to comply with regulatory obligations, allowing the management of the “worst case scenarios”, in which the intervention of the authority or the regulator is necessary.
Debt instruments can be temporarily suspended due to regulations or operational reasons.
The D-ASA suspension authority MUST be restricted to specific contract roles.
Contract suspension
rbac_contract_suspension MUST suspend or resume contract-wide operations.
When the contract is suspended, the implementation MUST reject:
- primary distribution;
- funding and claiming of due cashflows;
- holder payment-address updates;
- transfers;
- any other method explicitly guarded by the suspension flag.
Account suspension
account_suspension MUST suspend or resume a specific holder account.
When an account is suspended, the implementation MUST reject:
- unit allocations to that account during primary distribution;
- transfers from or to that account;
- on-chain cashflow execution to that account while the suspension remains in force.
Trustee-controlled default
rbac_contract_default MUST be restricted to the Trustee role.
In the current reference implementation, this method sets or clears the contract-level
defaulted performance flag in RBAC global state. It does not change the kernel
lifecycle status.
Observer-controlled events
The Observer role MUST authorize due RR and RRF event application through
apply_non_cash_event.
All other non-cash events remain arranger-controlled unless a future profile explicitly states otherwise.
Contract
The contract section defines the ACTUS compliance profile, the normalized kernel state, the schedule model, and the AVM configuration flow.
The canonical execution chain is:
ACTUS contract -> AVM normalization -> AVM execution
Contract
Financial contracts are legal agreements between two (or more) counterparties on the exchange of future cash flows. Debt instruments are a subset of financial contracts.
This section specifies the D-ASA ACTUS contract layer.
A conforming D-ASA MUST express the debt instrument as:
-
ACTUS contract attributes;
-
Normalized ACTUS terms, initial kernel state, and execution schedule for the AVM;
-
Explicit AVM execution of due ACTUS events.
The canonical execution chain is, therefore:
flowchart LR ACTUS["ACTUS Contract"] NORMALIZE["AVM Normalization"] EXEC["AVM Execution"] ACTUS --> NORMALIZE NORMALIZE --> EXEC style ACTUS fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000 style NORMALIZE fill:#c8e6c9,stroke:#388e3c,stroke-width:2px,color:#000 style EXEC fill:#ffccbc,stroke:#d84315,stroke-width:2px,color:#000
The following pages define:
- The supported ACTUS compliance profile;
- The normalized on-chain state and schedule model;
- The contract normalization and configuration flow;
- The numeric representation rules required to move ACTUS values onto the AVM.
ACTUS Compliance Profile
Debt instruments such as bullet bonds, amortizing loans, mortgages, etc. differ based on their cash flow exchange patterns (e.g., principal and interest payment time schedules, fixed or variable interest rates, etc.).
The ACTUS taxonomy reduces the majority of all financial contracts to a defined set of 32 generalized cash flow exchange patterns, called contract types.
The D-ASA MUST be classified with an ACTUS contract type \( [CT] \) (see the ACTUS taxonomy).
The contract type MUST have the following properties:
family: Basicclass: Fixed Incomesub-class: Maturities
Contract Identifier
The D-ASA contract identifier \( [CID] \) is the Algorand Application ID (uint64).
The D-ASA contract layer MUST follow a constrained ACTUS fixed-income profile that can be normalized and executed on the AVM.
Supported contract families
The current kernel supports the following ACTUS contract family identifiers:
| ID | Contract Type | Description | Rate | Use case |
|---|---|---|---|---|
0 | PAM | Principal payment fully at \( [IED] \) and repaid at \( [MD] \). | Fix or variable rates | All kind of bonds, term deposits, bullet loans, mortgages, etc. |
1 | ANN | Principal payment fully at \( [IED] \) and repaid periodically in constants amounts till \( [MD] \). | Fix or variable rates | Classical level payment mortgages, leasing contracts, etc. |
2 | NAM | As ANN, when resetting rate total amount (principal + interest) stays constant. \( [MD] \) shifts. | Variable only | Adjustable rate mortgages |
3 | LAM | Principal payment fully at \( [IED] \) and repaid periodically in constants amounts till \( [MD] \), interest reduced accordingly. | Fix or variable | Amortizing loans |
4 | LAX | Flexible version of LAM. | Fix or variable | Teaser rate loans |
5 | CLM | Loans rolled over as long as they are not called. Once called, it has to be paid back after noticed period. | Fix or variable | Loans with call options |
Non-normative subtypes such as
PAM:ZCBandPAM:FCBare resolved before normalization. The AVM kernel stores only the normalized family identifier.
Supported event types
The normalized execution schedule MUST contain only event types permitted for the configured contract family.
| Family | Allowed events |
|---|---|
PAM | IED, IP, MD, RR, RRF |
ANN | IED, IP, PR, MD, RR, RRF, IPCB, PRF |
NAM | IED, IP, PR, MD, RR, RRF, IPCB |
LAM | IED, IP, PR, MD, RR, RRF, IPCB |
LAX | IED, IP, PR, PI, MD, RR, RRF, IPCB, PRF |
CLM | IED, IP, PR, MD, RR, RRF |
-
Cash events are limited to
IP,PR, andMD. -
Non-cash events are limited to
IED,PI,RR,RRF,IPCB, andPRF.
Normalization constraints
The D-ASA profile further requires:
business_day_convention = NOS;calendar = NC;- One of the supported day-count conventions listed in Day-Count Convention;
- AVM-compatible
uint64values for amounts, times, and fixed-point factors.
Normalization MUST reject any contract that cannot satisfy those constraints.
Observed events
Observed schedule extension is intentionally narrow:
-
Only
CLMcontracts may append observed events at runtime; -
append_observed_cash_eventonly accepts observedPRcash events; -
apply_non_cash_eventapplies dueRRandRRFevents under Observer control; -
Arranger-controlled observed appends MUST preserve event ordering and contiguous event IDs.
Denomination
Debt instruments are denominated in a currency, in which principal and interests are calculated.
The D-ASA MUST be denominated either in an on-chain or off-chain denomination asset \([CUR]\)1.
All values (uint64) are integer minor units of the relevant on-chain or off-chain
denomination asset.
The reference implementation supports only on-chain ASA denominations (e.g., “stablecoins”).
On-chain denomination
The denomination asset MUST be an Algorand Standard Asset (ASA), an Application asset (App), or the ALGO.
The denomination asset identifier MUST be the ASA ID, the App ID, or 0 for
ALGO.
If asset is ALGO (0): amount is in microALGOs (\( 10^{-6} \) ALGO).
If asset is ASA or App: amount is in base units as per that asset’s decimals.
Tip
The value (
uint64)10000of an ASA denomination with 2decimalsis interpreted as100.00units of the ASA.
Off-chain denomination
The denomination asset identifier MUST be the ISO 4217 currency numeric code.
The denomination asset MUST use the decimal digits specified by the ISO 4217.
If asset is ISO 4217 numeric code: amount is in minor units (\( 10^{-d} \) with \( d \) as per ISO 4217 digits).
Tip
The value (
uint64)10000an EUR (ISO 4217, 2 decimals) denomination is interpreted as100.00Euro.
-
ACTUS only allows ISO 4217 currency identifiers, therefore an on-chain denomination is not supported by ACTUS. ↩
Day-Count Convention
Debt instruments use a day-count convention to calculate the amount of accrued interest when the next interest payment is less than a full interest period away.
The D-ASA MUST specify one day-count convention \( [IPCD] \).
The day-count convention MUST be identified with one of the following enumerated
IDs (uint8):
| ID | Name | ACTUS | Description |
|---|---|---|---|
0 | Actual/Actual | \([AA]\) | Year fractions accrue on the basis of the actual number of days per month and per year in the respective period |
1 | Actual/360 | \([A360]\) | Year fractions accrue on the basis of the actual number of days per month and 360 days per year in the respective period |
2 | Actual/365 | \([A365]\) | Year fractions accrue on the basis of the actual number of days per month and 365 days per year in the respective period |
3 | 30/360 ISDA | \([30E360ISDA]\) | Year fractions accrue on the basis of 30 days per month and 360 days per year in the respective period (ISDA method) |
4 | 30/360 | \([30E360]\) | Year fractions accrue on the basis of 30 days per month and 360 days per year in the respective period |
5 | 28/366 | \([28E366]\) | Year fractions accrue on the basis of 28 days per month and 366 days per year in the respective period |
Calendar
Calendars define the non-working days which may affect the dates of traditional debt instruments.
The AVM (so the D-ASA) time has no notion of calendars. Conversion of serial UNIX timestamps into a year/month/day triple1 (and vice versa) can be performed by external Algorand Applications2 or client side (normalization).
The D-ASA MAY specify a calendar \( [CLDR] \).
The calendar MUST be identified with one of the following enumerated IDs:
| ID | Name | ACTUS | Description |
|---|---|---|---|
0 | No Calendar | \([NC]\) | No holidays defined (default if not specified) |
1 | Monday to Friday | \([MF]\) | Saturdays and Sundays are holidays |
Business Day Convention
Debt instruments cash flows execution may be stopped on non-working days (according to a calendar).
The business day convention defines how D-ASA execution can be shifted to the next business day (following) or the previous on (preceding).
The D-ASA MAY specify a business day convention \( [BDC] \).
| ID | Name | ACTUS | Description |
|---|---|---|---|
0 | No Shift | \([NOS]\) | No shift applied to non-business days |
1 | Shift-Calculate Following | \([SCF]\) | Shift event dates first then calculate accruals etc. Strictly shift to the next following business day |
2 | Shift-Calculate Modified-Following | \([SCMF]\) | Shift event dates first then calculate accruals etc. Shift to the next following business day if this falls in the same month. Shift to the most recent preceding business day otherwise |
3 | Calculate-Shift Following | \([CSF]\) | Calculate accruals etc. first then shift event dates. Strictly shift to the next following business day |
4 | Calculate-Shift Modified-Following | \([CSMF]\) | Calculate accruals etc. first then shift event dates. Shift to the next following business day if this falls in the same month. Shift to the most recent preceding business day otherwise |
5 | Shift-Calculate Preceding | \([SCP]\) | Shift event dates first then calculate accruals etc. Strictly shift to the most recent preceding business day |
6 | Shift-Calculate Modified-Preceding | \([SCMP]\) | Shift event dates first then calculate accruals etc. Shift to the most recent preceding business day if this falls in the same month. Shift to the next following business day otherwise |
7 | Calculate-Shift Preceding | \([CSP]\) | Calculate accruals etc. first then shift event dates. Strictly shift to the most recent preceding business day |
8 | Calculate-Shift Modified-Preceding | \([CSMP]\) | Calculate accruals etc. first then shift event dates. Shift to the most recent preceding business day if this falls in the same month. Shift to the next following business day otherwise |
End of Month Convention
Debt instruments may define due dates as the last day of the month.
The end-of-month convention defines how D-ASA execution can be shifted according to the different number of days in months (31, 30, and 28) according to the calendar.
The D-ASA MAY specify an end-of-month convention \( [EOMC] \).
| ID | Name | ACTUS Acronym | Description |
|---|---|---|---|
0 | Same Day | \( [SD] \) | Schedule times always fall on the schedule anchor date day of the month |
1 | End of Month | \( [EOM] \) | Schedule times fall on the end of every month if the anchor date represents the last day of the respective month |
Normalization profile restrictions
The D-ASA kernel MUST store one normalized day-count convention identifier.
The current kernel accepts the following identifiers:
| ID | Name | ACTUS |
|---|---|---|
0 | Actual/Actual | AA |
1 | Actual/360 | A360 |
2 | Actual/365 | A365 |
3 | 30E/360 ISDA | 30E360ISDA |
4 | 30E/360 | 30E360 |
A contract configuration MUST fail if the normalized terms use any other day-count identifier.
The current D-ASA ACTUS profile imposes the following additional constraints on date handling:
business_day_conventionMUST beNOS;calendarMUST beNC;- Timestamps MUST be expressed as UTC UNIX seconds.
Those constraints are enforced during normalization so that the AVM only receives values that can be executed deterministically without external calendar logic.
Accrual factors
The AVM kernel does not recompute year fractions from raw dates. Instead, normalization
MUST precompute the relevant accrual factors and place them in each ExecutionScheduleEntry.
This split is intentional:
- Date arithmetic and ACTUS schedule generation happen off chain;
- Execution, validation, and state transitions happen on chain.
-
The paper “chrono-Compatible Low-Level Date Algorithms” (ref), by Howard Hinnant, provides a list of algorithms for the conversion of serial UNIX time into a proleptic Gregorian calendar (and vice versa). ↩
-
An example of Algorand Application implementing the conversion of serial UNIX time into a proleptic Gregorian calendar year/month/day triple. ↩
Main Contract Attributes
The following are the main ACTUS Contract Attributes.
This section is intended to clarify ACTUS-specific terminology and map it to the corresponding terms used in the D-ASA implementation. ACTUS remains the normative specification throughout.
Important
In the event of any inconsistency, the ACTUS specification shall prevail.
For further details, refer to the ACTUS specification.
Principal
Debt instruments principal is the amount of capital borrowed and used as a base for calculating interest.
The D-ASA MUST define the principal \( [NT] \), expressed in the denomination asset.
The D-ASA MUST define a minimum denomination, expressed in the denomination asset.
The minimum denomination MUST be a divisor of the principal.
Premium and Discount
Debt instruments principal may be placed at premium or discount on issuance.
The D-ASA MAY define a premium or discount \( [PDIED] \) to apply to the principal on the issuance.
Tip
Let’s have a D-ASA denominated in EUR, with a principal of 1M EUR paid at maturity and a minimum denomination of 1,000 EUR. The D-ASA has a principal discount of 200 bps (2%) at the issuance. Each D-ASA unit is sold on the primary market at 980 EUR and will be redeemed for 1,000 EUR of principal at maturity.
Interests
Debt instruments interest is calculated on a fixed or variable rate on the outstanding principal.
The interest rate is the nominal yield paid by the debt instrument on the principal, usually defined per-annum (APY).
Interest Rate
Debt instruments may have variable interest rates, based on external data oracles.
The D-ASA MAY define a nominal interest rate \( [IPNR] \).
If the interest rate is variable, the D-ASA MUST define interest update dates known \( [RRF] \) or unknown \( [RR] \).
Cap and Floor
Debt instruments may define limitations to the interest rate variability, either over the whole contract lifespan or over specific periods.
Life Caps
The D-ASA MAY define a life cap \( [RRLC] \) to apply to the variable interest rate.
The D-ASA MAY define a life floor \( [RRLF] \) to apply to the variable interest rate.
Period Caps
The D-ASA MAY define a period cap \( [RRPC] \) to the variable interest rate.
The D-ASA MAY define a period floor \( [RRPF] \) to apply to the variable interest rate.
Fixing Period
Debt instruments usually schedule interest rate updates before the new rate applies (defined by the rate reset schedule).
The D-ASA MUST define a fixing period \( [RRFIX] \) that specifies a period of time before the interest payment \( [IP] \) in which the interest can be updated.
Issuance
Debt instruments start accruing interest on the issuance date.
The D-ASA MUST have an initial exchange date \( [IED] \) (issuance).
Maturity
Debt instruments may have a maturity date, on which the principal is repaid and the contract obligations expire.
Debt instruments may have a fixed or variable maturity date.
The D-ASA MAY have a maturity date \( [MD] \).
The maturity date MAY be updated in case of pre-payment options.
Prepayment Options
Debt instruments could have early repayment options to repay the principal to lenders (partially or totally) before maturity or to reduce the maturity date.
Debt instrument with defined maturity date may terminate earlier if the full principal redemption happens earlier than maturity.
The D-ASA MAY define prepayment options.
Prepayment Effects
Debt instruments could have early repayment options to repay the principal to lenders (partially or totally) before maturity or to reduce the maturity date.
Debt instrument with a defined maturity date may terminate earlier if the full principal redemption happens earlier than maturity.
An early repayment option could have different prepayment effects \( [PPEF] \):
-
It MAY repay the principal partially or totally before the maturity date;
-
It MAY reduce the maturity date.
The prepayment effect MUST be identified with one of the following enumerated IDs:
| ID | Name | ACTUS Acronym | Description |
|---|---|---|---|
0 | No Prepayment | \([N]\) | Prepayment is not allowed under the agreement |
1 | Prepayment Reduces Redemption Amount | \([A]\) | Prepayment is allowed and reduces the redemption amount for the remaining period up to maturity |
2 | Prepayment Reduces Maturity | \([M]\) | Prepayment is allowed and reduces the maturity |
Penalties
Debt instruments may have a penalty as a consequence of an early repayment option.
The D-ASA MAY define a penalty type \( [PYTP] \) for the early repayment options.
The penalty type MUST be identified with one of the following enumerated IDs:
| ID | Name | ACTUS Acronym | Description |
|---|---|---|---|
0 | No Penalty | \([N]\) | No penalty applies |
1 | Fixed Penalty | \([A]\) | A fixed amount applies as penalty |
2 | Relative Penalty | \([R]\) | A penalty relative to the notional outstanding applies |
3 | Interest Rate Differential | \([I]\) | A penalty based on the current interest rate differential relative to the notional outstanding applies |
Performance
Debt instruments performances are exposed to credit risks.
The D-ASA performance \( [PRF] \) MUST be identified with one of the following enumerated IDs:
| ID | Name | ACTUS Acronym | Description |
|---|---|---|---|
0 | Performant | \( [PF] \) | Contract is performing according to terms and conditions |
1 | Delayed | \( [DL] \) | Contractual payment obligations are delayed according to the grace period |
2 | Delinquent | \( [DQ] \) | Contractual payment obligations are delinquent according to the delinquency period |
3 | Default | \( [DF] \) | Contract defaulted on payment obligations according to delinquency period |
4 | Matured | \( [MA] \) | Contract matured |
5 | Terminated | \( [TE] \) | Contract has been terminated |
Note
The current reference implementation supports manual default-performance tracking through an RBAC-managed boolean
defaultedflag. It does not yet model the fullPRFlifecycle enum or automatic grace-period and delinquency transitions on chain.
Grace Period
Debt instruments may define a grace period as a time window after the payment due date during which payment may be retried without a penalty.
The D-ASA MAY define a grace period \( [GRP] \).
Delinquency Period
Debt instruments may define a delinquency period as a time window after the grace period. If payment happens after the delinquency period, then the counterparty is in technical default.
The D-ASA MAY define a delinquency period \( [DQP] \).
Default
Default is the ultimate failure to pay the lenders according to the payment obligations. When this happens, the creditors have the right to declare default to the debtors.
Default processes require the intervention of regulatory bodies and courts, therefore the D-ASA default status bridges the default process off-chain.
The D-ASA SHOULD enter default status if it cannot perform payments on due dates.
The D-ASA MAY disable all non-administrative methods on default status.
The D-ASA default can be called either automatically (based on program conditions) or manually (based on the decision of a trustee).
The Trustee MAY set the default status with the rbac_contract_default
method.
In the current reference implementation, rbac_contract_default(defaulted: bool)
stores a boolean defaulted flag in RBAC global state. This performance flag is
distinct from the kernel lifecycle status.
Tip
The D-ASA has no grace period and no delinquency period. A D-ASA interest payment is triggered on due date, but there is not enough liquidity to pay all the lenders. The D-ASA contract automatically enters in default immediately.
Tip
The D-ASA has a grace period and a delinquency period. A D-ASA interest payment is triggered on due date, but there is not enough liquidity to pay all the lenders. The D-ASA program starts counting the grace period and delinquency period. If the delinquency period expires, then the contract enters in default.
Tip
A D-ASA interest payment is triggered on due date, but there is not enough liquidity to pay all the lenders. The D-ASA contract relies on a Trustee to call the default.
Kernel State and Schedule
The ACTUS kernel stores the executable contract as three normalized payload classes:
NormalizedActusTermsInitialKernelStateExecutionScheduleEntry[]
Normalized Terms
NormalizedActusTerms MUST contain the immutable or quasi-immutable contract
configuration required by the kernel, including:
- Contract Type ID;
- Denomination asset ID and Settlement asset ID;
- Total units;
- Notional Principal;
- Initial Exchange Amount and Date;
- Maturity Date;
- Day-Count Convention ID;
- Rate Reset parameters;
- Dynamic-principal-redemption flags;
- Fixed-point scale.
Initial kernel state
InitialKernelState MUST capture the pre-IED kernel state snapshot uploaded
with contract_config. It defines:
- Status Date \( [SD] \);
- Starting event cursor;
- Outstanding principal;
- Interest calculation base;
- Current nominal rate;
- Accrued interest;
- Next principal redemption;
- Cumulative interest and principal indices.
Execution schedule
The normalized ACTUS execution schedule is an array of ExecutionScheduleEntrys.
Execution schedule entry
Each ExecutionScheduleEntry MUST contain:
- A contiguous
event_id; - An ACTUS
event_type; scheduled_time;- Precomputed accrual factors;
- The next normalized rate and principal state;
- Entry flags.
Entry flags are stored as a bitfield integer and MUST comply with the following bit positions:
| Flag | Bit Position | Decimal Value | Meaning |
|---|---|---|---|
CASH_EVENT | 1 << 0 | 1 | Event produces cash flows (requires contract funds) |
NON_CASH_EVENT | 1 << 1 | 2 | Event updates state without cash flows |
OBSERVED_EVENT | 1 << 2 | 4 | Event requires external observation (e.g., rate reset) |
INITIAL_PRF | 1 << 3 | 8 | Event is the initial performance flag update |
The schedule MUST satisfy the following invariants:
Schedule invariants
event_id = 0MUST beIED;- Event IDs MUST be contiguous across all pages;
- Schedule entries MUST be ordered by nondecreasing
scheduled_time; - Page size MUST NOT exceed
16entries in the current kernel; - The last uploaded page MUST finalize
schedule_entry_countand move the contract toSTATUS_PENDING_IED.
A normalized ACTUS schedule page MUST be identified with a key of the form:
[S#||<0-based page index>]
Where || denotes concatenation.
ACTUS cycles
ACTUS cycles are an off-chain schedule-generation concept. They define recurring periods before normalization resolves them into concrete timestamps.
The D-ASA SDK models ACTUS cycles with the syntax:
<count><unit>[+|-]
where:
countis a positive integer;unitis one ofD,W,M,Q,H,Y;+and-are optional ACTUS stub markers.
Examples:
90D3M1Q1H2Y3M+1Q-
Cycles are used together with ACTUS anchors in ContractAttributes, such as:
interest_payment_anchor+interest_payment_cycleprincipal_redemption_anchor+principal_redemption_cyclerate_reset_anchor+rate_reset_cycle
The normalization process MUST resolve those cycles into explicit schedule entries before the contract is uploaded to the AVM.
As a result, the on-chain kernel does not store raw ACTUS cycles. It stores only the normalized timestamps and state transitions that were derived from those cycles.
Contract status machine
The kernel uses the following status identifiers:
| Status | ID | Meaning |
|---|---|---|
INACTIVE | 0 | Terms and schedule not fully configured |
PENDING_IED | 50 | Schedule uploaded; issuance not yet activated |
ACTIVE | 100 | IED executed; contract lifecycle is live |
ENDED | 200 | Terminal state reached |
stateDiagram-v2 [*] --> INACTIVE: Contract create INACTIVE --> PENDING_IED: Upload schedule PENDING_IED --> ACTIVE: Execute IED ACTIVE --> ACTIVE: Execute events ACTIVE --> ENDED: Terminal state ENDED --> [*] state "INACTIVE (0)<br/>Terms and schedule<br/>not fully configured" as INACTIVE state "PENDING_IED (50)<br/>Schedule uploaded<br/>Issuance not activated" as PENDING_IED state "ACTIVE (100)<br/>IED executed<br/>Contract lifecycle live" as ACTIVE state "ENDED (200)<br/>Terminal state reached" as ENDED
Performance default flag
Kernel lifecycle status and contract performance are distinct concepts.
Outside the kernel normalized state, the reference implementation stores a boolean
RBAC-managed global-state flag, defaulted, to record manual contract default
performance.
An active Trustee MAY update this flag by calling rbac_contract_default
with a boolean defaulted argument.
defaulted is not part of InitialKernelState and is not returned by contract_get_state.
Clients that need it MUST read the dedicated global-state key.
Due-event execution
The kernel advances the schedule through explicit ABI calls:
contract_execute_iedapplies the first dueIED;apply_non_cash_eventapplies the next due non-cash event afterIED;fund_due_cashflowsprocesses due cash events and advances the cursor;contract_get_next_due_eventreturns the next due entry, or a zero sentinel after end.
This split is the normative execution model for D-ASA.
Normalization and Configuration
The contract configuration flow MUST follow the same sequence as the reference implementation:
%%{init: {'flowchart':{'nodeSpacing': 20, 'rankSpacing': 20}}}%%
flowchart TD
A[ContractAttributes] --> B[normalize_contract_attributes]
B --> C[contract_config]
C --> D[contract_schedule]
D --> E[primary_distribution]
E --> F[contract_execute_ied]
Required steps
-
Define the debt instrument with
ContractAttributes. -
Normalize the contract off chain with
normalize_contract_attributes(). -
Upload
NormalizedActusTerms,InitialKernelState, and the prospectus withcontract_config. -
Upload the normalized schedule in contiguous pages with
contract_schedule. -
Reserve all units with
primary_distribution. -
Activate issuance by executing
contract_execute_ied.
contract_schedule MUST be called after contract_config, and contract_execute_ied
MUST be called only after the full schedule is uploaded and the full unit supply
is reserved.
Example
The deployment helpers and tests provide a code-accurate PAM fixed coupon bond example:
attrs = make_pam_fixed_coupon_bond_profile(
contract_id=1,
status_date=1_700_000_000,
initial_exchange_date=1_702_592_000,
maturity_date=1_858_112_000,
notional_principal=10_000,
nominal_interest_rate=0.02,
interest_payment_cycle=Cycle.parse_cycle("90D"),
interest_payment_anchor=1_710_368_000,
)
normalized = normalize_contract_attributes(
attrs,
denomination_asset_id=12345,
denomination_asset_decimals=2,
notional_unit_value=100,
secondary_market_opening_date=1_702_592_000,
secondary_market_closure_date=1_858_198_400,
)
pages = normalized.schedule_pages(page_size=16)
The normalized result has the following properties:
notional_principal = 1_000_000base units;total_units = 100;fixed_point_scale = 1_000_000_000;schedule = [IED] + 20 * [IP] + [MD];len(schedule) = 22;len(pages) = 2.
The upload sequence is:
client.send.contract_config(...)
client.send.contract_schedule(schedule_page_index=0, is_last_page=False, ...)
client.send.contract_schedule(schedule_page_index=1, is_last_page=True, ...)
client.send.primary_distribution(...)
client.send.contract_execute_ied()
Schedule paging
Schedule paging is part of configuration, not a transport detail. The caller MUST preserve:
- contiguous
schedule_page_indexvalues; - contiguous
event_idvalues across pages; - chronological ordering across page boundaries;
- one final page flagged with
is_last_page = true.
Secondary-market configuration
transfer_set_schedule is separate from kernel configuration. Secondary-market
dates are not part of the on-chain NormalizedActusTerms struct in the current
ABI and, if enforced, MUST be configured separately at the execution layer.
The SDK normalization metadata for secondary-market dates MUST satisfy
secondary_market_opening_date >= initial_exchange_date and
secondary_market_closure_date > secondary_market_opening_date.
Performance state
The contract-level defaulted performance flag is not part of normalization or
InitialKernelState. If used, it is updated separately at execution time through
the RBAC method rbac_contract_default.
Numeric Precision and Conversions
All values uploaded to the kernel MUST fit into AVM uint64 values.
Amounts
Display amounts are normalized into ASA base units:
base_units = int(display_amount * 10^decimals)
The conversion truncates toward zero because normalization converts the scaled decimal value to an integer directly.
Rates
Rates are normalized into fixed-point integers with:
fixed_point_rate = int(rate * 1_000_000_000)
The D-ASA fixed-point scale is:
FIXED_POINT_SCALE = 1_000_000_000
The kernel then uses wide multiplication and division to apply those rates safely on chain.
Units
The normalization process MUST derive the total unit supply from:
total_units = notional_principal / notional_unit_value
This division MUST be exact.
Initial exchange amount
The initial exchange amount is normalized as:
initial_exchange_amount = notional - discount
initial_exchange_amount = notional + premium
where:
- a positive
premium_discount_at_iedis treated as a discount; - a negative
premium_discount_at_iedis treated as a premium.
Normalization MUST fail if the result becomes negative or exceeds uint64.
Overflow constraints
Every normalization step MUST reject:
- negative amounts that would require signed integers on chain;
- values larger than
2^64 - 1; - scaled amounts or rates that overflow after multiplication by their scale.
Example
For a denomination asset with 2 decimals:
10_000display units normalize to1_000_000base units;100display units normalize to10_000base units;0.02normalizes to20_000_000fixed-point units;1_000_000 / 10_000 = 100total units.
Cumulative indices
The accounting layer uses fixed-point cumulative indices for contract-wide cashflow funding:
- Contract-wide amount -> Per-unit index delta;
- Per-unit index delta -> Claim at settlement time.
This design preserves precision at contract level while keeping holder settlement lazy and deterministic.
Metadata
Metadata can be used to inform optional properties that define the specific D-ASA.
Important
D-ASA metadata is informational only, they have no influence on the contract execution and are non-normative with respect to ACTUS specifications.
Prospectus
Debt instruments are defined by their prospectus.
The D-ASA can notarize the debt instrument prospectus in the metadata.
The D-ASA prospectus hash (byte[32]) and prospectus URL (string) MAY
be set on contract configuration.
The prospectus hash MUST be computed with SHA-512/256, as defined in NIST FIPS 180-4.
The digests are a single SHA-256 integrity metadata defined in the W3C subresource integrity specification. Details on generating those digests can be found on the MDN Web Docs (only SHA-256 is supported by this specification).
Accounting
The accounting section defines how the kernel’s contract-wide cashflow state is projected into holder positions, checkpoints, and claimable balances.
Account (Lender)
Lenders provide capital to borrowers with the expectation of a financial return, defined by debt instruments.
Lenders \( [CPID] \) own D-ASA accounts, characterized by a pair of Algorand Addresses:
- Holding Address: address that owns D-ASA units with the right to future payments;
- Payment Address: address that receives D-ASA payments.
The Payment Address MAY be different from the Holding Address.
The Account is uniquely identified by the account key of the form:
[A#||<holding address>]
Where || denotes concatenation.
D-ASA units can be in custody with a third party or temporarily deposited on an order book (Holding Address). At the same time, payments are always executed towards the lender (on the Payment Address).
Seniority
Debt instruments may have an order of repayment in the event of a sale or default of the issuer, based on lenders’ seniority. Lenders with the same seniority are treated equally.
D-ASA does not enforce lender seniority \( [SEN] \).
D-ASA units
D-ASA units represent pro-rata ownership of the normalized ACTUS contract.
Total supply
The kernel MUST store total_units as a uint64.
The off-chain normalization process MUST derive total_units exactly as:
notional_principal / notional_unit_value
The division MUST be exact. If the principal is not divisible by the chosen unit value, normalization MUST fail.
notional_unit_value is an SDK-side normalization input. It is used to derive
total_units, but it is not persisted on chain.
Unit states
A holder account stores two unit balances:
reserved_units: units allocated beforeIED;units: active units participating in funded ACTUS cashflows.
When IED executes, reserved units become activatable and are moved into units
the first time the holder position is touched.
Position accounting
Each holder position MUST track:
- The payment address;
- Active and reserved unit balances;
- Suspension state;
- The last settled event cursor;
- The last applied cumulative interest index;
- The last applied cumulative principal index;
- Claimable interest and principal amounts.
The accounting layer does not maintain a mutable on-chain nominal unit value. Transfers and claims are therefore unit-based and index-based, not value-array-based.
Lazy settlement
The accounting layer MUST settle holder positions lazily against the global cumulative indices maintained by the kernel.
On settlement:
- The delta between global and account checkpoints is computed;
- The delta is multiplied by the holder’s active units;
- The result is moved into
claimable_interestandclaimable_principal; - The checkpoints and settled cursor are advanced.
This model allows:
- Contract-wide funding of due ACTUS cash events once;
- Account-by-Account realization of the funded cashflows later.
Execution
The execution layer applies the configured contract in production through primary distribution, cashflow funding and claims, and unit transfers.
Algorand Virtual Machine (AVM)
The Algorand Virtual Machine (AVM) is a trust-less bytecode-based Turing-complete stack interpreter that executes programs on the Algorand protocol.
The D-ASA is an Algorand Application that runs on the AVM.
Performance
| Metric | Value |
|---|---|
| Block Size | 5 MB |
| Block Time | ~2.8 sec |
| TPS | ~10k txn/sec |
| Finality | instant |
| Numeric Precision | 512 bit |
For further details on the AVM architecture, refer to the AVM specification.
Time
The time on the Algorand Virtual Machine is defined by the block UNIX timestamp.
The Algorand protocol has dynamic block latency with instant finality. At the time of writing (March 2026), block finality is about 2.8 seconds.
The AVM time may present a drift with respect to external standard time references.
Transaction Ordering
Important
Transaction ordering in a block is not enforced by the Algorand protocol. In a healthy network, block proposers are selected randomly by the Algorand consensus based on a Verifiable Random Function (VRF). Therefore, the order of transactions (e.g., asset transfers or cashflow payments) in a block is random and unbiased, with no systematic advantage or precedence of a payee with respect to others.
Payment Agent
Debt instruments’ cash flows usually involve:
- Principal repayment
- Early principal repayments
- Interest payments
The Payment Agent executes ACTUS cashflows in two phases:
fund_due_cashflowsclaim_due_cashflows
Funding phase
fund_due_cashflows MUST:
- Inspect the schedule at the current global cursor;
- Process only due cash events (
IP,PR,MD); - Compute contract-wide interest and principal due from the normalized schedule;
- Reserve settlement funds in the contract;
- Convert those amounts into cumulative per-unit indices;
- Advance the global event cursor.
If an Op Daemon address is configured, fund_due_cashflows MUST only accept
calls from:
- The Op Daemon;
- The Arranger.
If no Op Daemon is configured, the current implementation applies no extra caller restriction beyond the contract checks.
Claim phase
claim_due_cashflows MUST:
- Settle the holder position to the latest cumulative indices;
- Expose the holder’s claimable interest and principal amounts;
- Execute an on-chain transfer if the payment address is executable;
- Otherwise leave the claimable amounts reserved for a later attempt.
The method MUST return an opaque payment_info context unchanged so callers
can bind off-chain metadata to the claim.
The payment information could be used, for example, for:
- Adding unique identifiers or external context to the payments;
- Enabling external payment system integration in the case of off-chain settlement;
- Providing information about the settled amount and conversion rate used with respect to the denomination asset.
Authorization model
If an Op Daemon address is configured, claim_due_cashflows MUST only accept
calls from:
- The Op Daemon;
- The Account holding address;
- The Account payment address.
If no Op Daemon is configured, the current implementation applies no extra caller restriction beyond the contract and account checks.
Settlement
Debt instruments cash flows may be settled in a currency different from the denomination.
The D-ASA MUST define either an on-chain or off-chain settlement asset \( [CURS] \)1 to regulate the cash flows.
All values (uint64) are integer minor units of the relevant on-chain or off-chain
settlement asset.
If the D-ASA defines a settlement asset different from the denomination asset, then the respective denomination/settlement conversion rate is applied at settlement time.
The denomination/settlement conversion rate can be provided by different oracles, depending on if the denomination/settlement assets are on-chain or off-chain.
If the D-ASA does not define a different settlement asset, then the cash flows MUST be settled in the denomination asset and the settlement asset identifier MUST be equal to the denomination asset identifier.
Note
The D-ASA reference implementation supports only on-chain ASA settlement assets.
Note
The D-ASA reference implementation requires settlement assets to be the denomination asset.
On-chain settlement
The settlement asset MUST be an Algorand Standard Asset (ASA), an Application asset (App), or the ALGO.
The settlement asset identifier MUST be the ASA ID, the App ID, or 0 for
ALGO.
If asset is ALGO (0): amount is in microALGOs (\( 10^{-6} \) ALGO).
If asset is ASA or App: amount is in base units as per that asset’s decimals.
Tip
The value (
uint64)10000of settlement in ASA settlement 2 decimals is interpreted as100.00units of the ASA.
On-chain settlement is possible even if the denomination asset is a traditional off-chain currency.
Off-chain settlement
The settlement asset identifier MUST be the ISO 4217 currency numeric code.
The settlement asset MUST use the decimal digits specified by the ISO 4217.
If asset is ISO 4217 numeric code: amount is in minor units (\( 10^{-d} \) with \( d \) as per ISO 4217 digits).
Tip
The value (
uint64)10000of a settlement in EUR (ISO 4217, 2 decimals) is interpreted as100.00Euro.
In the case of an off-chain settlement, the D-ASA state machine:
- Regulates payments’ approval conditions (e.g. interest is due);
- Notarizes the amounts and timestamps of payments settled off-chain.
-
ACTUS only allows ISO 4217 currency identifiers, therefore an on-chain settlement is not supported by ACTUS. ↩
Transfer Agent
Debt instruments can be transferable among investors.
D-ASA supports both on-chain and off-chain transfer agents.
The Transfer Agent executes primary and secondary movement of D-ASA units.
Secondary transfers
transfer MUST move active units only after:
- The contract is active;
- The transfer window is open, if a window was configured;
- Both counterparties are valid, distinct, and unsuspended accounts;
- Both counterparties are settled to the current global indices;
- There is no due ACTUS event pending at the global cursor.
The sender of the D-ASA transfer MUST have enough D-ASA units to transfer.
The method MUST return the number of units transferred.
Transfers are unit-based. The current kernel does not persist a mutable per-unit nominal value or per-unit coupon status.
Caller model
The D-ASA transferability policy may involve and integrate KYC/AML processes, pre-authorizations, secondary market restrictions, etc.
The current reference implementation requires the holder to call transfer directly.
The method MUST reject calls from any other address. Transfers are therefore direct holder actions, not transfer-agent proxy calls.
Primary Market
Debt instruments can be distributed on the primary market in different ways, such as book building, auctions, etc.
The primary market allocates units before the contract becomes active.
Allocation phase
primary_distribution MUST reserve units for an existing holder account while
the contract is in STATUS_PENDING_IED.
The method MUST reject allocations that:
- Occur before configuration is complete;
- Occur after
IED; - Exceed the remaining unallocated supply;
- Target a suspended or missing holder account;
- Request zero units.
Only an active Primary Dealer MUST be able to call primary_distribution.
The Primary Dealer role validity period MAY be used to precisely bound the primary distribution window.
Completion requirement
contract_execute_ied MUST fail unless:
reserved_units_total == total_units
This rule makes full issuance an explicit precondition of activation in the current reference implementation.
Activation
Primary distribution reserves units only. Reserved units do not accrue funded ACTUS
cashflows until IED executes and the holder position is activated.
Proxies
The Primary Dealer role MAY be delegated to a proxy Application implementing a primary market.
Tip
The primary market is performed as an auction on a dedicated Algorand Application. The implementation requires the
primary_distributionmethod to be called exclusively by the primary market Application, which defines the auction’s outcome.
Tip
The primary market is performed as a book building by an authorized Book-builder Address. The implementation requires the
primary_distributionmethod to be called exclusively by the authorized Book-builder.
Secondary Market
Debt instruments can be traded on secondary markets.
Secondary-market control is an execution-layer policy.
Transfer window
If transfer window enforcement is required, the Arranger MUST configure it with
transfer_set_schedule(open_date, closure_date).
The transfer window is interpreted as:
open_date <= now < closure_date
The IED MUST be defined and the method MUST reject a schedule in which
open_date >= closure_date and a schedule in which open_date < initial_exchange_date.
Default behavior
If no transfer window is configured, the current reference implementation applies no additional secondary-market date restriction beyond the generic transfer checks.
Relation to normalization
The SDK normalization helpers may carry secondary-market dates as deployment metadata.
Those dates are not part of the on-chain NormalizedActusTerms ABI struct in the
current kernel. On-chain enforcement therefore happens through transfer_set_schedule,
with contract_config additionally rejecting preconfigured transfer windows whose
opening date is before IED.
Delivery-vs-Payment (DvP)
Delivery-vs-Payment (DvP) can be used to exchange D-ASA units both on the primary market (placement) and secondary market (e.g., OTC trades).
The D-ASA framework supports the execution of composable DvP transactions.
The D-ASA DvP transactions are native Algorand Group transactions.
The Algorand Group transactions ensure atomicity, they are executed on an “all-or-nothing” basis, meaning that either all transactions in the group are executed or none of them are.
Therefore, D-ASA DvP ensures no counterparty risk, as the transfer of D-ASA units and the corresponding payment occur simultaneously. If either the transfer or the payment fails, the entire transaction group is rejected, preventing any party from being left in a vulnerable position.
The simplest DvP consists of:
- A delivery-leg, executed by the D-ASA Transfer Agent;
- A payment-leg, executed as a general Algorand transaction (usually an ASA transfer).
The payment is not restricted to specific assets (e.g., not restricted to the denomination or settlement assets).
The amounts of the DvP are negotiated between the two parties.
Events
The D-ASA uses two distinct event concepts:
-
ACTUS schedule events: contractual events stored in the normalized execution schedule.
-
AVM execution events: proofs that a scheduled event was actually applied on chain.
Those concepts are related, but they are not interchangeable.
ACTUS schedule events
ACTUS schedule events live inside ExecutionScheduleEntry records in the boxed
schedule. They describe what the contract is expected to execute next.
An ACTUS schedule event contains:
event_idevent_typescheduled_time- accrual factors
- next-state values
- flags
Schedule events exist before execution and remain part of contract state even if no AVM proof has been emitted yet.
See Kernel State and Schedule for the normative schedule definition.
AVM execution events
When the kernel applies a schedule entry, it emits a standard ARC-28
ExecutionEvent as an execution proof.
This proof is non-normative receipt data. It does not replace the kernel state, and it does not define future schedule entries. Its purpose is to attest that a given event was applied at a specific block time with a specific payoff and settlement outcome.
The current execution-proof schema is:
{
"name": "ExecutionEvent",
"desc": "Non-normative receipt for on-chain execution of a scheduled ACTUS event",
"args": [
{ "name": "contract_id", "type": "uint64", "desc": "Application ID emitting the proof" },
{ "name": "event_id", "type": "uint64", "desc": "Executed schedule event identifier" },
{ "name": "event_type", "type": "uint8", "desc": "Executed ACTUS event type" },
{ "name": "scheduled_time", "type": "uint64", "desc": "Contractual due timestamp from the schedule" },
{ "name": "applied_at", "type": "uint64", "desc": "Block timestamp at which execution occurred" },
{ "name": "payoff", "type": "uint64", "desc": "Contractual payoff computed for the event" },
{ "name": "payoff_sign", "type": "uint8", "desc": "Payoff sign identifier" },
{ "name": "settled_amount", "type": "uint64", "desc": "Amount actually reserved or transferred on chain" },
{ "name": "currency_id", "type": "uint64", "desc": "Settlement asset identifier" },
{ "name": "sequence", "type": "uint64", "desc": "Monotonic proof sequence, currently event_id + 1" }
]
}
Proof semantics
The following field distinctions are normative for interpreting the receipt:
scheduled_timeis the contractual timestamp from the normalized ACTUS schedule.applied_atis the actual block timestamp at which the kernel applied the event.payoffis the contractual amount implied by the event.settled_amountis the amount actually reserved or transferred on chain.- non-cash executions emit a proof with
settled_amount = 0.
Supported ACTUS event identifiers
The current kernel can emit proofs for the following ACTUS event types, subject to contract-family compatibility:
| Type | Acronym | Description |
|---|---|---|
1 | IED | Initial Exchange Date |
3 | PR | Principal Redemption |
4 | PI | Principal Increase |
5 | PRF | Principal Redemption Fixing |
8 | IP | Interest Payment |
11 | RRF | Rate Reset Fixing |
12 | RR | Rate Reset |
18 | IPCB | Interest Calculation Base Reset |
19 | MD | Maturity |
SDK Overview
The high-level Python API lives in the d_asa package and is centered on DAsa.
It wraps the generated ARC-56 client in d_asa.artifacts.dasa_client
and keeps the generated ABI structs out of the normal user flow.
from d_asa import (
DAsa,
ContractAttributes,
PricingContext,
make_pam_zero_coupon_bond,
normalize_contract_attributes,
)
The root package also continues to expose the domain models and the ACTUS normalizer directly:
ContractAttributesmake_pam_zero_coupon_bond(...)make_pam_fixed_coupon_bond_profile(...)normalize_contract_attributes(...)NormalizationResultNormalizedActusTermsInitialKernelStateExecutionScheduleEntryAccountPositionObservedEventRequestObservedCashEventRequestCycle
Use DAsa for contract interaction, and keep d_asa.artifacts.dasa_client as the
explicit low-level escape hatch when needed.
Deploy and Attach
Deploy
from d_asa import DAsa
app = DAsa.deploy(
algorand=algorand,
arranger=arranger,
app_name="my-dasa",
)
Deploy and Configure
deploy_configured(...) accepts either a precomputed NormalizationResult or raw
ContractAttributes plus the normalization inputs.
app = DAsa.deploy_configured(
algorand=algorand,
arranger=arranger,
normalized=normalized,
prospectus_url="Issuer term sheet",
)
When configuration happens through the high-level API, DAsa also stores a PricingContext
derived from normalized.terms.notional_unit_value.
Attach to an Existing App
app = DAsa.from_app_id(
algorand=algorand,
app_id=12345,
)
If you already have the generated client:
from d_asa.artifacts.dasa_client import DasaClient
raw_client = DasaClient(algorand=algorand, app_id=12345)
app = DAsa.from_client(raw_client)
Contract View
app.contract exposes readonly helpers built from on-chain state:
get_state()get_next_due_event()get_schedule_entry(event_id)get_arranger()get_address_roles(address)get_role_validity(role, address)raw_client
Role Wrappers
DAsa binds signer accounts into role-oriented wrappers.
arranger = app.arranger(arranger_account)
account_manager = app.account_manager(manager_account)
primary_dealer = app.primary_dealer(dealer_account)
trustee = app.trustee(trustee_account)
authority = app.authority(authority_account)
observer = app.observer(observer_account)
op_daemon = app.op_daemon(op_daemon_account)
holder = app.account(holder_account)
Arranger
ArrangerRole covers the contract lifecycle and arranger-controlled actions:
configure(...)configure_from_attributes(...)configure_contract(...)upload_schedule(...)execute_ied()set_transfer_window(...)fund_due_cashflows(...)append_observed_cash_event(...)append_observed_cash_events(...)apply_non_cash_event(...)rotate_arranger(...)set_op_daemon(...)assign_role(...)revoke_role(...)
Other Roles
AccountManagerRole.open_account(...)PrimaryDealerRole.primary_distribution(...)TrusteeRole.set_default(...)AuthorityRole.suspend_account(...)AuthorityRole.set_contract_suspension(...)ObserverRole.apply_non_cash_event(...)OpDaemonRole.fund_due_cashflows(...)OpDaemonRole.claim(...)
Holding Accounts
HoldingAccount is the investor-facing wrapper:
get_raw_position()get_actualized_position(...)get_valuation(...)quote_trade(...)build_otc_dvp(...)transfer(...)claim(...)update_payment_address(...)
Actualized Positions and Valuation
HoldingAccount.get_actualized_position() mirrors the contract’s lazy settlement
rules off chain:
- reserved units become active after
IED - cumulative interest and principal indices are applied to the holder checkpoints
- the returned position is a preview only; it does not mutate chain state
holder = app.account(investor)
position = holder.get_actualized_position()
get_valuation() builds on top of that position and adds economic fields:
principal_shareclaimable_interestclaimable_principalaccrued_interest_not_dueeconomic_value_total
valuation = holder.get_valuation()
Trade Quotes
quote_trade(units=...) is intended to support secondary-market pricing in the
middle of an accrual period.
The quote always returns:
- the current principal share of the traded units
- accrued interest that is not yet due
- a par-reference clean value
- a par-reference dirty value
If a clean quote is supplied, the SDK derives a market dirty value by adding accrued-not-due carry.
from decimal import Decimal
from d_asa import TradeQuoteInput
quote = holder.quote_trade(
25,
clean_quote=TradeQuoteInput(clean_price_per_100=Decimal("99.25")),
)
Important behavior:
-
Quotes are rejected when a due ACTUS event is already pending at the requested timestamp.
-
Existing
claimable_interestandclaimable_principalremain attached to the seller account and are reported as retained balances, not transferred unit value. -
clean_price_per_100requires aPricingContext, becausenotional_unit_valueis SDK metadata and is not stored on chain.
Over-the-Counter (OTC) DvP Builder
HoldingAccount.build_otc_dvp(...) builds an atomic delivery-vs-payment group for
OTC secondary trades.
The builder is intentionally non-custodial:
- it creates the payment leg
- it creates the D-ASA transfer leg
- it returns the composed group for inspection, signing, simulation, or sending
Algo Settlement
draft = app.account(seller).build_otc_dvp(
buyer=buyer,
units=10,
payment_amount=150_000,
)
result = draft.send()
ASA Settlement
draft = app.account(seller).build_otc_dvp(
buyer=buyer,
units=10,
payment_amount=15_000,
payment_asset_id=settlement_asa_id,
)
Guardrails
The builder validates:
units > 0payment_amount > 0- seller and buyer are different
- the seller signer matches the transfer sender holding address
- optional quote tolerance, when provided
You can combine it with quote_trade(...) inputs to fail fast when the negotiated
payment amount drifts too far from the SDK’s accrual-aware reference.
Interfaces
The interface section documents the public ARC-4 ABI grouped by architecture layer, plus the ABI structs used by those methods.
ABI Types
This section documents the canonical ARC-4 structs exposed by the D-ASA ABI.
The types below refer to the on-chain ABI structs in smart_contracts/abi_types.py,
not to the richer SDK-side normalization models in src/models.py.
RoleValidity
Time-bounded role assignment.
| Field | Type | Meaning |
|---|---|---|
role_validity_start | uint64 | Inclusive activation timestamp |
role_validity_end | uint64 | Inclusive expiration timestamp |
Prospectus
Informational contract metadata uploaded with contract_config.
| Field | Type | Meaning |
|---|---|---|
hash | byte[32] | Prospectus digest |
url | string | Prospectus location or label |
NormalizedActusTerms
Normalized ACTUS terms required by the AVM kernel.
| Field | Type | Meaning |
|---|---|---|
contract_type | uint8 | ACTUS contract family id |
denomination_asset_id | uint64 | Denomination asset id |
settlement_asset_id | uint64 | Settlement asset id |
total_units | uint64 | Total unit supply |
notional_principal | uint64 | Notional principal in base units |
initial_exchange_amount | uint64 | Initial exchange amount in base units |
initial_exchange_date | uint64 | IED timestamp |
maturity_date | uint64 | MD timestamp, or 0 if absent |
day_count_convention | uint8 | Supported day-count identifier |
rate_reset_spread | uint64 | Fixed-point spread |
rate_reset_multiplier | uint64 | Fixed-point rate multiplier |
rate_reset_floor | uint64 | Fixed-point floor |
rate_reset_cap | uint64 | Fixed-point cap |
rate_reset_next | uint64 | Prefixed next rate value |
has_rate_reset_floor | bool | Floor flag |
has_rate_reset_cap | bool | Cap flag |
dynamic_principal_redemption | bool | Dynamic annuity redemption flag |
fixed_point_scale | uint64 | Fixed-point scale, currently 1_000_000_000 |
SDK-only metadata such as notional_unit_value and secondary-market dates is not
part of this ABI struct.
InitialKernelState
Pre-IED state snapshot uploaded with contract_config.
| Field | Type | Meaning |
|---|---|---|
status_date | uint64 | Reference timestamp for the uploaded state |
event_cursor | uint64 | Starting global event cursor |
outstanding_principal | uint64 | Outstanding principal in base units |
interest_calculation_base | uint64 | Interest base in base units |
nominal_interest_rate | uint64 | Current fixed-point nominal rate |
accrued_interest | uint64 | Accrued interest in base units |
next_principal_redemption | uint64 | Next redemption target |
cumulative_interest_index | uint64 | Per-unit fixed-point interest index |
cumulative_principal_index | uint64 | Per-unit fixed-point principal index |
ExecutionScheduleEntry
One normalized ACTUS schedule entry.
| Field | Type | Meaning |
|---|---|---|
event_id | uint64 | Global contiguous event identifier |
event_type | uint8 | ACTUS event id |
scheduled_time | uint64 | Due timestamp |
accrual_factor | uint64 | Fixed-point accrual factor |
redemption_accrual_factor | uint64 | Fixed-point redemption factor |
next_nominal_interest_rate | uint64 | Fixed-point next rate |
next_principal_redemption | uint64 | Next redemption state value |
next_outstanding_principal | uint64 | Next outstanding principal |
flags | uint64 | Entry flags |
ExecutionSchedulePage
Alias for:
ExecutionScheduleEntry[]
The current kernel accepts at most 16 entries per page.
ObservedEventRequest
Observed non-cash event payload used by apply_non_cash_event.
| Field | Type | Meaning |
|---|---|---|
event_id | uint64 | Expected event id when appending |
event_type | uint8 | ACTUS event id |
scheduled_time | uint64 | Event timestamp |
accrual_factor | uint64 | Fixed-point accrual factor |
redemption_accrual_factor | uint64 | Fixed-point redemption factor |
observed_rate | uint64 | Observed fixed-point rate input |
next_nominal_interest_rate | uint64 | Fixed-point next rate |
next_principal_redemption | uint64 | Next redemption state value |
next_outstanding_principal | uint64 | Next outstanding principal |
flags | uint64 | Observed-event flags |
ObservedCashEventRequest
Observed cash event payload used by append_observed_cash_event.
| Field | Type | Meaning |
|---|---|---|
event_id | uint64 | Expected event id when appending |
event_type | uint8 | ACTUS cash event id |
scheduled_time | uint64 | Event timestamp |
accrual_factor | uint64 | Fixed-point accrual factor |
redemption_accrual_factor | uint64 | Fixed-point redemption factor |
next_nominal_interest_rate | uint64 | Fixed-point next rate |
next_principal_redemption | uint64 | Next redemption state value |
next_outstanding_principal | uint64 | Next outstanding principal |
flags | uint64 | Observed cash-event flags |
KernelState
Readonly snapshot returned by contract_get_state.
| Field | Type | Meaning |
|---|---|---|
contract_type | uint8 | ACTUS contract family id |
status | uint64 | Kernel lifecycle status |
total_units | uint64 | Total unit supply |
reserved_units_total | uint64 | Units reserved pre-IED |
initial_exchange_amount | uint64 | Initial exchange amount |
event_cursor | uint64 | Current global event cursor |
schedule_entry_count | uint64 | Total uploaded schedule entries |
outstanding_principal | uint64 | Outstanding principal |
interest_calculation_base | uint64 | Current interest base |
nominal_interest_rate | uint64 | Current fixed-point nominal rate |
accrued_interest | uint64 | Current accrued interest |
cumulative_interest_index | uint64 | Global per-unit interest index |
cumulative_principal_index | uint64 | Global per-unit principal index |
reserved_interest | uint64 | Reserved interest balance |
reserved_principal | uint64 | Reserved principal balance |
The contract-level defaulted performance flag is stored separately in RBAC-managed
global state and is not part of KernelState.
AccountPosition
Accounting position returned by account_get_position.
| Field | Type | Meaning |
|---|---|---|
payment_address | address | Cashflow destination |
units | uint64 | Active units |
reserved_units | uint64 | Pre-IED reserved units |
suspended | bool | Account suspension flag |
settled_cursor | uint64 | Last settled global cursor |
interest_checkpoint | uint64 | Applied interest index checkpoint |
principal_checkpoint | uint64 | Applied principal index checkpoint |
claimable_interest | uint64 | Claimable interest amount |
claimable_principal | uint64 | Claimable principal amount |
CashFundingResult
Result returned by fund_due_cashflows.
| Field | Type | Meaning |
|---|---|---|
funded_interest | uint64 | Interest reserved in the call |
funded_principal | uint64 | Principal reserved in the call |
total_funded | uint64 | Total reserved amount |
processed_events | uint64 | Number of processed cash events |
timestamp | uint64 | Execution timestamp |
CashClaimResult
Result returned by claim_due_cashflows.
| Field | Type | Meaning |
|---|---|---|
interest_amount | uint64 | Interest realized or reported |
principal_amount | uint64 | Principal realized or reported |
total_amount | uint64 | Total realized or reported amount |
timestamp | uint64 | Execution timestamp |
context | byte[] | Opaque caller-supplied context |
RBAC Interface
The RBAC interface manages privileged roles, suspension state, contract default state, and application control.
Only the Arranger may call RBAC methods, unless otherwise specified.
contract_update
{
"name": "contract_update",
"readonly": false,
"args": [],
"returns": { "type": "void" },
"errors": ["UNAUTHORIZED"]
}
rbac_rotate_arranger
{
"name": "rbac_rotate_arranger",
"readonly": false,
"args": [
{ "name": "new_arranger", "type": "address" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the rotation" },
"errors": ["UNAUTHORIZED", "INVALID_ROLE_ADDRESS"]
}
The new arranger must not be the Algorand global zero address.
rbac_set_op_daemon
{
"name": "rbac_set_op_daemon",
"readonly": false,
"args": [
{ "name": "address", "type": "address" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the assignment" },
"errors": ["UNAUTHORIZED"]
}
This is a non-normative helper for payment automation.
rbac_assign_role
{
"name": "rbac_assign_role",
"readonly": false,
"args": [
{ "name": "role_id", "type": "uint8" },
{ "name": "role_address", "type": "address" },
{ "name": "validity", "type": "RoleValidity" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the assignment" },
"errors": ["UNAUTHORIZED", "DEFAULTED", "INVALID_ROLE", "INVALID_ROLE_ADDRESS", "INVALID_SORTING"]
}
Only assign non-Arranger roles. The target address must not be the global zero address,
and validity.role_validity_start must be strictly earlier than validity.role_validity_end.
rbac_revoke_role
{
"name": "rbac_revoke_role",
"readonly": false,
"args": [
{ "name": "role_id", "type": "uint8" },
{ "name": "role_address", "type": "address" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the revocation" },
"errors": ["UNAUTHORIZED", "DEFAULTED", "INVALID_ROLE", "INVALID_ROLE_ADDRESS"]
}
rbac_contract_suspension
{
"name": "rbac_contract_suspension",
"readonly": false,
"args": [
{ "name": "suspended", "type": "bool" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the suspension update" },
"errors": ["UNAUTHORIZED"]
}
Only an active Authority may call this method.
rbac_contract_default
{
"name": "rbac_contract_default",
"readonly": false,
"args": [
{ "name": "defaulted", "type": "bool" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the default update" },
"errors": ["UNAUTHORIZED"]
}
Only an active Trustee may call this method.
It sets or clears the contract-level defaulted performance flag. This flag is
distinct from the kernel lifecycle status.
rbac_get_arranger
{
"name": "rbac_get_arranger",
"readonly": true,
"args": [],
"returns": { "type": "address", "desc": "Current arranger address" },
"errors": []
}
rbac_get_address_roles
{
"name": "rbac_get_address_roles",
"readonly": true,
"args": [
{ "name": "address", "type": "address" }
],
"returns": {
"type": "(bool,bool,bool,bool,bool)",
"desc": "Account manager, primary dealer, trustee, authority, observer"
},
"errors": []
}
rbac_get_role_validity
{
"name": "rbac_get_role_validity",
"readonly": true,
"args": [
{ "name": "role_id", "type": "uint8" },
{ "name": "role_address", "type": "address" }
],
"returns": { "type": "RoleValidity", "desc": "Stored validity interval" },
"errors": ["INVALID_ROLE", "INVALID_ROLE_ADDRESS"]
}
Only applicable to non-Arranger roles.
Accounting Interface
The Accounting interface manages holder positions and account-level suspension.
account_suspension
{
"name": "account_suspension",
"readonly": false,
"args": [
{ "name": "holding_address", "type": "address" },
{ "name": "suspended", "type": "bool" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the suspension update" },
"errors": ["UNAUTHORIZED", "INVALID_HOLDING_ADDRESS"]
}
Only an active Authority may call this method.
account_open
{
"name": "account_open",
"readonly": false,
"args": [
{ "name": "holding_address", "type": "address" },
{ "name": "payment_address", "type": "address" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the account opening" },
"errors": ["UNAUTHORIZED", "DEFAULTED", "SUSPENDED", "INVALID_HOLDING_ADDRESS"]
}
Only an active Account Manager may call this method.
The current implementation treats an already existing holding address as INVALID_HOLDING_ADDRESS.
account_update_payment_address
{
"name": "account_update_payment_address",
"readonly": false,
"args": [
{ "name": "holding_address", "type": "address" },
{ "name": "payment_address", "type": "address" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the account update" },
"errors": ["UNAUTHORIZED", "DEFAULTED", "SUSPENDED", "INVALID_HOLDING_ADDRESS"]
}
Only the Account Holding Address itself may call this method.
account_get_position
{
"name": "account_get_position",
"readonly": true,
"args": [
{ "name": "holding_address", "type": "address" }
],
"returns": { "type": "AccountPosition", "desc": "Full holder accounting position" },
"errors": ["INVALID_HOLDING_ADDRESS"]
}
ACTUS Kernel Interface
The ACTUS Kernel interface configures the normalized contract, stores the execution schedule, and advances non-cash lifecycle events.
contract_create
{
"name": "contract_create",
"create": "require",
"readonly": false,
"args": [
{ "name": "arranger", "type": "address" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of contract creation" },
"errors": ["INVALID_ROLE_ADDRESS"]
}
The arranger argument must not be the Algorand global zero address.
contract_config
{
"name": "contract_config",
"readonly": false,
"args": [
{ "name": "terms", "type": "NormalizedActusTerms" },
{ "name": "initial_state", "type": "InitialKernelState" },
{ "name": "prospectus", "type": "Prospectus" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of contract configuration" },
"errors": [
"UNAUTHORIZED",
"ALREADY_CONFIGURED",
"INVALID_ACTUS_CONFIG",
"INVALID_DAY_COUNT_CONVENTION",
"INVALID_DENOMINATION",
"INVALID_SETTLEMENT_ASSET",
"INVALID_IED"
]
}
Only the Arranger may call this method.
contract_schedule
{
"name": "contract_schedule",
"readonly": false,
"args": [
{ "name": "schedule_page_index", "type": "uint64" },
{ "name": "is_last_page", "type": "bool" },
{ "name": "schedule_page", "type": "ExecutionSchedulePage" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the schedule upload" },
"errors": [
"UNAUTHORIZED",
"ALREADY_CONFIGURED",
"TERMS_NOT_CONFIGURED",
"INVALID_SCHEDULE_PAGE",
"INVALID_EVENT_ID",
"INVALID_ACTUS_CONFIG",
"INVALID_SORTING"
]
}
Only the Arranger may call this method.
contract_execute_ied
{
"name": "contract_execute_ied",
"readonly": false,
"args": [],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the IED execution" },
"errors": [
"NOT_CONFIGURED",
"UNAUTHORIZED",
"DEFAULTED",
"SUSPENDED",
"INVALID_EVENT_CURSOR",
"INVALID_EVENT_TYPE",
"NO_DUE_CASHFLOW",
"INVALID_ACTUS_CONFIG",
"PRIMARY_DISTRIBUTION_INCOMPLETE"
]
}
Only the Arranger may call this method.
apply_non_cash_event
{
"name": "apply_non_cash_event",
"readonly": false,
"args": [
{ "name": "event_id", "type": "uint64" },
{ "name": "payload", "type": "ObservedEventRequest" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the event application" },
"errors": [
"NOT_CONFIGURED",
"DEFAULTED",
"SUSPENDED",
"OBSERVED_EVENT_REQUIRED",
"UNAUTHORIZED",
"INVALID_EVENT_ID",
"INVALID_EVENT_CURSOR",
"INVALID_EVENT_TYPE",
"INVALID_SCHEDULE_PAGE",
"INVALID_ACTUS_CONFIG",
"INVALID_SORTING",
"NO_DUE_CASHFLOW",
"PENDING_IED"
]
}
RR and RRF execution is Observer-controlled. Other non-cash events are Arranger-controlled.
append_observed_cash_event
{
"name": "append_observed_cash_event",
"readonly": false,
"args": [
{ "name": "payload", "type": "ObservedCashEventRequest" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the append operation" },
"errors": [
"NOT_CONFIGURED",
"UNAUTHORIZED",
"DEFAULTED",
"SUSPENDED",
"PENDING_IED",
"OBSERVED_EVENT_REQUIRED",
"INVALID_EVENT_ID",
"INVALID_EVENT_TYPE",
"INVALID_SCHEDULE_PAGE",
"INVALID_ACTUS_CONFIG",
"INVALID_SORTING"
]
}
contract_get_state
{
"name": "contract_get_state",
"readonly": true,
"args": [],
"returns": { "type": "KernelState", "desc": "Current kernel state snapshot" },
"errors": ["NOT_CONFIGURED"]
}
KernelState does not include the separate RBAC-managed defaulted performance
flag. Clients that need that value must read the dedicated global-state key.
contract_get_next_due_event
{
"name": "contract_get_next_due_event",
"readonly": true,
"args": [],
"returns": {
"type": "ExecutionScheduleEntry",
"desc": "Next due schedule entry, or a zero sentinel if the contract ended"
},
"errors": ["NOT_CONFIGURED"]
}
Payment Agent Interface
The Payment Agent interface funds due ACTUS cash events and allows holder claims.
fund_due_cashflows
{
"name": "fund_due_cashflows",
"readonly": false,
"args": [
{ "name": "max_event_count", "type": "uint64" }
],
"returns": { "type": "CashFundingResult", "desc": "Funding result for the processed events" },
"errors": [
"NOT_CONFIGURED",
"UNAUTHORIZED",
"DEFAULTED",
"SUSPENDED",
"PENDING_IED",
"NO_DUE_CASHFLOW",
"NOT_ENOUGH_FUNDS"
]
}
If an Op Daemon is configured, only the Arranger or Op Daemon may call this method. Otherwise the method is permissionless.
claim_due_cashflows
{
"name": "claim_due_cashflows",
"readonly": false,
"args": [
{ "name": "holding_address", "type": "address" },
{ "name": "payment_info", "type": "byte[]" }
],
"returns": { "type": "CashClaimResult", "desc": "Holder claim result" },
"errors": [
"NOT_CONFIGURED",
"INVALID_HOLDING_ADDRESS",
"UNAUTHORIZED",
"DEFAULTED",
"SUSPENDED",
"PENDING_IED",
"NO_DUE_CASHFLOW",
"NOT_ENOUGH_FUNDS"
]
}
If the holder’s payment path is not executable, the method reports the claimable amounts without clearing them.
Transfer Agent Interface
The Transfer Agent interface manages transfer windows, primary allocations, and secondary transfers.
transfer_set_schedule
{
"name": "transfer_set_schedule",
"readonly": false,
"args": [
{ "name": "open_date", "type": "uint64" },
{ "name": "closure_date", "type": "uint64" }
],
"returns": { "type": "uint64", "desc": "UNIX timestamp of the schedule update" },
"errors": ["UNAUTHORIZED", "INVALID_SORTING", "INVALID_TRANSFER_OPENING"]
}
Only the Arranger may call this method. The IED MUST be already defined and
the opening date MUST be greater than or equal to IED.
primary_distribution
{
"name": "primary_distribution",
"readonly": false,
"args": [
{ "name": "holding_address", "type": "address" },
{ "name": "units", "type": "uint64" }
],
"returns": { "type": "uint64", "desc": "Remaining undistributed unit balance" },
"errors": [
"NOT_CONFIGURED",
"UNAUTHORIZED",
"DEFAULTED",
"SUSPENDED",
"PRIMARY_DISTRIBUTION_CLOSED",
"INVALID_HOLDING_ADDRESS",
"ZERO_UNITS",
"OVER_DISTRIBUTION"
]
}
transfer
{
"name": "transfer",
"readonly": false,
"args": [
{ "name": "sender_holding_address", "type": "address" },
{ "name": "receiver_holding_address", "type": "address" },
{ "name": "units", "type": "uint64" }
],
"returns": { "type": "uint64", "desc": "Number of units transferred" },
"errors": [
"NOT_CONFIGURED",
"DEFAULTED",
"SUSPENDED",
"PENDING_IED",
"CLOSED_TRANSFER",
"UNAUTHORIZED",
"INVALID_HOLDING_ADDRESS",
"SELF_TRANSFER",
"NULL_TRANSFER",
"OVER_TRANSFER",
"PENDING_ACTUS_EVENT"
]
}
The current implementation requires the sender holding address to submit the transaction.
Reference Implementation
⚠️The reference implementation has not been audited. Do not use this code for real products. The author declines all responsibility.
The reference implementation exposes one canonical on-chain contract, DASA, and
composes it from five modules:
RbacModuleActusKernelModuleAccountingModulePaymentAgentTransferAgent
The implementation executes normalized ACTUS schedules on the AVM and currently
supports the kernel contract families PAM, ANN, NAM, LAM, LAX, and CLM.
Delivered artifacts
The canonical artifacts are:
src/d_asa/artifacts/DASA.arc56.jsonsrc/d_asa/artifacts/dasa_client.pysrc/d_asa/artifacts/dasa_avm_client.py
The deployment helpers in smart_contracts/d_asa/deploy_config.py provide code-accurate
demo normalization flows for:
- a PAM fixed coupon bond profile;
- a PAM zero coupon bond profile.
Those demos are examples of normalized inputs to the shared kernel, not separate contract classes with different public ABIs.
Architecture
The current D-ASA architecture is layered, not product-specific. The on-chain contract
is a single DASA application assembled from five modules:
flowchart LR RBAC["RBAC"] --> KERNEL["ACTUS Kernel"] KERNEL --> ACCOUNTING["Accounting"] ACCOUNTING --> PAYMENT["Payment Agent"] ACCOUNTING --> TRANSFER["Transfer Agent"]
Processing chain
The canonical processing chain is:
flowchart LR ACTUS["ACTUS Contract"] --> NORMALIZE["AVM Normalization"] NORMALIZE --> ABI["ABI Upload"] ABI --> EXEC["AVM Execution"]
This separation is intentional:
- ACTUS contract definition and schedule generation happen off chain.
- Normalization converts that contract into fixed-size AVM-compatible structs.
- The AVM kernel validates and executes the normalized lifecycle.
Module responsibilities
-
RbacModule: role assignment, suspension, contract default control, and application-control methods. -
ActusKernelModule: normalized-term validation, schedule storage, event cursor, state transitions, and generic ACTUS getters. -
AccountingModule: holder positions, unit activation, checkpoints, and claim ledgers. -
PaymentAgent: funding of due cash events and holder withdrawals. -
TransferAgent: primary distribution, transfer schedule, and settled unit transfers.
Public API by layer
| Layer | Public methods |
|---|---|
| RBAC | contract_update, rbac_rotate_arranger, rbac_set_op_daemon, rbac_assign_role, rbac_revoke_role, rbac_contract_suspension, rbac_contract_default, rbac_get_arranger, rbac_get_address_roles, rbac_get_role_validity |
| Accounting | account_suspension, account_open, account_update_payment_address, account_get_position |
| ACTUS Kernel | contract_create, contract_config, contract_schedule, contract_execute_ied, apply_non_cash_event, append_observed_cash_event, contract_get_state, contract_get_next_due_event |
| Payment Agent | fund_due_cashflows, claim_due_cashflows |
| Transfer Agent | transfer_set_schedule, primary_distribution, transfer |
Execution flows
Configuration
sequenceDiagram participant SDK as "Normalization SDK" participant ABI as "ABI Caller" participant K as "ACTUS Kernel" SDK->>ABI: "NormalizedActusTerms + InitialKernelState + Schedule Pages" ABI->>K: "contract_config(...)" ABI->>K: "contract_schedule(page 0)" ABI->>K: "contract_schedule(page n, is_last_page=true)" ABI->>K: "primary_distribution(...)" ABI->>K: "contract_execute_ied()"
Cash events
sequenceDiagram participant ABI as "ABI Caller" participant PA as "Payment Agent" participant ACC as "Accounting" ABI->>PA: "fund_due_cashflows(max_event_count)" PA->>ACC: "advance global indices and reserve funds" ABI->>PA: "claim_due_cashflows(holding_address, payment_info)" PA->>ACC: "settle holder checkpoints" PA-->>ABI: "CashClaimResult"
Transfers
sequenceDiagram participant ABI as "Holder" participant TA as "Transfer Agent" participant ACC as "Accounting" ABI->>TA: "transfer(sender, receiver, units)" TA->>ACC: "settle sender" TA->>ACC: "settle receiver" TA->>ACC: "move active units"
Tests
The D-ASA project is developed with AlgoKit.
- Install AlgoKit
- Setup your virtual environment (managed with Poetry)
algokit bootstrap all
- Start your Algorand LocalNet (requires Docker)
algokit localnet start
- Run tests (managed with PyTest)
algokit project run test
or, for verbose results:
poetry run pytest -s -v tests/<contract_name>/<test_case>.py
The repository also includes a D-ASA showcase walkthrough that prints the normalized ACTUS schedule beside the real ARC-28 execution proofs and realized cashflows for PAM fixed coupon and zero coupon bonds:
poetry run pytest -s -v -m showcase tests/pam/test_pam_lifecycle_showcase.py

Coverage Areas
-
tests/sdk/*covers the Python-side SDK and normalization layer: contract builders, schedules, day-count conventions, registry mappings, models, and deploy configuration helpers. -
tests/mock_module_rbac/*exercises RBAC behavior in isolation through the dedicated mock module artifact. -
tests/pam/fcb/*andtests/pam/zcb/*run end-to-end PAM lifecycle tests on LocalNet for fixed coupon and zero coupon bonds, including schedule upload,IED, cashflow funding, holder claims, and final contract state. -
tests/pam/test_pam_lifecycle_showcase.pyis the narrative walkthrough suite for new users. It prints the normalized ACTUS schedule beside the emitted ARC-28 execution receipts and realized cashflows. It is markedshowcaseand excluded from the defaultalgokit project run testcommand. -
Shared fixtures live in
tests/conftest.py,tests/pam/conftest.py, andtests/conftest_helpers.py; helper decoding and time-warp utilities live intests/utils.py.
DeepWiki (Experimental)
⚠️ This is experimental AI-generated documentation of the D-ASA reference repository. Readers may use this documentation to complement the contents of this documentation, to deepen understanding of the D-ASA architecture and reference implementation.
DeepWiki of the D-ASA repository:
https://deepwiki.com/cusma/d-asa
The embedded chatbot, with deep-research capabilities over the D-ASA codebase, can answer specific questions.
Contributing to D-ASA
Thank you for contributing to D-ASA.
Prerequisites
The standard contributor workflow assumes:
- AlgoKit for project bootstrap, LocalNet, build, and deployment workflows
- Poetry for the Python environment
- Docker for Algorand LocalNet
- pre-commit for local checks
Docs contributors also need:
- Rust and Cargo
mdbookmdbook-mermaid
Getting Started
Clone the Repository
git clone git@github.com:cusma/d-asa.git
cd d-asa
Bootstrap the Development Environment
The standard setup path is:
algokit project bootstrap all
make pre-commit
make doctor
If you are only setting up Python dependencies:
make install
If you are contributing to docs, install the mdBook toolchain as well:
cargo install mdbook --version 0.5.2 --locked
cargo install mdbook-mermaid --version 0.17.0 --locked
mdbook-mermaid install .
Verify the Environment
Run:
make doctor
make doctor runs algokit doctor, checks core contributor dependencies, checks
Docker daemon reachability for LocalNet workflows, and warns if docs-only tools
are missing.
Development Workflow
Core Commands
The main contributor commands are:
make help
make build
make test
make test-cov
make lint
make format
make typecheck
make all
make all runs the standard code quality loop for contributors:
- build smart contracts
- run lint and type checks
- run the default non-showcase test suite
It intentionally does not require mdBook tooling.
LocalNet Workflow
Start LocalNet when you need end-to-end execution tests or LocalNet deployment:
make localnet
make deploy-localnet
make showcase
make localnet-stop
These targets map to the existing AlgoKit workflow already used by CI and the project scripts.
Docs Workflow
For docs editing with live reload:
make docs-serve
For docs validation:
make docs
make docs runs both:
- the docs pre-commit hooks:
mdbook-buildmdbook-testmarkdownlinttrailing-whitespace
- the mdBook build artifacts remain available in
book/untilmake clean
Pull Requests
When preparing a pull request:
- Keep changes focused and atomic.
- Add or update tests when behavior changes.
- Update docs when user-facing behavior, workflows, or interfaces change.
- Run the relevant local checks before opening the PR.
Suggested pre-PR checklist:
make doctor
make all
If you changed docs:
make docs
If you changed LocalNet behavior or showcase flows:
make localnet
make showcase
make localnet-stop
Repository Layout
The main top-level layout is:
.algokit/ -> AlgoKit configuration and generators
.github/ -> CI/CD workflows and shared GitHub actions
docs/ -> mdBook source files
modules/ -> D-ASA module implementations
smart_contracts/ -> AVM smart contracts and generated contract artifacts
src/ -> D-ASA SDK, Python-side normalization, helpers, and shared runtime code
tests/ -> SDK, contract, LocalNet, and showcase tests
Style and Quality
Python
The project uses:
blackfor formattingrufffor linting and import organizationmypyfor type checkingpytestfor tests
Run the standard checks with:
make format
make lint
make typecheck
make test
Markdown and mdBook
The docs are written in CommonMark and validated in
CI with markdownlint, trailing whitespace checks, and mdbook test.
Numbered Lists
Numbered lists MUST use 1.-only style.
1. First item 1. Second item 1. Third item
Tables
Table rows MUST keep aligned column widths.
✅ Correct table format
| Month | Savings | |----------|---------| | January | EUR 250 | | February | EUR 80 | | March | EUR 420 |❌ Wrong table format
| Month | Savings | |----------|---------| | January | EUR 250 | | February | EUR 80 | | March | EUR 420 |
Column alignment may be controlled with : markers in the separator row.
Admonitions
An admonition is a special type of callout or notice block used to highlight important information. It is written as a blockquote with a special tag on the first line.
> [!NOTE]
> General information or additional context.
> [!TIP]
> A helpful suggestion or best practice.
> [!IMPORTANT]
> Key information that shouldn't be missed.
> [!WARNING]
> Critical information that highlights a potential risk.
> [!CAUTION]
> Information about potential issues that require caution.
MathJax
Mathematical formulas are rendered with MathJax.
Reference:
Additional Notes
- Do not remove or hand-edit generated artifacts under
smart_contracts/artifactsorsrc/artifactsunless the change explicitly requires regenerated output. - Prefer the checked-in
algokitcommands andmaketargets over ad-hoc local command variations so contributor workflows stay aligned with CI.
License
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.