Skip to content
Merged
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
59 changes: 59 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!--
Keep the description short. Link the issues this PR closes with closing
keywords so they close on merge:

Closes #123
Closes #124
-->

## Summary

<!-- What changes and why, in a few lines. -->

## Linked issues

Closes #

## Checks

- [ ] Lint passes for every area touched
- [ ] Build passes
- [ ] Tests pass
- [ ] No secrets, keys, or `.env` values are committed

<details>
<summary><strong>Contract changes only</strong> — expand if this PR touches <code>contracts/</code></summary>

Run from `contracts/`:

```sh
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all
```

- [ ] All three pass
- [ ] Reviewed against the relevant sections of
[`contracts/SECURITY.md`](../blob/main/contracts/SECURITY.md#pre-deployment-checklist)

Confirm the sections that apply to this change:

- [ ] **Authorization** — every new or modified state-changing entrypoint calls
`require_auth()` on the correct principal, and no entrypoint treats a
caller-supplied address argument as proof of identity. Negative tests
exist **without** `mock_all_auths`.
- [ ] **Arithmetic** — no unchecked arithmetic on any path handling amounts,
shares, or percentages; overflow returns a typed error.
- [ ] **Storage and TTL** — correct durability chosen, and persistent entries
that must outlive the default are extended.
- [ ] **Pause** — new mutating entrypoints respect the pause guard.
- [ ] **Admin and upgrades** — privileged entrypoints are admin-gated and emit
an event.
- [ ] **Events** — every new state change emits an observable event, and the
event catalogue is updated.
- [ ] **Size** — WASM size impact considered for new dependencies or large code
additions.

If this PR knowingly leaves one of these open, say which and why:

</details>
35 changes: 25 additions & 10 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read pinned toolchain
id: toolchain
run: echo "channel=$(grep -m1 '^channel' rust-toolchain.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: ${{ steps.toolchain.outputs.channel }}
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
Expand All @@ -32,10 +35,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read pinned toolchain
id: toolchain
run: echo "channel=$(grep -m1 '^channel' rust-toolchain.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: ${{ steps.toolchain.outputs.channel }}
components: clippy
- name: Cache cargo registry
uses: actions/cache@v4
Expand All @@ -55,10 +61,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read pinned toolchain
id: toolchain
run: echo "channel=$(grep -m1 '^channel' rust-toolchain.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: ${{ steps.toolchain.outputs.channel }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
Expand All @@ -78,10 +87,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read pinned toolchain
id: toolchain
run: echo "channel=$(grep -m1 '^channel' rust-toolchain.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: ${{ steps.toolchain.outputs.channel }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
Expand All @@ -101,10 +113,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read pinned toolchain
id: toolchain
run: echo "channel=$(grep -m1 '^channel' rust-toolchain.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
toolchain: ${{ steps.toolchain.outputs.channel }}
targets: wasm32-unknown-unknown
- name: Cache cargo registry
uses: actions/cache@v4
Expand Down
8 changes: 7 additions & 1 deletion contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
target
.env
test_snapshots/
test_snapshots/

# Deployment records hold live contract ids per network and are produced by
# scripts/deploy.sh. They are environment state, not source. The committed
# deployments/testnet.example.json documents the shape.
deployments/*
!deployments/*.example.json
127 changes: 127 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# AssetsUp Soroban Contracts

Stellar/Soroban smart contracts backing the AssetsUp asset-management platform.
The workspace holds five crates covering the asset registry, multisig approval
of high-value transfers, escrow and KYC, and on-chain maintenance history.

## Crate map

| Crate | Directory | Deployable | Responsibility |
|---|---|---|---|
| `assetsup` | [`assetsup/`](assetsup/) | yes | Primary asset registry: registration, ownership transfer, tokenization, dividends, voting, leasing, insurance, detokenization. |
| `contrib` | [`contrib/`](contrib/) | yes | Secondary registry: audit log, emergency pause, insurance, leasing. Most files in this crate are **not compiled** — see below. |
| `multisig-wallet` | [`multisig-wallet/`](multisig-wallet/) | yes | General-purpose *m-of-n* wallet: transaction submission, confirmation, execution, owner/threshold governance, emergency freeze. |
| `multisig-transfer` | [`multisig_transfer/`](multisig_transfer/) | yes | Approval workflow for asset transfers, gated on per-category approval rules. Calls into a registry contract to move ownership. |
| `asset-maintenance` | [`asset-maintenance/`](asset-maintenance/) | yes | On-chain maintenance history, schedules, warranties, provider registry, and alerts. |

All five build to WASM and are deployable. `assetsup`, `contrib`,
`multisig-wallet`, and `multisig-transfer` additionally declare `crate-type =
["lib", "cdylib"]` so they can be imported by integration tests; only
`asset-maintenance` is `cdylib`-only.

Every crate has its own README with an entrypoint table, storage layout,
emitted events, and error list. Start there.

## How `assetsup` and `contrib` relate

This is the most common source of confusion in the workspace, because the two
crates share several module names (`audit`, `detokenization`, `insurance`,
`lease`, `tokenization`, and transfer restrictions).

They are **two independent contracts, deployed separately, with separate
storage**. Neither reads the other's state. The overlap is duplicated code, not
a shared library:

- **`assetsup` is the authoritative asset registry.** It is the larger crate
(~8,900 lines), it is the one the backend is expected to treat as the source
of truth for asset identity and ownership, and it is the only one with
dividends, voting, and detokenization proposals wired to a token supply.
- **`contrib` is a second, smaller registry** with an audit log, an emergency
pause, insurance, and leasing.

Where a module name appears in both, the implementations have diverged and are
not interchangeable. `assetsup::insurance` models policies and claims with a
full claim state machine; `contrib::insurance` is a smaller policy/claim store.
The same holds for `lease` and `audit`.

> **`contrib` is mostly dead code.** `contrib/src/lib.rs` declares only
> `audit`, `pause`, `types`, `insurance`, and `lease`. The other files in
> `contrib/src/` — escrow, KYC, staking, oracle, tokenization, detokenization,
> transfer restrictions, and its `error.rs` — have no `mod` declaration and are
> **not compiled into the crate**, about 1,670 lines in total. So the deployed
> `ContribContract` has no escrow, no KYC, no staking, no oracle, and no typed
> errors, despite the source files being present. See
> [`contrib/README.md`](contrib/README.md) before relying on any of them.

Resolving this duplication — deciding which crate owns each concern — is
tracked in [SC-46]. Until that lands, treat the two as separate contracts and
consult the per-crate README for the behaviour of the specific one you are
calling.

## Toolchain

The Rust toolchain is **pinned** in [`rust-toolchain.toml`](rust-toolchain.toml).
`rustup` picks it up automatically for any command run inside `contracts/`, and
every contracts job in [`.github/workflows/CI.yaml`](../.github/workflows/CI.yaml)
reads the same `channel` value, so local and CI builds always use the same
compiler.

Current pin: **Rust 1.96.0**, with the `rustfmt` and `clippy` components and the
`wasm32-unknown-unknown` target.

Floating `stable` is deliberately avoided. A new stable release can introduce
clippy lints that fail `-D warnings` on a pull request that changed nothing,
turning an unrelated Rust release into a broken build for every open PR.

### Upgrading the toolchain

Bump the toolchain **in its own dedicated pull request** so that any lint churn
is isolated from feature work and reviewable on its own:

1. Edit `channel` in `contracts/rust-toolchain.toml`.
2. Run `cargo fmt --all`, then `cargo clippy --all-targets --all-features -- -D warnings`
and fix any lints the new release introduced.
3. Confirm `cargo test --all` still passes and that the pinned `soroban-sdk`
version still compiles — the SDK sets its own MSRV and can lag new releases.
4. Open the PR with only the toolchain bump and its lint fixes.

CI needs no change: it reads the channel from the file.

## Local development

```sh
cd contracts

cargo build --all
cargo test --all
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
```

CI enforces all four. Run them before opening a pull request.

Build a contract for deployment:

```sh
cargo build --package assetsup --target wasm32-unknown-unknown --release
```

## Deployment

Scripted, reproducible testnet deployment lives in
[`scripts/deploy.sh`](scripts/deploy.sh). See
[`scripts/README.md`](scripts/README.md) for prerequisites and usage.

## Security

The trust model, per-contract attack surface, and the pre-deployment checklist
are documented in [`SECURITY.md`](SECURITY.md). Contract changes are reviewed
against that checklist.

## Naming conventions

- Crate (package) names use **hyphens**: `multisig-wallet`, `asset-maintenance`.
- Directory names should match the package name. One exception remains today —
the directory `multisig_transfer/` holds the package `multisig-transfer`,
which is why `cargo test -p multisig_transfer` fails while
`-p multisig-transfer` works. Tracked in [SC-33].
Loading
Loading