Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Solution Thumbnail

Build an employee loan arrangement dApp

Overview

This solution will guide the reader through the design and the implementation of an Algorand dApp created for the management of employee loan arrangements involving companies, employees, and national financial authorities.

This solution takes advantage of Algorand Standard Assets (ASA), Atomic Transfers (AT) and Algorand Stateful and Stateless Smart Contracts (ASC1), to simplify the process of employee loan arrangements. The proposed solution makes use of goal CLI programming language.

Introduction

Employee loans are exclusive loans provided to employees working for public institutions or private companies. Usually, employee loan providers are financial authorities such as banks, financial institutions and national insurance authorities. Typically, these loans are offered on a cheaper or interest-free basis to the employees. Employers take charge of the loan repayment by charging the employee via agreed deductions on their future payslips. An employee can apply for loans up to a certain value over their net salary.

The loan arrangement requires financial authorities to carry out several operations like KYC (Know Your Customer) screening, validation and suitability checks on employers and employees status. Nowadays, these procedures require several interactions between parties causing inefficient workflows and long processing times.

Employee loans represent a good blockchain use case where the arrangements are fully governed by stateful and stateless smart contracts, and the payments are managed directly on-chain. This solution proposes a simplified implementation of an employee loan arrangement, presenting an implementation of the loan approval/payment process. The solution proposed does not manage the repayment deductions. A good further heir relationsheir relationsexercise could be the integration of an on-chain repayment mechanism to manage loan repayment installments - an interesting solution could be the integration with a recurring payment application such as the one proposed with the ASA Recurring Withdrawal DApp solution.

Info

This solution assumes financial exchanges on-chain based on the ALGO token. Any entity involved in the application has a balance associated with its account. The reader can switch to any type of financial model based on ASA tokens.

Warning

This solution is intended for learning purposes only. It does not cover error checking and other edge cases. The smart contract(s) in this solution have NOT been audited. Therefore, it should not be used as a production application.

Solution Architecture

Algorand provides a set of layer-1 functionalities that can be combined to solve complex real-world problems. This solution proposes an architecture composed by the following components:

  1. Algorand Standard Asset: Unique token used within the application to identify the employees of a certain employer;
  2. Atomic Transfers: Group of transactions executed simultaneously that invoke both the Stateful and the Stateless components of the application. The first transaction Calls the Stateful component while the second transaction invokes a Payment transaction to/from the Stateless component.
  3. Stateful Algorand Smart Contract: It represents the main application, called Employee Loan Application, that approves or rejects loans according to custom TEAL logic. The loan details and conditions are stored globally on the blockchain global state, while the employee details are stored locally into the account balance of any employee requesting a loan.
  4. Stateless Algorand Smart Contract: Implements the Escrow Contract Account used by the authorities to allocate funds for a certain loan. It approves payment transactions according to custom TEAL logic.

The architecture diagram below illustrates the components of the application and their relationships. The process involves three actors: an Employer that creates a new loan application, an Employee that requests a loan through his employer, and an Authority that pays the loan to the employee. Each of these entities interacts with the application as an independent Algorand Standalone Account.

The following steps define the deployment and usage of the application:

  1. The Employer deploys a new Employee Loan Application and the Employer Right Token;
  2. The Authority funds an Escrow Contract Account, and initializes the Employee Loan Application;
  3. The Employee registers itself (opt-in) for the Employer Rights Token and the Employee Loan Application;
  4. The Employer opens a new loan request on behalf of an employee;
  5. The Authority approves or rejects the loan. If the approval succeeds, the Authority pays the loan through a payment transaction from the escrow account.

Employee Loan dApp Architecture

Statefull ASC1 component

The Stateful component of this solution represents the Employee Loan Application. It permanently stores the loan agreement details on the global state of the blockchain. In the event of loan requests, it stores the employee details on its local account balance. The Employee Loan Application makes use of the following variables:

Variable Name Domain Type
AuthorityAddr Global ByteSlice
EmployerRightsId Global UInt
LoanThreshold Global UInt
LoanEscrow Global ByteSlice
LoanFund Global UInt
PaySlip Local UInt
RequiredLoan Local UInt
AuthorizedLoan Local UInt
ExistingLoan Local UInt
AuthorityKyc Local UInt

The stateful applications lyfecycle comprises the deployment of two TEAL programs:
1. Approval Program: responsible for processing all the application calls of the contract. This component implements the TEAL logic that manages the application;
2. Clear Program: catches the clear application calls that remove the smart contract from the account’s state.

Clear Program

The clear program used in this solution approves any clear call.

#pragma version 3
int 1

Approval Program

The approval program is the core component of the application. It manages the interactions between different Algorand components by executing TEAL condition branches to approve or reject an application call transaction (AppCall). There are five fundamental types of AppCalls:
1. NoOp
2. OptIn
3. DeleteApplication
4. UpdateApplication
5. CloseOut

The Stateful ASC1 boilerplate template is a great starting point for creating a complete approval program.

In addition to the five AppCalls, the application create transaction can be integrated with TEAL logic to execute specific operations at the contract creation phase. The approval program proposed in this solution handles all these AppCalls transactions.

#pragma version 3
txn ApplicationID
int 0
==
bnz branch_create
txn OnCompletion
int OptIn
==
bnz branch_opt_in
txn OnCompletion
int CloseOut
==
bnz branch_close_out
txn OnCompletion
int UpdateApplication
==
bnz branch_update
txn OnCompletion
int DeleteApplication
==
bnz branch_delete
txn OnCompletion
int NoOp
==
bnz branch_app_calls
int 0
return

The branch labels above invoke different TEAL branches according to the AppCalls. Let’s go through the details of each branch.

Application Create

Usually, the AppCall create branch initialises the application and all its variables. In this application the Employer invokes the create call passing with the transaction:
1. The address of the authority "addr:<AUTHORITY_ADDR>" as transaction argument;
2. The asset id of the Employee Rights Token.

branch_create:
txn NumAppArgs
int 1
==
bz branch_fail
byte "AuthorityAddr"
txna ApplicationArgs 0
app_global_put
txn NumAssets
int 1
==
bz branch_fail
int 0
asset_params_get AssetManager
bz branch_fail // if Asset manager not present, fail.
txn Sender
==
bz branch_fail // fail if the Employer who is creating the app is not the creator of the provided Employer Rights Token
byte "EmployerRightsId"
txn Assets 0
app_global_put
int 1
return

The AppCall ‘create’ initialises the global variables AuthorityAddr and EmployerRightsId respectively with the address of the authority responsible for the loan and the Employer Rights Token id. In this way, the application will be globally associated with a certain Authority and Employer.

User Opt-In

Any user account that wants to use its local state for an application needs to opt-in to the smart contract. The Employee Loan Application only makes use of employees local state. Any employee that wants to apply for a loan needs to opt-in to the application. The opt-in TEAL logic of this solution also implements a permission mechanism that consent only to opt-in transactions from authorized employees. This mechanism enhances security avoiding users from opting-in to loans offered by other employers.

branch_opt_in:
txn NumAssets
int 1
==
bz branch_fail
byte "EmployerRightsId"
app_global_get
store 0
load 0
txn Assets 0
==
bz branch_fail // The employee presented the wrong Employer Rights Token
int 0 // Employee opting-in
load 0 // Id of the Employer Rights Token
asset_holding_get AssetBalance
bz branch_fail // fail if sender does not have the Employer Rights Token
int 1
==
bz branch_fail
int 1
return

User Close Out

The users of the Employee Loan Application can always close-out their participation in the contract - no specific restrictions needed.

branch_close_out:
int 1
return

Application Update

In this solution, only the application creator, such as the Employer user, can update the approval program. The TEAL logic will reject any AppCall update transaction coming from other users.

branch_update:
txn Sender
global CreatorAddress
==
bz branch_fail
int 1
return

Application Delete

As for the AppCall update, this solution allows only the application creator, such as the Employer user, to delete the application. The TEAL logic will reject any AppCall delete transaction coming from other users.

branch_delete:
txn Sender
global CreatorAddress
==
bz branch_fail
int 1
return

Application Calls

The AppCalls NoOp are generic transactions that compose the core TEAL logic of the approval program. The Employee Loan Application supports four different actions which can be executed based of the transaction content.

  1. Contract Init: if the AppCall transaction has the argument "str:Init";
  2. Loan Request: if the AppCall transaction has the argument "str:Request";
  3. Loan Checks: if the AppCall transaction has the argument "str:Check";
  4. Loan Approval: if the AppCall transaction has the argument "str:Authorize";

// AppCalls branch
branch_app_calls:
global GroupSize
int 2
==
byte "AuthorityAddr"
app_global_get
store 1
load 1
gtxn 0 Sender
== // The Authority is the sender
&&
gtxna 0 ApplicationArgs 0
byte "Init"
==
&&
bnz branch_loan_setup

global GroupSize
int 1
==
global CreatorAddress
gtxn 0 Sender
==
&&
gtxna 0 ApplicationArgs 0
byte "Request"
==
&&
bnz branch_loan_request

global GroupSize
int 1
==
load 1 // load the Authority addr from scratch
gtxn 0 Sender
== // The Authority is the sender
&&
gtxna 0 ApplicationArgs 0
byte "Check"
==
&&
bnz branch_loan_check

global GroupSize
int 2
==
// TEAL 3
load 1 // load the Authority addr from scratch
gtxn 0 Sender
== // The Authority is the sender
&&
gtxna 0 ApplicationArgs 0
byte "Authorize"
==
&&
bnz branch_loan_authorize

int 0
return

Application Call 1: Contract Init

When the Employer creates a new Employee Loan Application, the respective Authority initialise the loan contract by invoking a Contract Init action. The contract init executes an Atomic Transfer composed of an AppCall transaction and a payment transaction. The former sets up the global variables detailed below, while the latter funds the Escrow Contract Account allocated by the Authority for this loan application:

  1. Loan Threshold: AppCall argument 1; minimum salary threshold that cannot be exceeded for the Employee. This value might vary according to the financial condition of the employee and is assigned after off-chain checks conducted by the Authority;
  2. Loan Escrow Contract Account: Stateless Escrow Contract Account public key; obtained from the receiver field of the payment transaction;
  3. Loan Fund: funds amount allocated by the Authority for the Employee Loan Application; obtained from the amount field of the payment transaction.

branch_loan_setup:
gtxn 0 NumAppArgs
int 2
==
gtxn 1 TypeEnum
int pay
==
&&
bnz loan_setup
int 0
return
loan_setup:
byte "LoanThreshold"
gtxna 0 ApplicationArgs 1
btoi
app_global_put
byte "LoanEscrow"
gtxn 1 Receiver
app_global_put
byte "LoanFund"
gtxn 1 Amount
app_global_put
int 1
return

By allocating funds on an escrow contract account, the authorities limit the loans funding, avoiding exposing their entire wallet/budgets. Authorities can allocate as many escrow contract accounts as the number of Employee Loan Applications they are responsible for. In addition, contract account payments are regulated by the embedded TEAL logic, this guarantees that the application only permits pre-authorized payments.

Application Call 2: Loan Request

The Employer creates a new loan request for an Employee who wants to apply for a loan. The AppCall sets the employee local state with the following variables:

  1. Payslip: AppCall argument 1; employee payslip;
  2. Required Loan: AppCall argument 2; the total amount of loan requested by the employee;
  3. Authorized Loan: AppCall argument 3; authorization flag determining the loan approval, value initialized at 0.

To prevent malicious users from trying to change the state of arbitrary users, the AppCall requires the Employee’s address to be specified within the accounts array of the transaction. Then, the TEAL logic verifies the account have previously opted-in to the current Employee Loan Application.

branch_loan_request:
gtxn 0 NumAppArgs
int 4
==
gtxna 0 ApplicationArgs 3
btoi
int 0
== // AuthorizedLoan must be 0 when requested
&&
gtxn 0 NumAccounts
int 1
==
&&
assert
int 1
gtxn 0 ApplicationID
app_opted_in
assert
int 1
byte "Payslip"
gtxna 0 ApplicationArgs 1
btoi
app_local_put
int 1
byte "RequiredLoan"
gtxna 0 ApplicationArgs 2
btoi
app_local_put
int 1
byte "AuthorizedLoan"
gtxna 0 ApplicationArgs 3
btoi
app_local_put
int 1
return

Application Call 3: Loan Checks

The Authority conducts off-chain suitability checks before approving a loan request. Consequently, the Authority stores the results of these checks on the Employee’s local state via this Loan Checks AppCall transaction. The AppCall initializes the following variables:

  1. Existing Loan: AppCall argument 1; the amount of salary (monthly instalment) already committed for previous loans;
  2. Authority KYC: AppCall argument 2; KYC boolean flag.

As for AppCall 2, this AppCall requires the Employee’s account to be specified within the accounts array in order to approve the transaction.

Info

In the context of this solution the Authority only carries two suitability checks. An interesting exercise could be the implementation of more complex suitability checks.

branch_loan_check:
gtxn 0 NumAppArgs
int 3
==
gtxna 0 ApplicationArgs 1
btoi
int 0
>= // Negative values not allowed for the argument ExistingLoans
&&
gtxn 0 NumAccounts
int 1
==
&&
assert
int 1
gtxn 0 ApplicationID
app_opted_in
assert
int 1
byte "ExistingLoan"
gtxna 0 ApplicationArgs 1
btoi
app_local_put
int 1
byte "AuthorityKyc"
gtxna 0 ApplicationArgs 2
btoi
app_local_put
int 1
return

Application Call 4: Loan Approval

The Authority approves a new loan by executing an Atomic Transfer composed by:

  1. AppCall with argument 0 str: Authorize running a TEAL logic for the loan approval. The TEAL logic succeeds if and only if the employee’s payslip remains above the loan threshold even after the new loan deductions; the KYC results approved, and whether the escrow contract account has enough allocated funds (twice the amount of the required loan).
  2. Payment Transaction that deposits the approved amount of funds from the Escrow Contract Account into the Employee balance.

Hint

This solution assumes loan repayments charged based on monthly instalments, computed as the total loan amount divided over 12 months with no interest. The contract could be enhanced to implement extended payment schedules and interest payments.

gtxn 0 NumAppArgs
int 1
==
gtxn 0 NumAccounts
int 1
==
&&
int 1
gtxn 0 ApplicationID
app_opted_in
assert
gtxn 1 TypeEnum
int pay
==
&&
gtxn 1 Sender
byte "LoanEscrow"
app_global_get
==
&&
gtxn 1 Amount
int 1
byte "RequiredLoan"
app_local_get
store 2
load 2
==
&&
assert
int 1
byte "AuthorizedLoan"
app_local_get
int 0
==
bz branch_fail // AuthorizedLoan needs to be 0 (authorized loans cannot be re-authorized)
int 1
byte "AuthorityKyc"
app_local_get
int 1
==
bz branch_fail
load 2
int 12
/  // RequiredLoan splitted into 12 instalments with 0 interests
int 1
byte "ExistingLoan"
app_local_get
+
int 1
byte "Payslip"
app_local_get
swap
-
byte "LoanThreshold"
app_global_get
>=
bz branch_fail // LoanThreshold <= PaySlip - (RequiredLoanInstalment + ExistingLoan)
load 2
int 2
*
byte "LoanFund"
app_global_get
store 3
load 3
< //Security deposit check: 2*RequiredLoan < LoanFund
bz branch_fail
// All checks passed. Update AuthorizedLoan and LoanFund variables and authorize the payment
byte "LoanFund"
load 3
load 2
-
app_global_put // update the LoanFund variable
int 1
byte "AuthorizedLoan"
load 2
app_local_put // update local variable AuthorizedLoan of the employee with the value RequiredLoan
int 1
return

The TEAL logic above verifies first that the Employee has opted-in to the Employee Loan Application as with the previous AppCalls, then checks whether the Escrow Contract Account passed with the transaction is the one stored with the application. This check prevents malicious users from triggering payments to unauthorized accounts.

Stateless ASC1 component

Stateless ASC1 components approve or reject transactions according to predefined TEAL logic. In this solution, the stateless component implements an Escrow Contract Account that executes loan payments.

The Escrow Contract Account approves payments if and only if submitted as Atomic Transfers composed by:
1. The first is an AppCall to the stateful Employee Loan Application, allowing only payments happening in the context of this application;
2. The second is a Payment Transaction that transfers ALGO from the Escrow Contract Account to the Employee balance.

Info

In this example, the ID of the stateful smart contract should be hardcoded into the contract in place of TMPL_APP_ID.

global GroupSize
int 2
==
gtxn 0 TypeEnum
int appl
==
&&
gtxn 0 ApplicationID
int TMPL_APP_ID
==
&&
gtxn 0 OnCompletion
int NoOp
==
&&
gtxn 1 TypeEnum
int pay
==
&&
gtxn 1 Fee
int 1000
<=
&&
gtxn 1 CloseRemainderTo
global ZeroAddress
==
&&
gtxn 1 RekeyTo
global ZeroAddress
==
&&

Please refer to this guidelines to understand each component of this Stateless ASC1.

Application Deployment

The application deployment shown in this section follows a sequence of transactions issued manually through goal CLI. Consider as a next step exercise to deploy this application using any Algorand SDK.

Initialising the ASA Employee Right Token

To interact with the Employee Loan Application, each employee needs to own a participation token, viz. the Employer Rights Token. An Employer Rights Token is an Algorand Standard Asset created and distributed by the Employer.

Employer Rights Token Creation

The Employe creates an Employer Rights Token as follows:

$ ./goal asset create --creator <EMPLOYER_ADDR> --total 1000 --name EmployerRightsToken --unitname ERT --decimals 0

In this example, Employer Rights Tokens are unique tokens and cannot be divided into decimals.

Employer Rights Token Distribution

To receive Employee Rights Tokens, the employees need to opt-in to the token itself. The opt-in is an asset transfer transaction sent by the Employee to itself, with a token amount set to 0.

$ ./goal asset send -a 0 --assetid <EMPLRIGHT_ID>  -f <EMPLOYEE_ADDR> -t <EMPLOYEE_ADDR> --creator <EMPLOYER_ADDR>

After the opt-in, the Employer sends the participation token to the Employee

$ ./goal asset send -a 1 --assetid <EMPLRIGHT_ID>  -f <EMPLOYER_ADDR> -t <EMPLOYEE_ADDR> --creator <EMPLOYER_ADDR>

$ ./goal asset freeze --freezer <EMPLOYER_ADDR> --freeze=true --account <EMPLOYEE_ADDR> --creator <EMPLOYER_ADDR> --asset <EMPLRIGHT_ID>

Participation tokens are issued as frozen by the Employer. Frozen tokens cannot be exchanged by malicious users without the creator, viz. Employer, consent.

Creating Stateful Employee Loan Application

To create a new Employee Loan Application, the Employer deploys first the Stateful component of the application by providing the approval_loan.teal and clear_loan.teal programs and allocating the number of global and local variables required by the application. In addition, the Employer provides its Employer Rights Token to be associated with the application and the Authority chosen as a financial provider of the loan.

Input

$ ./goal app create --creator <EMPLOYER_ADDR> --app-arg "addr:AUTHORITY_ADDR" --foreign-asset EMPLRIGHTS_ASSID --approval-prog approval_loan.teal --clear-prog clear_state_loan.teal --global-byteslices 2 --global-ints 3 --local-byteslices 0 --local-ints 5`

Output

Created app with app index TMPL_APP_ID

The application global state is then initialized with the variables Authority Address and Employer Rights Token Id.

Input

$ ./goal app read --app-id TMPL_APP_ID --global

Output

{
   "AuthorityAddr": {
      "tb": <ADDR_BYTECODE>,
      "tt": 1
   },
   "EmployerRightsId": {
      "tt": 2,
      "ui": <EMPLRIGHTS_ASSID>
   },
 }

Initialising Stateless Escrow Contract Account

The Stateless ASC1 component loan_escrow.teal can now be completed by hardcoding the application id TMPL_APP_ID of the Stateful component. Thus, the contract can be compiled as follows:

Input

$ ./goal clerk compile loan_escrow.teal

Output

loan_escrow.teal: LOAN_ESCROW_ACCOUNT

Initializing the Employee Loan Application

The Authority realises that a new loan application has been created by an Employer. At this point the Authority initialises the loan arrangement through the first Atomic Transfer composed by:
1. An AppCall transaction that initialises the loan arrangement passing the arguments "str:Init", "int:<THRESHOLD_AMOUNT>";
2. A Payment Transaction that allocates funds for the loan into the Escrow Contract Account.

Creating unsigned standalone transactions

$ ./goal app call --app-id TMP_APP_ID -f <AUTHORITY_ADDR> --app-arg "str:Init" --app-arg "int:<THRESHOLD_AMOUNT>" -o loan_init.txn

$ ./goal clerk send --from=<AUTHORITY_ADDR> --to=<LOAN_ESCROW_ACCOUNT> --fee=1000 --amount=<FUND_AMOUNT> --note="loan fund" --out="loan_fund.txn"

Atomically grouping unsigned transactions

$ cat loan_init.txn loan_fund.txn > combinedloan_init.tx

$ ./goal clerk group -i combinedloan_init.tx -o groupedloan_init.tx

Splitting the unsigned transactions

Input

$ ./goal clerk split -i groupedloan_init.tx -o unsigned_init.tx

Output

Wrote transaction 0 to unsigned_init-0.tx
Wrote transaction 1 to unsigned_init-1.tx

Signing the standalone transactions

$ ./goal clerk sign -i unsigned_init-0.tx -o init_0.stxn

$ ./goal clerk sign -i unsigned_init-1.tx -o init_1.stxn

Grouping the two signed transactions

$ cat init_0.stxn init_1.stxn > init.sgtxn

Submitting signed group transactions

./goal clerk rawsend -f init.sgtxn

After the execution of the Atomic Transfer, the Employee Loan Application is ready to accept new loan requests.

Application Usage

The Employee Loan Application consists in the following steps:

  1. Loan request;
  2. Loan suitability checks;
  3. Loan authorization.

Each step has its own TEAL logic that approves or rejects transactions.

Loan Request

The Employee starts a new loan request with the opt-in to the Employee Loan Application. This action allocates the Employee’s local state required by the application.

$ ./goal app optin --app-id TMP_APP_ID -f <EMPLOYEE_ADDR> --foreign-asset <EMPLRIGHTS_ASSID>

The Employee starts the loan request process by registering with the Employee Loan Application - opt-in transaction. This action allocates the Employee’s local state required by the application.

At this point, the Employer starts a new loan request on behalf of the Employee. This AppCall sets the Employee’s local state with the details on the payslip and the required loan through the transaction parameters "int:<PAYSLIP>" and "int:<LOAN_AMOUNT>". At this stage, the loan needs to be set as not authorised with the parameter "int:0".

$ ./goal app call --app-id APP_ID --from <EMPLOYER_ADDR> --app-account <EMPLOYEE_ADDR> --app-arg "str:Request" --app-arg "int:<PAYSLIP>" --app-arg "int:<LOAN_AMOUNT>" --app-arg "int:0"

Loan Suitability Checks

The Authority executes suitability checks on the Employee who applied for a loan. These checks verify the presence of pending loan instalments and the KYC. The results of these checks are then stored on the Employee’s local state with the following AppCall.

$ ./goal app call --app-id APP_ID --from <AUTHORITY_ADDR> --app-account <EMPLOYEE_ADDR> --app-arg "str:Check" --app-arg "int:<AMOUNT_EXISTING_LOANS>" --app-arg "int:<KYC_FLAG>"

At this point all the variables have been stored into the Employee’s local state and the application global state.

Input querying the global state

$ ./goal app read --global --app-id <TMP_APP_ID>

Output

{
   "AuthorityAddr": {
      "tb": <ADDR_BYTECODE>,
      "tt": 1
   },
   "EmployerRightsId": {
      "tt": 2,
      "ui": 2
   },
   "LoanEscrow": {
      "tb": <ADDR_BYTECODE>,
      "tt": 1
   },
   "LoanFund": {
      "tt": 2,
      "ui": <AMOUNT>
   },
   "LoanThreshold": {
      "tt": 2,
      "ui": <THRESHOLD_VALUE>
   }
}

Input querying the Employee’s local state

$ ./goal app read --local --from <EMPLOYEE_ADDR> --app-id <TMP_APP_ID>

Output

{
   "AuthorityKyc": {
      "tt": 2,
      "ui": 1
   },
   "AuthorizedLoan": {
      "tt": 2
   },
   "ExistingLoan": {
      "tt": 2
   },
   "Payslip": {
      "tt": 2,
      "ui": <PAYSLIP_VAL>
   },
   "RequiredLoan": {
      "tt": 2,
      "ui": <LOAN_AMOUNT>
   }
}

Loan Authorization

Finally, to authorize the loan and proceed with the payment, the Authority executes an Atomic Transfer compose by:
1. An AppCall transaction that invokes the TEAL logic with the authorization checks;
2. A Payment Transaction sending the requested loan from the Escrow Contract Account to the Employee balance.

Creating unsigned standalone transactions

$ ./goal app call --app-id 6 -f <AUTHORITY_ADDR> --app-account <EMPLOYEE_ADDR> --app-arg "str:Authorize" -o loan_authorize.txn

$ ./goal clerk send --from=<LOAN_ESCROW_ACCOUNT> --to=<EMPLOYEE_ADDR> --fee=1000 --amount=<LOAN_AMOUNT> --note="loan payment" --out="loan_pay.txn"

Atomically grouping unsigned transactions

$ cat loan_authorize.txn loan_pay.txn > combinedloan_auth.tx

$ ./goal clerk group -i combinedloan_auth.tx -o groupedloan_auth.tx

Splitting the unsigned transactions

Input

$ ./goal clerk split -i groupedloan_auth.tx -o unsigned_auth.tx

Output

Wrote transaction 0 to unsigned_auth-0.tx
Wrote transaction 1 to unsigned_auth-1.tx

Signing the standalone transactions

$ ./goal clerk sign -i unsigned_auth-0.tx -o auth_0.stxn

$ ./goal clerk sign -i unsigned_auth-1.tx -p loan_escrow.teal -o auth_1.sltxn

Grouping the two signed transactions

$ cat auth_0.stxn auth_1.sltxn > auth.sgtxn

Submitting signed group transactions

$ ./goal clerk rawsend -f auth.sgtxn

If the Atomic Transfer succeeds, the Employee account is credited with the required loan amount.

Conclusion

This solution detailed the implementation, deployment and usage of an Algorand dApp for the management of employee loan arrangements. The application makes use of all the fundamental functionalities of the Algorand protocol and can be used as a starting point for further developments.