Allow SAC to create G-account balances
Specification
CAP: 0073
Title: Allow SAC to create G-account balances
Working Group:
Owner: Dmytro Kozhevin <@dmkozh>
Authors: Dmytro Kozhevin <@dmkozh>
Consulted: Leigh McCulloch <@leighmcculloch>
Status: Final
Created: 2025-09-18
Discussion: https://github.com/orgs/stellar/discussions/1668
Protocol version: 26
Simple Summary
Add Stellar Account contract (SAC) functions for managing trustlines of Stellar (G-)accounts and create new G-accounts on XLM transfer.
Working Group
As specified in the Preamble.
Motivation
Stellar Asset contract allows moving the Stellar assets between any accounts (both classic G-accounts and custom C-accounts), but Soroban doesn't provide a way to create a new G-account balance. This behavior is not consistent with the contract (C-)accounts, for which the balances are created automatically on the first transfer.
The feature imparity is tolerable to some degree, because a G-account can create a trustline via a classic operation, and a new G-account itself can be created via a separate operation. While the workaround provides a way for using SAC for G-accounts after some 'classic'-side setup, it comes with some issues, such as:
- It's not possible to create an trustline and perform a SAC transfer to it atomically as classic operations can't be mixed with Soroban operations
- It's tricky for Soroban-based apps to provide programmatic support for the missing trustlines, as bespoke processing is necessary. The 'simplest' option is to bubble up the error and ask the user to establish a trustline, and even that is not trivial to support
By allowing the trustline and account creation from within Soroban the protocol will resolve these feature gaps and improve the user and developer experience for any use case that needs to deal with SAC.
Goals Alignment
This CAP is aligned with the following Stellar Network Goals:
- The Stellar Network should make it easy for developers of Stellar projects to create highly usable products
Abstract
This CAP introduces a new Stellar Asset contract function trust for creating trustlines. The trust function creates an unlimited trustline for a G-account if it does not already exist. The trustline creation semantics are the same as for the existing ChangeTrustOp. Account authorization is required for actually creating the trustline, but not for calling trust.
This also updates the transfer and transfer_from functions specifically for the 'native' asset (i.e. for the XLM SAC). When an XLM transfer is coming to a G-address that doesn't have a corresponding account entry yet, the account will be created as long as the transfer amount covers the minimum allowed account balance.
Specification
New SAC functions
The following new functions are added to the Stellar Asset contract:
/// Creates this contract asset's unlimited trustline for the provided address.
///
/// This is no-op if the input address is a C-address, or if provided G-address
/// already has the respective trustline.
///
/// If the trustline is actually created, this will require
/// authorization from `address` (i.e. `address.require_auth` will be called).
///
/// Panics only during trustline creation if the asset issuer does not exist, or
/// when a new trustline can not be created.
fn trust(env: Env, address: Address);
Semantics
trust function
When trust SAC function creates a new trustline, it follows the semantics of ChangeTrustOp with the respective input arguments. There are only a few Soroban-specified adjustments:
- The asset for which the trustline is managed is implied by the SAC instance for which
trustis called- Thus, unlike for
ChangeTrustOp, only the regularAssettrustlines can be created, as liquidity pool shares don't have a SAC associated with them
- Thus, unlike for
- There is no fine control over the limits, a new trustline with limit
i64::MAXis created if no trustline exists for a given address. Limit is not modified otherwise - Input
addresscan be a C-address, but passing it is always a no-op require_authis called to performaddressauthorization for creating a trustline, which means that all the Soroban auth features are available- Sponsorship is not supported for creating a new trustline, so the address must have sufficient XLM balance in order to be able to create a new trustline
Besides these differences, the trustline creation semantics are directly translated from ChangeTrustOp. Here is a quick summary:
- If the asset issuer does not exist, the function panics
- If trustline does not exist, it is created with
i64::MAXlimit- This increases the number of sub-entries (and thus base reserve) of the account that owns the trustline and thus is subject to the existing protocol limitations for creating the classic sub-entries
- Sponsorship is not compatible with Soroban and thus base reserve may only belong to the trustline owner
- Creating a trustline to account's own asset will result in panic
XLM transfer creates AccountEntry
When an XLM transfer is performed to a G-address that does not exist yet (i.e. it does not have a respective AccountEntry), Soroban host will attempt to create a new AccountEntry for it. Specifically, it will create a new AccountEntry with the full transfer amount as balance if the transfer amount is at least the minimum account balance (1 XLM as of this CAP). Otherwise, it will panic.
If the transfer destination is M-address, its G-address part will be considered to perform the logic described above.
This change affects both SAC functions that perform transfers: transfer and transfer_from.
The newly created account will immediately become available to interact with, including using it to perform authorization via require_auth.
Design Rationale
Account base reserve management from Soroban
This CAP introduces a way to create and remove the classic trustline entries from Soroban. Most of the design coincide with those for the G-account signer management in CAP-72. Signers could in theory be stored in a contract data entry and that would require a relatively small amount of changes. However, if Soroban created trustlines only as contract data entries, then almost every classic operation would need to be modified to account for these entries. Thus, that alternative is even less feasible than in the case of CAP-72.
trust function vs automatic trustline creation
Transfers to accounts without a trustline could initiate trustline creation in a similar fashion to the account entry creation in case of XLM SAC. However, that would likely result in confusing user experience: an additional signature from the transfer receiver would be required (in order to approve the trustline creation). It's not generally expected for the transfer to expect the receiver signature, and that scenario would also happen very rarely in a general case, so there is a high chance for developers to miss it.
trust function allows contract developers to explicitly request trustline creation from the users. That requires a conscious effort to support the trustline creation behavior, which reduces the chance of the additional required signature payload to go unnoticed.
trust function granularity
Over the last year over 98% of the active trustlines had limit over 1e18 (effectively unlimited, 95+% of trustlines are also at int64 max), so unlimited trust is going to be sufficient most of the time. For the remaining small amount of cases, it is not likely that there is a good generalized approach to managing the exact trustline limit on the contract side, so it's not obvious that a more granular function would actually end up being useful.
No trustline creation events
There is no protocol-defined event for the trustline creation and thus trust function will not emit any events.
Muxed account support
Since transfer function has support for muxed destinations, we need to take that into account for the purpose of this CAP. We create an underlying G-account in order to keep the semantics consistent for all kind of transfers (muxed id part normally has no impact on the on-chain contract logic beyond the events). The transfer will then proceed to emit a regular transfer event with to_muxed_id populated according to the existing semantics.
In case of the new trust function, muxed addresses are not supported. Even though trustline creation may be necessary to be performed before the actual transfer, there is no reason to pass a muxed address into the creation function itself. There is no event to be emitted that would contain the muxed id, and that would be generally inconsistent with the SAC interface that only supports muxed destination specifically for transfers.
Backwards Incompatibilities
This CAP does not introduce backward incompatibilities.
Resource Utilization
The new contract functions will have the appropriate metering. No new cost types need to be introduced, as the operations can lean on the existing metering primitives.
Security Concerns
Test Cases
TBD
Implementation
TBD
Preamble
Discussion
0 linked threads