Fractional Auction Listing for Real Estate Investing
Overview
This is the follow-up from my previous article Using Assets and Smart Contracts in Real Estate. Here, I will describe new concepts including stateless and stateful smart contracts, atomic transfers and we will go through the implementation of the FAL (Fractional Auction Listing) Application using the Algorand Blockchain and goal
CLI. This solution is intended for learning purposes only. It does not cover error checking and other edge cases therefore, should not be used as a production application.
FAL - Fractional Auction Listing
If you already read my previous article, you know that fractional ownership or fractional investing is a way to buy partial shares, securities or units of an asset. Instead of waiting until you have the total amount necessary to buy the entire asset (a house for example) you are interested in the seller or the brokerage can split that security among other investors. Doing so, you can maximize your portfolio’s rate of return (RoR) while providing steady income. Investors in Real Estate Fractional Investing can buy, hold, and sell fractional ownership in real estate assets, similar to trading stocks or cryptocurrencies. These assets can range from apartments, houses, multifamily units to commercial real estate, hospitals, malls, and more. Moreover, Real Estate Fractional Ownership gives investors all the benefits of real estate classic investing like stable dividends from rent and capital appreciation, diversification in recessions, above-average returns without the hassle of property management.
FAL - Prerequisites
Now let’s go through the prerequisites that our FAL app will need in order to function properly.
-
FAL - listingID
First, the Owner needs to specify the property (asset) for the Fractional Auction Listing. For consistency we will use aslistingID
, the asset ID we created in the previous tutorial. We can refer to this ASA available on the Algorand blockchain listingID=71054280. -
FAL - targetPrice, startAuctionDate, endAuctionDate
The owner will need to specify information such as the target listing price and the Start and End Auction date. ThestartAuctionDate
defines when the Auction for that property becomes open for investments. ThetargetPrice
is the price the Owner is willing to sell his asset and if the target price is reached. The owner can decide to terminate the Auction early otherwise the Auction will conclude when theendAuctionDate
is reached. -
Escrow Account
Each Fractional Auction will need an escrow account (escrowAccount
) to hold the payments collected from the investors. The escrow account will accumulate and hold the investments from when the Auction starts until the Auction end date. This account, will run a stateless smart contract and will allow any accounts to send Algos or other Assets to the escrow using the Atomic Transfers.
The FAL Application will use a stateful smart contract because we need to persist some application values (Global Storage) and some user-specific values (Local Storage) for each account participating in the contract.
FAL - Functionalities
The following Use Case diagrams explain the common scenarios for the Owner and the Investor using the FAL Application. From these diagrams you can see that Atomic refers to those operations that will involve a call to the stateful smart contract and a payment or asset transaction.
The Owner should be able to:
-
Create FAL: The Owner needs to set the
listingID
,targetPrice
,beginDate
,endDate
andcloseoutDate
. TheownerAddress
andreceiverAddress
also need to be stored. TheownerAddress
is stored only to allow that address to modify or delete the smart contract. -
Update FAL: In order to link the stateful smart contract to the escrow account, the owner needs to update the stateful smart contract after its creation with the
escrowAddress
. In particular, you will need to use theapplicationID
returned from the stateful smart contract creation and modify the escrow account code to add the specific value. This will return theescrowAddress
that needed for the Update function. -
Withdraw Funds: After
endDate
, if thetargetPrice
is met, thereceiverAddress
should be able to receive the total funds from theescrowAddress
. This will be possible with a payment transaction from theescrowAddress
to thereceiverAddress
. -
Delete FAL Application: After the
closeDate
, the owner will need to delete the terminated FAL and all funds in theescrowAddress
should be transfered to thereceiverAddress
.
The Investors should be able to:
-
Optin and Invest in a FAL: Before an investor starts funding in the FAL Application, the account must first opt into the application and the investment can be accepted after the
startDate
and not after theendDate
. -
Reclaim Investment: Investors should be able to recover their investment after the
endDate
if thetargetPrice
has not been met.
For the purpose of this solution, we will use bash
and goal
command line to create, update, send transaction and delete the Application. As an exercise, you could try to replicate the following solution using the SDK provided.
Create FAL Application
To create our first FAL Application, we will use the goal app create
command which creates a transaction to the blockchain, and it will return the applicationID that we will need to pass during the Update function to link the escrow account. Before creating our stateful smart contract, the code for the ApprovalProgram and the ClearStateProgram should be written. These two programs are responsible for the The Lifecycle of our Stateful Smart Contract. You can find and download them from this github repo . Please refer to TEAL Reference Guide and TEAL Opcodes to understand how to write TEAL and the opcodes available. There is also an example of Boilerplate for Stateful Smart Contract for you to have a look.
export DATE=`date "+%Y%m%d-%H%M%S"`
# get timestamp in seconds for the startDate
export FALSD=$(DATE '+%s')
# we allow 30min for investors to place their bid
invs=1800
invss=2500
# FAL endDate
export FALED=$(( $FALSD + $invs ))
# FAL closeDate
export FALCD=$(( $FALED + $invss ))
# FAL listingID https://algoexplorer.io/asset/71054280
export LISTINGID="71054280"
export ADDR_CREATOR="YOURACCOUNTIDENTIFIERGOESHERE"
export TEAL_APPROVAL_PROG="approval_program.teal"
export TEAL_CLEAR_PROG="clear_state_program.teal"
export GLOBAL_BYTESLICES=3
export GLOBAL_INTS=6
export LOCAL_BYTESLICES=0
export LOCAL_INTS=1
goal app create --creator $ADDR_CREATOR \
--approval-prog $TEAL_APPROVAL_PROG \
--clear-prog $TEAL_CLEAR_PROG \
--global-byteslices $GLOBAL_BYTESLICES \
--global-ints $GLOBAL_INTS \
--local-byteslices $LOCAL_BYTESLICES \
--local-ints $LOCAL_INTS \
--app-arg "int:$LISTINGID" \
--app-arg "int:$FALSD" \
--app-arg "int:$FALED" \
--app-arg "int:1000000" \
--app-arg "addr:$ADDR_CREATOR" \
--app-arg "int:$FALCD" \
-d ~/node/data
The ADDR_CREATOR
is the account that is creating the application and this transaction is signed by this account.
For stateful smart contracts, there is a limit of 64 key-value pairs that can be used by the contract for global storage and 16 key-value pairs that can be used for local storage. Remember that when creating the smart contract the amount of storage can never be changed once the contract is created.
Here we are defining nine global variables (three byte slices and six integers) and one local storage variable (integer). The byte slices store the addresses while the global integers store the timestamps for the start and end dates, the listing ID, target price, and current fund total. The local integer stores the amount of a specific investor.
Once this command is run the result will be the app ID which can then be used to make ApplicationCall transactions to the smart contract and update the our Application.
Created app with app index 1111
Please refer to the official documentation on how to create a stateful smart contract and how to create your first application.
Update FAL Application
Anyone at any time can Update the Stateful Smart Contract. In our application however we want only the original Owner to be able to update the program and in this case some additional code must be added to our ApprovalProgram to handle this situation.
export APP_ID="1111"
export ESCROW_ADDR="ESCROWADDRESSACCOUNT"
goal app update --app-id=$APP_ID \
--from $ADDR_CREATOR \
--approval-prog $TEAL_APPROVAL_PROG \
--clear-prog $TEAL_CLEAR_PROG \
--app-arg "addr:$ESCROW_ADDR"
With this command, we are updating the global state of our application and adding the escrow account. In the ApprovalProgram we will need to add the logic to verify that:
- the Owner of the stateful smart contract is making the update call,
- one parameter is passed in (the escrow address),
- And it will store the address in the application’s global state (
escrowAddress
).
Please refer to Update Stateful Smart Contract for more information about this command and the TEAL ApprovalProgram.
Optin and Invest
Because our application is using local state, any account including the creator of the smart contract, before start making Application Transaction calls that use local state, it must optin first to the smart contract. This can be done with the goal
CLI:
export INVESTOR_ADDR="INVESTORADDRESSACCOUNT"
goal app optin --app-id $APP_ID \
--from $INVESTOR_ADDR
The ApprovalProgram will check the Transaction Type to verify it is optin. Please refer to the official documentation for all different transaction types.
After optin into the application it will be possible to invest. The invest operation requires two transactions. The first one is the stateful smart contract call using the string parameter containing the word “invest”, and the second is the payment transaction to the escrow fund.
goal app call --app-id $APP_ID \
--app-arg "str:invest" \
--from=$INVESTOR_ADDR \
--out=unsignedtransaction1.tx \
-d ~/node/data
goal clerk send --from=$INVESTOR_ADDR \
--to=$ESCROW_ADDR \
--amount=500000 \
--out=unsignedtransaction2.tx \
-d ~/node/data
cat unsignedtransaction1.tx unsignedtransaction2.tx > combinedtransactions.tx
goal clerk group -i combinedtransactions.tx -o groupedtransactions.tx
goal clerk sign -i groupedtransactions.tx -o signout.tx
goal clerk rawsend -f signout.tx
Please refer to Atomic Transfers for more information on how to create and group transactions atomically.
Withdraw Funds
When the endDate
has passed, it will be possible for the Owner to claim the investments and send the fund to the receiverAddress
if the targetPrice
is reached. If that’s the case, the receiver can then submit a payment transaction from the escrow to their account using the following goal
command line.
export TEAL_ESCROW_PROG="fal_escrow.teal"
export RECEIVER_ADDR="RECEIVERADDRESSACCOUNT"
goal app call --app-id $APP_ID \
--app-arg "str:claim" \
--from $RECEIVER_ADDR \
--out=unsignedtransaction1.tx \
-d ~/node/data
goal clerk send --to=$RECEIVER_ADDR \
--close-to=$RECEIVER_ADDR \
--from-program=$TEAL_ESCROW_PROG \
--amount=0 \
--out=unsignedtransaction2.tx \
-d ~/node/data
cat unsignedtransaction1.tx unsignedtransaction2.tx > combinedtransactions.tx
goal clerk group -i combinedtransactions.tx -o groupedtransactions.tx
goal clerk split -i groupedtransactions.tx -o split.tx
goal clerk sign -i split-0.tx -o signout-0.tx
cat signout-0.tx split-1.tx > signout.tx
goal clerk rawsend -f signout.tx
-
The first part is the call to the Stateful Smart Contract passing the string argument “claim”.
-
The second transaction is the payment transaction from the escrow account to the receiver and it is not signed as it is a Stateless TEAL contract.
It should have an amount of 0 and must set the --close-to
attribute to the receiver. This will empty the escrow funds into the receiver’s account.
Reclaim Funds
If the targetPrice
is not met and the endDate
has passed, the Investors can reclaim their investment fund. The goal
CLI will be:
export TEAL_ESCROW_PROG="fal_escrow.teal"
goal app call --app-id $APP_ID \
--app-account=$ESCROW_ADDR \
--app-arg "str:reclaim" \
--from $INVESTOR_ADDR \
--out=unsignedtransaction1.tx
# reclaim amount less the transaction fee
goal clerk send --to=$INVESTOR_ADDR \
--close-to=$INVESTOR_ADDR \
--from-program=$TEAL_ESCROW_PROG \
--amount=499000 \
--out=unsignedtransaction2.tx
cat unsignedtransaction1.tx unsignedtransaction2.tx > combinedtransactions.tx
goal clerk group -i combinedtransactions.tx -o groupedtransactions.tx
goal clerk split -i groupedtransactions.tx -o split.tx
goal clerk sign -i split-0.tx -o signout-0.tx
cat signout-0.tx split-1.tx > signout.tx
goal clerk rawsend -f signout.tx
The amount is different from the initial investment because of the transaction fee is paid by the escrow.
Delete FAL
After the closeDate
has passed, the Owner which is also the creator of the FAL, should be able to delete the Application if the escrow account has been emptied. The goal
command line will be:
goal app delete --app-id $APP_ID
--from $ADDR_CREATOR
--app-account=$ESCROW_ADDR
We pass the escrow account
in as the value forapp-account
to check if it is empty. This will be handled in the TEAL ApprovalProgram logic.
Conclusion
With this article, we have demonstrated how to create a Fractional Auction Listing Application that is decentralized, secure and automated. This is one of the many use cases of Algorand’s layer-1 features and as I have shown there is no central authority that manages the FAL Escrow account. The stateful TEAL application takes care of the investments and automates the Fractional Ownership of the asset.