Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/adr/0001-composition-via-explicit-leaf-overrides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 1. Composition via explicit leaf overrides

Status: accepted

## Context

OpenZeppelin stellar-contracts routes token behavior through a single
`type ContractType` slot, making flavors mutually exclusive: `Vault` and
`AllowList` cannot compose as types
([#560](https://github.com/OpenZeppelin/stellar-contracts/issues/560)). Upstream
fixes are in flux (`Compose`,
[PR #821](https://github.com/OpenZeppelin/stellar-contracts/pull/821), open and
unaudited). The same convergence exists across Rust chains: cw20-base's
documented extension pattern is explicit delegation from the final contract, not
type-level composition.

## Decision

`ContractType = Vault` stays intact so share math remains 100% OpenZeppelin.
`AllowList` is reused as a storage module, and every gate is written in this
contract's own entrypoint overrides. This is the design, not a stopgap: adopting
a future upstream composition mechanism is a governance option to be evaluated
on its merits, never a scheduled migration.

## Consequences

- The storage layout stays upstream-compatible either way.
- Every gated entrypoint needs an explicit test (the check is code we own).
- The earlier plan to "delete manual overrides when #560 ships audited" is void;
#560 is tracked only as a potential simplification.
25 changes: 25 additions & 0 deletions docs/adr/0002-exits-always-open-deposit-gates-receiver-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 2. Exits always open; deposits gate the receiver only

Status: accepted

## Context

The vault enforces a post-KYC allowlist, but a compliance vault that can trap
funds is worse than no vault. The open question was whether the deposit payer
must also be allowlisted, or only the receiver of the shares.

## Decision

`withdraw`/`redeem` (and any future redemption-claim path) are never
allowlist-gated and never pausable: a de-listed holder can always leave.
`deposit`/`mint` gate the receiver only. The payer is not checked — USDC carries
its own compliance on the asset side; the regulated asset here is the share, and
shares can only land in allowlisted hands.

## Consequences

- Third-party funding (custodian or treasury paying for an allowlisted investor)
is accepted behavior.
- Pause blocks entries and transfers only.
- Every change to an exit path must preserve the invariant, and tests assert
that exits work for de-listed holders and while paused.
34 changes: 34 additions & 0 deletions docs/adr/0003-roles-full-name-symbols-role-manager-read-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 3. Five full-name roles; Role Manager is read-only, execution in /admin

Status: accepted

## Context

Access control moves from `Ownable` to `stellar-access` AccessControl with five
roles. `symbol_short!` caps at 9 characters, so `governance`, `compliance` and
`attestation` cannot be compile-time constants. The roadmap assumed OpenZeppelin
Role Manager as the full governance UI. The spike
([#7](https://github.com/BootNodeDev/open-rwa-vault/issues/7)) verified it
against a live testnet contract: detection, role listing and admin transfers
work; `grant_role`/`revoke_role` fail against OZ ≥0.6.0 (argument-order bug we
reported upstream:
[openzeppelin-adapters#72](https://github.com/OpenZeppelin/openzeppelin-adapters/issues/72)),
its execution path has no Stellar multisig support, and the hosted instance is
testnet-only. Roles are discovered from indexed grant events, so a role never
granted is invisible to the tool.

## Decision

Roles are declared with runtime `Symbol::new` and full names — `governance`,
`compliance`, `attestation`, `treasury`, `guardian` — matching the pattern OZ's
own generator uses. All five are granted at deploy time. Role Manager is scoped
to visualization, audit and history; role mutations and pause execute through
the admin panel's signing-harness forms (compose → sign → share → submit), which
the multisig authorities require anyway.

## Consequences

- The deploy script grants every role so tooling renders them from day one.
- The /admin surface gains grant/revoke forms; the runbook documents Role
Manager as the verification step after each operation.
- Self-hosting Role Manager is a mainnet-track task, not needed for testnet.
21 changes: 21 additions & 0 deletions docs/adr/0004-authorities-are-native-stellar-multisigs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 4. Authorities are native Stellar multisig accounts

Status: accepted

## Context

Separation of duties requires each privileged role to be held by a multi-party
authority. Stellar accounts support signers, weights and thresholds natively
(`SetOptions`), proven in the current testnet deployment.

## Decision

Each authority is a native Stellar multisig G-account. Contracts check only the
account's authorization; no multisig logic is reimplemented on-chain.

## Consequences

- Any execution surface must support multi-signature collection. The signing
harness does; Role Manager does not (see ADR-0003).
- The deploy tooling creates and configures the authority accounts as part of a
reproducible setup.
21 changes: 21 additions & 0 deletions docs/adr/0005-exact-version-pins-no-mid-milestone-upgrades.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 5. Exact version pins; upgrades never mid-milestone

Status: accepted

## Context

The OZ 0.7.x line has a published audit; soroban-sdk 27.x is incompatible with
OZ 0.7.2 (coupled to sdk 26). A dependency bump mid-milestone invalidates tested
behavior at the worst possible time.

## Decision

Exact pins: `stellar-*` = 0.7.2, `soroban-sdk` 26.x. Dependency upgrades are
never performed mid-milestone; each migration is its own issue in the following
milestone.

## Consequences

- Dependabot needs a cargo ecosystem entry plus an ignore rule for soroban-sdk
major bumps.
- A compatible OZ release triggers a migration issue, not a hot swap.
34 changes: 34 additions & 0 deletions docs/adr/0006-upstream-policy-and-authorized-674-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 6. Upstream policy, and the authorized fallback for #674

Status: accepted

## Context

Gaps found in OZ primitives so far:
[#560](https://github.com/OpenZeppelin/stellar-contracts/issues/560)
(composition),
[#674](https://github.com/OpenZeppelin/stellar-contracts/issues/674)
(conversions ignore `total_assets` overrides — blocks attested-NAV accounting),
and
[openzeppelin-adapters#72](https://github.com/OpenZeppelin/openzeppelin-adapters/issues/72)
(Role Manager grant/revoke, found and filed by us). Waiting on upstream
responses would put third parties on our critical path. The roadmap also
prohibited rewriting conversion math without a formal decision — this ADR is
that decision, made in advance.

## Decision

Every gap gets an upstream comment or PR with this repo as evidence, plus a
local workaround marked `// WORKAROUND(OZ#nnn): <what> — delete when <trigger>`
(greppable; a workaround without this marker is a process bug). Upstream never
sits on the critical path. Specifically for #674: if the fix has not shipped in
an audited release when attested-NAV accounting lands, a fork-minimal wrapper is
pre-authorized — it may override only the conversion entrypoints, must delegate
every other line to the audited crate, must carry a parity test against OZ
behavior at zero off-chain value, and must carry the deletion trigger.

## Consequences

- Workarounds are enumerable (`grep WORKAROUND`), each with its exit condition.
- The #674 wrapper is the only sanctioned hand-rolled money math, bounded and
deletable.
20 changes: 20 additions & 0 deletions docs/adr/0007-apache-2-0-license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 7. Apache-2.0 license

Status: accepted

## Context

The reference base ships BUSL-1.1, which is source-available but not
OSI-approved. The Stellar Community Fund requires a permissive OSI-approved
license for its awards. BootNode owns the reference base, so relicensing the
code that moves across is possible.

## Decision

Apache-2.0, LICENSE file at the repo root.

## Consequences

- SCF-compatible, aligned with the OZ/Stellar ecosystem, includes an explicit
patent grant.
- Code migrated from the demo base is relicensed on the way in.
20 changes: 20 additions & 0 deletions docs/adr/0008-lp-counter-removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 8. LP counter removed from the contract

Status: accepted

## Context

The reference base kept an on-chain counter of liquidity providers. No product
requirement consumes it; on-chain counters add state that every entry/exit must
touch, creating footprint contention under concurrency.

## Decision

Removed. If the dashboard needs the figure, it is reconstructed off-chain from
events.

## Consequences

- One less write per deposit/withdraw and one less contention point.
- An on-chain holder-limit requirement, if it ever appears, is a new feature
with its own design — not this counter revived.