Stellar has its distributed exchange for trading currencies and other assets. It has a public orderbook with passive orders. So far so good.

But in many use cases, financial institutions prefer OTC (over the counter) trading a.k.a. off-exchange trading.

I wonder, how the settlement part for OTC trades of Stellar assets could be done "on chain" i.e. on the Stellar ledger. Can it be done with a single transaction -- or is a 3rd party escrow account needed?


  • Alice and Bob do OTC trading and agree in a chat to swap Stellar assets: 1000 anchorX USD for 900 anchorY EUR .

I guess the following approach does not work, because step one is not feasible:

  1. Alice creates a transaction with two payment operations (1: pay 1000 USD from Alice to Bob, 2: pay 900 EUR from Bob to Alice)
  2. Alice signs the transaction
  3. Alice sends the transaction to Bob.
  4. Bob signs the transaction
  5. Bob submits the transaction to the Stellar network.
  • What part of step 1 isn't feasible? That seems like one way it could work.
    – Paul
    Sep 4 '18 at 20:41
  • Valid question. A quick look at payment operation documentation gave me the impression that I cannot define a source account. But after checking the code examples I learned that source account can also be a parameter.
    – Roman
    Sep 4 '18 at 20:48

Your answer is correct, except that the thing you say is impossible is actually easy. A Stellar transaction can contain up to 100 operations, and each operation can have its own source account. So it's trivial to form a transaction in which A pays some asset to B and B pays some asset to A. Because the transaction is atomic, either both will execute or neither will.

There's a tiny asymmetry in that some account actually has to pay the transaction fee, but this is easy to compensate for if you even care (transaction fees are negligible).

Another thing to watch out for is that Bob could sit on the transaction for a while. So best to have the first signer, Alice, use her account so she can burn the sequence number if she loses patience. An alternative would be to place timebounds in the transaction before the first party signs. Yet another option would be for Alice to rotate through signing keys, if she signs a lot of OTC transactions like this and wants to flush the queue at the end of each day.


If you have both asset (USD and EUR) on Stellar, there's no problem at all.

const {Server, Network, Keypair, TransactionBuilder, Operation, Asset} = require('stellar-sdk')


const server = new Server('https://horizon-testnet.stellar.org'),

//suppose we want to exchange two assets issued by the same anchor

// load details for both accounts
    .then(([aliceAccount, bobAccount]) => {
        // here we can check if both accounts have enough funds for exchange
        // if (aliceAccount.balance...)

        // start building the transaction
        // Alice will be the source account in this case and tx fees will be charged from her account
        let transaction = new TransactionBuilder(aliceAccount)
                source: aliceAddress,
                destination: bobAddress,
                asset: usd,
                amount: '1000'
                source: bobAddress,
                destination: aliceAddress,
                asset: eur,
                amount: '900'

        // now we need to sign the transaction using both accounts' private keys
        const aliceKeys = Keypair.fromSecret('SBFIZKNU2AZWKC7C273IGQ4A2IBQJEDHZCNFQKB2BTT4FJKLATML3TYE'),
            bobKeys = Keypair.fromSecret('SDKU7OHIEJ6YPU3M3CW446L3ZEPGYPO7MNSO352HG6R4N6JVBQ2WXXWP')


        return server.submitTransaction(transaction)
    .then(res => console.log('Results:', res))
    .catch(error => console.error(error))

The step with signing could be deferred. You generate the transaction, send it to Alice, she signs and send it back to you. Then you send the same tx to Bob, he signs it as well. Once both participants signed the tx, it can be submitted to network. The signatures can be gathered simultaneously and then merged into the tx envelope.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.