Skip to content

Commit f2b328a

Browse files
JakeUrbanclaudesisureshCopilot
authored
Add CAP-0084: Muxed Contract Addresses (#1968)
* Add CAP-0084: Muxed Contract Addresses Extend the address multiplexing introduced for Stellar accounts in CAP-67 to contract addresses. Adds an SC_ADDRESS_TYPE_MUXED_CONTRACT variant of SCAddress, reuses the MuxedAddressObject host object and its accessors, extends the SAC transfer/mint functions to accept a muxed contract destination, and proposes a corresponding strkey encoding to be defined normatively in SEP-23. Discussion: #1950 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Siddharth Suresh <siddharth@stellar.org> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 7bca224 commit f2b328a

2 files changed

Lines changed: 184 additions & 0 deletions

File tree

core/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
| [CAP-0057](cap-0057.md) | State Archival Persistent Entry Eviction | Garand Tyson | Draft |
118118
| [CAP-0060](cap-0060.md) | Update to Wasmi register machine| Graydon Hoare | Accepted |
119119
| [CAP-0072](cap-0072.md) | Contract signers for Stellar accounts | Dmytro Kozhevin | Draft |
120+
| [CAP-0084](cap-0084.md) | Muxed Contract Addresses | Jake Urban | Draft |
120121

121122
### Rejected Proposals
122123
| Number | Title | Author | Status |

core/cap-0084.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
## Preamble
2+
3+
```
4+
CAP: 0084
5+
Title: Muxed Contract Addresses
6+
Working Group:
7+
Owner: Jake Urban <jake@stellar.org>
8+
Authors: Jake Urban <jake@stellar.org>
9+
Consulted: Dmytro Kozhevin <@dmkozh>, Leigh McCulloch <@leighmcculloch>
10+
Status: Draft
11+
Created: 2026-06-24
12+
Discussion: https://github.com/stellar/stellar-protocol/discussions/1950
13+
Protocol version: 28
14+
```
15+
16+
## Simple Summary
17+
18+
Extend the address multiplexing introduced for Stellar accounts in [CAP-67](cap-0067.md) to contract addresses. A contract address may be paired with a `uint64` multiplexing identifier so that a single on-chain contract can represent an arbitrary number of off-chain, 'virtual' balances, in the same way that a single Stellar account can today.
19+
20+
## Working Group
21+
22+
As described in the preamble section.
23+
24+
## Motivation
25+
26+
[CAP-67](cap-0067.md) added multiplexing support for account addresses by introducing the `SC_ADDRESS_TYPE_MUXED_ACCOUNT` variant of `SCAddress`, the `MuxedAddressObject` host object, and a convention for representing the multiplexing identifier in the unified asset events. It did not, however, add an equivalent representation for contract addresses.
27+
28+
As a result there is an asymmetry in the protocol: a single Stellar account can be multiplexed to represent many off-chain destinations, but a single contract address cannot. There is no protocol-level way to attach a multiplexing identifier to a contract address, so the only way to distinguish between multiple off-chain balances held behind a contract is to deploy a distinct contract for each balance.
29+
30+
This CAP removes that asymmetry by replicating the CAP-67 account multiplexing design for contract addresses.
31+
32+
### Goals Alignment
33+
34+
This CAP is aligned with the following Stellar Network Goals:
35+
36+
- The Stellar Network should make it easy for developers of Stellar projects to create highly usable products.
37+
38+
## Abstract
39+
40+
A new `SC_ADDRESS_TYPE_MUXED_CONTRACT` variant is added to `SCAddress`, consisting of a contract id and a `uint64` multiplexing identifier. The existing `MuxedAddressObject` host object and its accessor host functions are generalized to represent muxed contract addresses in addition to muxed account addresses, requiring no new host functions. The Stellar Asset Contract `transfer` and `mint` functions accept a muxed contract address as the destination and emit the multiplexing identifier using the existing `to_muxed_id` event convention. A corresponding strkey encoding for muxed contract addresses is proposed here and will be defined normatively in SEP-23.
41+
42+
## Specification
43+
44+
### XDR changes
45+
46+
This patch of XDR changes is based on the latest protocol 27 XDR files in [stellar-xdr](https://github.com/stellar/stellar-xdr). The exact base commit will be pinned when the XDR is agreed on in a protocol meeting.
47+
48+
```diff mddiffcheck.ignore=true
49+
diff --git a/Stellar-contract.x b/Stellar-contract.x
50+
--- a/Stellar-contract.x
51+
+++ b/Stellar-contract.x
52+
@@
53+
enum SCAddressType
54+
{
55+
SC_ADDRESS_TYPE_ACCOUNT = 0,
56+
SC_ADDRESS_TYPE_CONTRACT = 1,
57+
SC_ADDRESS_TYPE_MUXED_ACCOUNT = 2,
58+
SC_ADDRESS_TYPE_CLAIMABLE_BALANCE = 3,
59+
- SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4
60+
+ SC_ADDRESS_TYPE_LIQUIDITY_POOL = 4,
61+
+ SC_ADDRESS_TYPE_MUXED_CONTRACT = 5
62+
};
63+
64+
struct MuxedEd25519Account
65+
{
66+
uint64 id;
67+
uint256 ed25519;
68+
};
69+
70+
+struct MuxedContract
71+
+{
72+
+ uint64 id;
73+
+ ContractID contractId;
74+
+};
75+
+
76+
union SCAddress switch (SCAddressType type)
77+
{
78+
case SC_ADDRESS_TYPE_ACCOUNT:
79+
AccountID accountId;
80+
case SC_ADDRESS_TYPE_CONTRACT:
81+
ContractID contractId;
82+
case SC_ADDRESS_TYPE_MUXED_ACCOUNT:
83+
MuxedEd25519Account muxedAccount;
84+
case SC_ADDRESS_TYPE_CLAIMABLE_BALANCE:
85+
ClaimableBalanceID claimableBalanceId;
86+
case SC_ADDRESS_TYPE_LIQUIDITY_POOL:
87+
PoolID liquidityPoolId;
88+
+case SC_ADDRESS_TYPE_MUXED_CONTRACT:
89+
+ MuxedContract muxedContract;
90+
};
91+
```
92+
93+
No new host functions are introduced by this CAP. The existing `get_address_from_muxed_address` and `get_id_from_muxed_address` host functions added in CAP-67 are reused, with their behavior extended to muxed contract addresses as described in the Semantics section.
94+
95+
### Semantics
96+
97+
#### `MuxedAddressObject` host object
98+
99+
CAP-67 introduced the `MuxedAddressObject` host object and a 1:n mapping between `SCVal::SCV_ADDRESS` (and the respective `SCAddress`) and the host objects. This CAP extends that mapping so that `SC_ADDRESS_TYPE_MUXED_CONTRACT` is also represented by `MuxedAddressObject`:
100+
101+
- (unchanged) `SC_ADDRESS_TYPE_ACCOUNT` and `SC_ADDRESS_TYPE_CONTRACT` correspond to `AddressObject`
102+
- (unchanged) `SC_ADDRESS_TYPE_MUXED_ACCOUNT` corresponds to `MuxedAddressObject`
103+
- `SC_ADDRESS_TYPE_MUXED_CONTRACT` corresponds to `MuxedAddressObject`
104+
- (unchanged) `SC_ADDRESS_TYPE_CLAIMABLE_BALANCE` and `SC_ADDRESS_TYPE_LIQUIDITY_POOL` are disallowed by the host
105+
106+
As with muxed account addresses, `MuxedAddressObject` is a regular host object and is not implicitly compatible with the `AddressObject` type. A contract that expects an `AddressObject` as an input argument will fail if an `SCAddress::SC_ADDRESS_TYPE_MUXED_CONTRACT` is passed to it, in the same way it fails when a muxed account address is passed.
107+
108+
The two host functions introduced in CAP-67 operate on a muxed contract address as follows:
109+
110+
- `get_address_from_muxed_address` returns the contract address part as an `AddressObject` of type `SC_ADDRESS_TYPE_CONTRACT`, stripping the multiplexing identifier.
111+
- `get_id_from_muxed_address` returns the multiplexing identifier as a `U64Val`.
112+
113+
#### `SC_ADDRESS_TYPE_MUXED_CONTRACT` is prohibited in storage keys
114+
115+
As with `SC_ADDRESS_TYPE_MUXED_ACCOUNT`, a muxed contract address is meant to support off-chain multiplexing and would by definition be a mistake to use in a contract data storage key. The Soroban host will produce an error if `SC_ADDRESS_TYPE_MUXED_CONTRACT` is present in a contract data key `SCVal` (either directly, or anywhere in the nested containers). This restriction is applied to all kinds of contract data storage, including instance storage.
116+
117+
As with muxed account addresses, a contract that wants to use the muxing information on-chain can decompose the muxed contract address into its two parts using the provided host functions and store them in a user-defined data structure.
118+
119+
#### Update the SAC `transfer` and `mint` functions to support muxed contract addresses
120+
121+
The `transfer` and `mint` functions of the Stellar Asset Contract already accept an `AddressObject` or `MuxedAddressObject` for the `to` argument (see CAP-67). This CAP requires no change to the function signatures: a `MuxedAddressObject` that wraps a muxed contract address is now a valid input for the `to` argument. If a muxed contract address is passed, the function behaves as if the corresponding non-muxed contract `AddressObject` had been passed, with the only difference being the event payload as described below.
122+
123+
#### Events
124+
125+
This CAP introduces no new event format. The `transfer` and `mint` events emitted by the Stellar Asset Contract represent a muxed contract destination using exactly the convention defined in CAP-67: the non-muxed contract address appears in the event topics as `to`, and the multiplexing identifier is carried in the `data` map as `to_muxed_id`.
126+
127+
The `transfer` event format involving a muxed contract destination is `topics: ["transfer", from:<non-muxed SCAddress>, to:<non-muxed contract SCAddress>] data: { amount: i128, to_muxed_id:u64 }`. The `mint` event format is `topics: ["mint", to:<non-muxed contract SCAddress>] data: { amount: i128, to_muxed_id:u64 }`. As with the `id` field of `MuxedContract`, `to_muxed_id` is always represented as an `SCV_U64`.
128+
129+
A muxed contract destination can only originate from an invocation of the Stellar Asset Contract `transfer` or `mint` function, so none of the classic-to-event mapping rules in CAP-67 (such as deriving `to_muxed_id` from the transaction memo) apply to muxed contract addresses.
130+
131+
#### Strkey encoding
132+
133+
A new strkey type is required to provide a textual encoding for muxed contract addresses, analogous to the muxed account strkey (`M...`). The normative definition will be added to SEP-23; this section describes the intended encoding so that implementers can begin work.
134+
135+
A muxed contract strkey follows the standard strkey structure used for all other types: the byte sequence `[version byte][payload][2-byte CRC16 checksum]` is base32-encoded to produce the textual representation. The payload for a muxed contract address is the 32-byte contract id concatenated with the 8-byte (big-endian) multiplexing identifier, for a total payload length of 40 bytes, mirroring the muxed account strkey payload (32-byte ed25519 key followed by the 8-byte identifier).
136+
137+
The version byte (and therefore the resulting human-readable prefix letter) will be allocated by SEP-23. For example, a muxed contract address with contract id `0x0000...0000` (32 bytes) and multiplexing identifier `0` has a 40-byte payload of all zero bytes; this is prefixed with the version byte and suffixed with the CRC16 checksum of `[version byte][payload]`, and the resulting 43-byte sequence is base32-encoded.
138+
139+
## Design Rationale
140+
141+
### Parity with CAP-67
142+
143+
The design deliberately mirrors the account multiplexing design from CAP-67 rather than introducing a new mechanism. Reusing the `MuxedAddressObject` host object, the existing accessor host functions, and the `to_muxed_id` event convention means that SDKs, indexers, and other downstream consumers handle muxed contract addresses with the same machinery they already use for muxed account addresses. The only genuinely new artifacts are the `SCAddress` variant and the strkey encoding.
144+
145+
### Reusing `MuxedAddressObject` rather than adding a new host object
146+
147+
Because the two host functions that operate on `MuxedAddressObject` (`get_address_from_muxed_address` and `get_id_from_muxed_address`) already return the generic `AddressObject` and `U64Val` respectively, they apply to muxed contract addresses without modification. Introducing a separate host object for muxed contracts would duplicate this surface for no functional benefit.
148+
149+
### Destination-only support
150+
151+
This CAP only enables a muxed contract address to be used as the destination of a `transfer` or `mint`, matching CAP-67, which only emits multiplexing information for the destination (`to_muxed_id`) and never for the source of a transfer. Multiplexing the source of a transfer is not part of CAP-67 and is intentionally out of scope here; it can be added by a future CAP if a need arises.
152+
153+
### Limiting scope to `transfer` and `mint`
154+
155+
The Stellar Asset Contract `transfer` and `mint` functions are the same functions that CAP-67 updated to accept a muxed account destination. Extending support to additional functions that take a destination argument, such as `transfer_from`, was considered but deferred to keep contract multiplexing symmetric with account multiplexing. Should a future need arise, support can be added by a subsequent CAP.
156+
157+
### Strkey defined in SEP-23
158+
159+
Strkey encodings are defined normatively in SEP-23, so the authoritative version byte, prefix letter, and validation rules for muxed contract addresses belong there rather than in this CAP. This CAP describes the intended encoding so that the strkey can be implemented in parallel, but defers the final definition to a companion SEP-23 change.
160+
161+
## Protocol Upgrade Transition
162+
163+
On the protocol upgrade, the `SC_ADDRESS_TYPE_MUXED_CONTRACT` variant becomes a valid `SCAddress`, the `MuxedAddressObject` host object and its accessor host functions begin accepting muxed contract addresses, and the Stellar Asset Contract `transfer` and `mint` functions begin accepting a muxed contract address as the destination. The new host behavior uses the standard mechanism for protocol-gating, so a muxed contract address cannot be constructed or processed by the host before the protocol version that includes this CAP.
164+
165+
### Backwards Incompatibilities
166+
167+
This CAP does not introduce any backward incompatibilities. The `SC_ADDRESS_TYPE_MUXED_CONTRACT` variant is additive, and before the protocol upgrade it is not possible to upload Wasm that constructs or processes a muxed contract address. Downstream consumers that parse `SCAddress` will need to handle the new variant and the new strkey type.
168+
169+
### Resource Utilization
170+
171+
The change has a negligible impact on resource utilization. A muxed contract address is 8 bytes larger than a non-muxed contract address, and the host operations on it lean on the existing metering primitives used for muxed account addresses.
172+
173+
## Security Concerns
174+
175+
This CAP does not change the on-chain authorization or balance semantics of a transfer: a muxed contract address behaves identically to its underlying non-muxed contract address, with the multiplexing identifier serving only to convey off-chain routing information in the event payload. The storage-key prohibition prevents a muxed contract address from being inadvertently persisted as part of contract data, consistent with the treatment of muxed account addresses in CAP-67.
176+
177+
## Test Cases
178+
179+
TBD
180+
181+
## Implementation
182+
183+
TBD

0 commit comments

Comments
 (0)