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.
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 smart contract. Once the job is done, both the house and contractor have to sign to release the funds.
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 have to define an owner account who is responsible for 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');
which creates a new connection with the Stellar testnet.
Probably, you wonder why I’m sending 2.5 XLM to the escrow? Stellar reequires that each account has a starting balance of 1 XLM. In addition, we are adding two more signers besides the random escrow signer. For each signer you add to the contract, you have to top up the starting balance with 0.5 XLM. So, …
3 x 0.5 (signers) + 1 (base balance) = 2.5
Additional info can be found in the Stellar documentation.
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’ve added 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, not the escrow owner. You can see an example of an escrow smart contract transaction creation here.
Stellar Escrow Contract
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 escrow smart contract. The code for sending the top up 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();
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 with their keypair.
transaction.sign(houseKeyPair);transaction.sign(contractorKeyPair);
This will give you a 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 transaction (a 0 XLM 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:
One payment object (history) for an account.
To retrieve the XLM balance of an account, you just have to load (retrieve) 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;
}
Source: https://www.in3dc.com