Stellar Escrow Smart Contract Development
Create your first escrow smart contract on the Stellar network.
Before reading this tutorial, you should have a basic understanding of how Stellar works and how to create a simple account on the test network. Take a look at my previous article in this series to get you updated.
This article will explain to you how to develop an escrow smart contract using Stellar Lumens. I will as well highlight some extra features like retrieving a balance and a clean history log.
Extra note: This article is part of the Blockchaingers series. Together with TheLedger, we have won the ‘Digital Nations Infrastructure’ track at the largest blockchain hackathon. You can find out more about our idea here. Escrow smart contracts on the Stellar testnet are part of this prototype.
Use Case Description
We have two identities: a house and a contractor. The house can pay a contractor for delivering house related services like a check-up of your central heating. Once the house and contractor agree to deliver a service, the house will deposit the agreed amount (in XLM) into the escrow contract. Once the job is done, both the house and contractor have to sign to release the funds.
Preparation
First, we need a new, empty account on the testnet. Let’s create one.
const newKey = Stellar.Keypair.random();const transaction = new Stellar.TransactionBuilder(ownerAccount)
.addOperation(Stellar.Operation.createAccount({
destination: escrowPubKey,
startingBalance: '2.5000000'
}))
.build();transaction.sign(ownerKeypair);return StellarConfig.server.submitTransaction(transaction);
We still define an owner account who is creating the escrow but is not able to perform any actions with it. We use a config file for retrieving our server, this piece of code is the same as: new Stellar.Server('https://horizon-testnet.stellar.org');
.
Probably, you wonder why I’m sending 2.5 XLM to the escrow? Each account needs a starting balance of 1 XLM. In addition, we are adding two more signers besides the random escrow signer. For each additional signer, you have to add 0.5 XLM. So, …
3 x 0.5 + 1 = 2.5
This is defined in the Stellar documentation.
Building Stellar Escrow Transaction
Let’s retrieve the escrow account,
const escrowAccount = StellarConfig.server.loadAccount(pubKey);
and build the escrow transaction.
let transaction = new Stellar.TransactionBuilder(escrowAccount)
.addOperation(Stellar.Operation.setOptions({
signer: {
ed25519PublicKey: houseKeypair.publicKey(),
weight: 1
}
}))
.addOperation(Stellar.Operation.setOptions({
masterWeight: 0,
lowThreshold: 2,
medThreshold: 2,
highThreshold: 2,
signer: {
ed25519PublicKey: contractorKeypair.publicKey(),
weight: 1
}
}))
.build();
As you can see, we add two signers to the escrow contract. We give both signers equal voting power (1) and set the threshold to two. Because we are not giving the escrow account itself an explicit weight, this is set to zero. This means both the house and contractor have to sign to release the funds. You can see an example of an escrow smart contract transaction creation here.
At last, we need to sign (with the random keypair) and send the transaction to the network.
transaction.sign(newKey);
await StellarConfig.server.submitTransaction(transaction);
Ideally, the house sends the agreed amount to the smart contract. The code for sending the transaction looks like this.
memo = Stellar.Memo.text('Pay: House to Contractor');
return new Stellar.TransactionBuilder(<source-account>, { memo })
.addOperation(Stellar.Operation.payment({
<destination-pub-key>,
asset: Stellar.Asset.native(),
<amount>}))
.build();
Release Funds
Releasing the funds is actually very simple. You create a new transfer transaction from the escrow account to the contractor. The only difference here is that both the house and contractor have to sign.
transaction.sign(houseKeyPair);
transaction.sign(contractorKeyPair);
Additional Operations
Retrieve History For Account
This will give you a nice list of all payments a certain account has executed. We remove the first result (with shift()
)from the array as that’s the account creation (0 payment to itself).
async retrievePayments(pubKey:string) {
let account = await this.loadAccountAsync(pubKey);
let payments = await axios.get(`${StellarConfig.baseUrl}/accounts/${account.accountId()}/payments`); let paymentRecords = payments.data._embedded.records;
paymentRecords.shift(); return paymentRecords.map(record => {
return {
id: record.transaction_hash,
from: record.from,
to: record.to,
amount: record.amount
};
});
}
You can use the HTTP API endpoint /accounts/<account-ID>/payments to retrieve this in your web browser as well. You’ll get something like:
Get Account Balance
To retrieve the XLM balance of an account, you just have to load the account based on its public key. As an account can have multiple balances (native XLM and other coins deployed on the Stellar network), we will only look for the native balance.
async getBalance(pubKey) {
const account = await StellarConfig.server.loadAccount(pubKey);
let balance; account.balances.forEach((balanceObject) => {
if (balanceObject.asset_type === 'native') {
balance = balanceObject.balance;
}
}); return balance;
}
What to read next
- Exploring Stellar Lumens | Introduction Development Tutorial
- Blockchaingers hackathon 2018 | Aftermath
- Our solution for the Blockchaingers hackathon
— — — — — — — — — — — — — — — — — — — — — — — — — — — — -
NEXT ARTICLE IN THIS SERIES→ The tech-stack to win the worlds biggest blockchain hackathon of 2018!