Send to and receive payments from Contract Accounts
Payments between Contract Accounts (C addresses) and Classic Accounts (G addresses) are supported on a protocol level, but there are differences in the implementations that are required to support them. This guide will cover implementations for applications that use classic G accounts and want to support transacting with Contract Accounts.
Receiving payments from Contract Accounts (C to G)
If a sender is using a Contract Account, and sending an SAC token (such as XLM, USDC, etc.), the transaction will be received by the G account normally since it is supported on a protocol level.
Receiving payments from Contract Accounts requiring a memo
Entities such as exchanges and asset issuers usually use omnibus accounts, using a shared receiving address and a unique identifier memo like:
- Account:
GBLVHX33XGOBDOXK7ERDL34NVH6WW7VTT2OBAHPJ7G3D423HBG5NOMY7 - Memo ID:
123456789
Transactions involving Contract Accounts make use of contract invocations to execute a 'transfer' function on the asset contract. This kind of transaction doesn't allow for the 'memo' field to be used, so instead, the sending app will encode it in the address, generating a unique muxed address that includes the receiving account (GBLVHX…) and provided id in the muxed id field (123456789).
To enable support for deposits coming from Contract Accounts, nothing needs to change in the information displayed to customers or the deposit accounts used by the exchange. Wallets will still use the G account and memo provided to send a payment to the exchange's G account including the memo with the unique ID.
That's the main difference between receiving from customers using G or Contract Accounts: instead of looking at the memo field to identify the individual account, you'll look at the muxed ID. Everything else stays the same.
Example using Horizon
Receiving from a G address
Using Horizon's accounts/{acc_id}/transactions endpoint (example)
The memo field will be present in the response.
Example response from a G address:
{
"memo": "123456789",
"memo_bytes": "MTIzNDU2Nzg5",
"source_account": "GCNG5JXJY3LNRMXCX23RIGKTURQACTSV5LL6NKL535BYRGOGWUX6J45Y",
"memo_type": "text"
}
Receiving from a Contract Account
Using Horizon's accounts/{acc_id}/payments endpoint (example)
After the transaction is processed, this payment detail will be present in the '/payments' endpoint, under a section called 'asset_balance_changes', which will present the encoded ID and the underlying G-address separately.
It is important to note the 'asset_balance_changes' section only supports Stellar assets and transactions that make use of the transfer function in the Stellar Asset Contract (SAC).
Example response from a Contract Account:
{
"asset_balance_changes": [
{
"asset_type": "native",
"type": "transfer",
"from": "CBP4GFAK4GDKCMVLNIHRZPEAPT7CFYTBKICOXCVMP2FEN3QFKCRZ27KS",
"to": "GBLVHX33XGOBDOXK7ERDL34NVH6WW7VTT2OBAHPJ7G3D423HBG5NOMY7",
"amount": "1.0000000",
"destination_muxed_id": "123456789"
}
]
}
For a detailed code example, see the section 'Monitoring Payments -> Using the Horizon API' of this repository's examples.
Example using RPC
Since the release of the Unified Asset Events (CAP-67) on Protocol 23, a number of transactions involving Stellar assets now emit standardized events that can be ingested by both the Horizon API and the Stellar RPC. If funds are sent through a 'payment' operation or by invoking the 'transfer' function of the asset contract, the same kind of event is emitted for both cases.
To monitor these payments, the method 'getEvents' of the Stellar RPC is used to stream events emitted by the Stellar Asset Contract. A filter can be used to specify the target asset (SAC contract ID) and accounts.
From a G address
When the receiver is a normal G address, the value portion of the event is just the amount received.
{
"inSuccessfulContractCall": true,
"topicJson": [
{
"symbol": "transfer"
},
{
"address": "GD6MYLASTCZ3H4UFLH2YSIASHJNBFUCKC5YHGU44VCH2BGHN3WJQX6LG"
},
{
"address": "GA4PDUF3BLBSK47UTNY44AIZAP5EPHW2UIJRKB5HN76HHC55IWKSIHFD"
},
{
"string": "native"
}
],
"valueJson": {
"i128": "10000000"
}
}
From a Contract Account
When the transaction is sent using a muxed account, the value portion of the event is broken down into an object containing the amount and the unique ID encoded in the address.
{
"inSuccessfulContractCall": true,
"topicJson": [
{
"symbol": "transfer"
},
{
"address": "GD6MYLASTCZ3H4UFLH2YSIASHJNBFUCKC5YHGU44VCH2BGHN3WJQX6LG"
},
{
"address": "GA4PDUF3BLBSK47UTNY44AIZAP5EPHW2UIJRKB5HN76HHC55IWKSIHFD"
},
{
"string": "native"
}
],
"valueJson": {
"map": [
{
"key": {
"symbol": "amount"
},
"val": {
"i128": "10000000"
}
},
{
"key": {
"symbol": "to_muxed_id"
},
"val": {
"u64": "123456789"
}
}
]
}
}
For a detailed code example, see the section ‘Monitoring Payments -> Using the Stellar RPC’ of this repository's examples.
Sending payments from Contract Accounts to a recipient that requires a memo
If a sender is using a Contract Account, and sending an SAC token (such as XLM, USDC, etc.) to a recipient that requires a memo, (an exchange, for example) it should create a muxed address that includes the receiving account (GBLVHX…) and provided memo id in the muxed id field (123456789). In the user inteface, the resulting memo address doesn't need to be surfaced to the user, it should be used directly in the transaction.
If the receiving party already provides an M address, it should be used directly, without requiring a memo field. Since this is the value provided by the user, it should be used in the transaction and displayed to the user.
Sending payments to Contract Accounts (G to C)
Sending a ‘transfer’ via Stellar RPC
This approach is simpler but requires the use of an RPC and involves a slightly different transaction workflow than a traditional native operation. For a detailed code example, see the section ‘Sending Payments through the SAC -> Using the Stellar RPC’ of this repository's examples.
Assembling the operation and transaction
To perform a ‘transfer’ through an SAC, it is necessary to assemble a ‘InvokeHostFunctionOp’ operation with similar arguments as a ‘payment’ operation along with the contract identifier and target function name(‘transfer’).
This can be seen in detail in the ‘assembleTransferOperation’ function of the demo. While the example demonstrates this process in greater detail, it is possible to use the js-stellar-sdk and its Contract client in a simpler manner.
The transaction is then built by adding this operation and configuring the additional parameters as any other ‘payment’ transaction.
Simulating the transaction
After building the transaction, the RPC method for ‘simulateTransaction’ is used to simulate an execution of this transaction against the current state of the network, identify if it is expected to succeed and, also provide a number of additional parameters about this execution.
This process is fully handled by the SDK in a very simple step by using the ‘prepareTransaction’ function of the RPC server client. It automatically simulates and provides an updated transaction object. See this process in detail in the demo.
Signing
When the same account is used as the ‘sender’ of the funds and also the ‘source’ of the transaction, the signing step will be exactly the same. Once the updated transaction object is returned from the previous step, you just sign it with the source/sender account and it will be ready.
When the ‘source’ account of the transaction differs from the ‘sender’ account, an additional signed entry will be necessary to authorize the ‘transfer’ invocation explicitly. Refer to the Soroban Authorization Framework documentation for further details.
Sending the Transaction for processing
When a transaction is submitted for processing through the Stellar RPC, differently from the Horizon API, it will resolve immediately confirming its submission but not the processing. It is necessary to poll the transaction status for the next moment until you get a confirmation that it was successfully processed or a status update on a potential failure.
Sending a ‘transfer’ via Horizon API
When sending a ‘transfer’ via the Horizon API, this approach introduces a few extra steps when compared to the use of an RPC. For a detailed code example, see the section ‘Sending Payments through the SAC -> Using the Horizon API’ of this repository's examples.
Assembling the operation and transaction
This process is mostly the same as described in the previous section ‘Sending a ‘transfer’ via Stellar RPC’ with some slight modifications. Since the Horizon API does not provide an endpoint to simulate a transaction execution, this means that when the transaction is assembled we need to ensure it also contains:
- The Soroban authorization entries that fulfill the smart contracts authorization requirements.
- The Soroban data object, detailing the footprint, resources, and resource fee expected for this contract invocation.
These are normally populated automatically based on the output of the simulation and by simply signing and adding the authorization entries generated by the RPC. Here, we need to include these manually.
The auth entries can be easily predicted and assembled based on the configuration. Assuming the ‘sender’ account is the same as the ‘source’ account of the transaction, a source-account credential needs to be added to the operation, indicating this invocation will be used the same envelope signature to authorize the ‘transfer’. See this process in detail in the ‘assembleSourceAuthEntry’ function of the demo.
Once the auth entries are assembled and signed(when not a ‘source-account’ entry), they need to be included in the operation, in the ‘auth’ parameter.
For the Soroban Data on the other hand, the process can be a bit harder to execute manually. Considering all SAC contracts use the same native implementation, we can expect all ‘transfer’ invocations to be very similar.
The amount of resources consumed shouldn't vary much with the exception of some edge cases such as the transaction involving archived entries or a custom authorization for a smart wallet as the sender. See this process of manually assembling the Soroban Data in the function ’getSorobanData’ of the demo.
Once the Soroban Data is assembled, it is included in the Transaction Builder.
Signing
After building the transaction, assuming the ‘auth entries’ from the previous step were configured correctly, the transaction can be signed normally by the source account.
Sending the Transaction for processing
This step remains the same as any transaction submitted through the Horizon API.
Guides in this category:
📄️ Create an account
Learn about creating Stellar accounts, keypairs, funding, and account basics.
📄️ Send to and receive payments from Contract Accounts
Learn to send payments to and receive payments from Contract Accounts on the Stellar network.
📄️ Send and receive payments
Learn to send payments and watch for received payments on the Stellar network.
📄️ Channel accounts
Create channel accounts to submit transactions to the network at a high rate.
📄️ Claimable balances
Split a payment into two parts by creating a claimable balance.
📄️ Clawbacks
Use clawbacks to burn a specific amount of a clawback-enabled asset from a trustline or claimable balance.
📄️ Fee-bump transactions
Use fee-bump transactions to pay for transaction fees on behalf of another account without re-signing the transaction.
📄️ Sponsored reserves
Use sponsored reserves to pay for base reserves on behalf of another account.
📄️ Path payments
Send a payment where the asset received differs from the asset sent.
📄️ Pooled accounts: muxed accounts and memos
Use muxed accounts to differentiate between individual accounts in a pooled account.
📄️ Install and deploy a smart contract with code
Install and deploy a smart contract with code.
📄️ Invoke a contract function in a transaction using SDKs
Use the Stellar SDK to create, simulate, and assemble a transaction.
📄️ simulateTransaction RPC method guide
simulateTransaction examples and tutorials guide.
📄️ Submit a transaction to Stellar RPC using the JavaScript SDK
Use a looping mechanism to submit a transaction to the RPC.
📄️ Upload WebAssembly (Wasm) bytecode using code
Upload the Wasm of the contract using js-stellar-sdk.