A simple-interest yielding bank account in Leo.
This program implements a bank that issues tokens to users and allows users to deposit tokens to accrue simple interest on their deposits.
- The bank issues users tokens via the
issue
function. - A user deposits tokens via the
deposit
function. - Upon a user's request to withdraw, the bank calculates the appropriate amount of compound interest and pays the user the principal and interest via the
withdraw
function.
Note that the program can be easily extended to include addition features such as a transfer
function, which would allow users to transfer tokens to other users.
You may have already guessed that this program has a few bugs. We list some of them below:
withdraw
can only be invoked by the bank. A malicious bank could lock users' tokens by not invokingwithdraw
.withdraw
fails if the sum of the interest and principal is greater than the user's balance.- User's can increase their principal by depositing tokens multiple times, including immediately before withdrawal.
- Integer division rounds down; if the calculated interest is too small, then it will be rounded down to zero.
Can you find any others?
record
declarationsassert_eq
- core functions, e.g.
BHP256::hash
- record ownership
- loops and bounded iteration
- mappings
- finalize
Follow the Leo Installation Instructions.
This basic bank program can be run using the following bash script. Locally, it will execute Leo program functions to issue, deposit, and withdraw tokens between a bank and a user.
cd basic_bank
./run.sh
The .env
file contains a private key and address. This is the account that will be used to sign transactions and is checked for record ownership. When executing programs as different parties, be sure to set the private_key
field in .env
to the appropriate value. You can check out how we've set things up in ./run.sh
for a full example of how to run the program as different parties.
We will be playing the role of two parties.
The private key and address of the bank.
private_key: APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
address: aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px
The private key and address of the user.
private_key: APrivateKey1zkp2RWGDcde3efb89rjhME1VYA8QMxcxep5DShNBR6n8Yjh
address: aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t
Let's make some bank transactions. We'll take the role of the bank and issue 100 tokens to the user. We swap the private key into .env
and run the issue
transition function. The inputs are simply the recipient of the issuance and the amount.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
" > .env
leo run issue aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t 100u64
Now, let's have the user deposit 50 of their tokens with the bank. We'll take the role of the user and call the deposit function, having the user use the output record that was issued to them by the bank. The inputs are the output record from the issue
transition and the amount the user wishes to deposit.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp2RWGDcde3efb89rjhME1VYA8QMxcxep5DShNBR6n8Yjh
" > .env
leo run deposit "{
owner: aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t.private,
amount: 100u64.private,
_nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public
}" 50u64
You'll see that the output contains a new private record belonging to the user with 50 credits, and a finalize deposit
function taking the arguments (bank address, amount) that will update a public mapping with 50 credits. This information is queryable on-chain.
With the 50 token deposit, let's say 15 periods of time pass with compounding interest at a rate of 12.34% on the principal amount.
You can run the calculation yourself, it comes out to 266 tokens accrued using those numbers.
Now, let's have the bank withdraw all tokens after 15 periods. Let's switch to the bank role, and call the withdraw
transition function. The inputs are the recipient's address, amount, rate, and periods.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
" > .env
leo run withdraw aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t 50u64 1234u64 15u64
You'll see here the withdrawal function creates a new private record for the user containing all 266 withdrawn tokens, and then calls the finalize withdraw
function with arguments (address, amount), which will update the public balance of the bank back to 0. The public mapping will be queryable on-chain.