Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: [AAVE V3 Lending Protocol] Borrowing, Repayment, and Interest Accrual #58

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lending-protocol/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules
package-lock.json
25 changes: 25 additions & 0 deletions lending-protocol/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# My dApp Template

## Install

```
npm install
```

## Start a local devnet for testing and development

Please refer to the documentation here: https://wiki.alephium.org/full-node/devnet

## Compile

Compile the TypeScript files into JavaScript:

```
npm run compile
```

## Testing

```
npm run test
```
50 changes: 50 additions & 0 deletions lending-protocol/alephium.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Configuration } from '@alephium/cli'
import { Number256 } from '@alephium/web3'

// Settings are usually for configuring
export type Settings = {
issueTokenAmount: Number256
openaiAPIKey?: string
ipfs?: {
infura?: {
projectId: string,
projectSecret: string
}
}
}

const defaultSettings: Settings = {
issueTokenAmount: 100n,
openaiAPIKey: process.env.OPENAI_API_KEY || '',
ipfs: {
infura: {
projectId: process.env.IPFS_INFURA_PROJECT_ID || '',
projectSecret: process.env.IPFS_INFURA_PROJECT_SECRET || ''
}
}
}

const configuration: Configuration<Settings> = {
networks: {
devnet: {
nodeUrl: 'http://127.0.0.1:22973',
// here we could configure which address groups to deploy the contract
privateKeys: ['a642942e67258589cd2b1822c631506632db5a12aabcf413604e785300d762a5'],
settings: defaultSettings
},

testnet: {
nodeUrl: process.env.NODE_URL as string,
privateKeys: process.env.PRIVATE_KEYS === undefined ? [] : process.env.PRIVATE_KEYS.split(','),
settings: defaultSettings
},

mainnet: {
nodeUrl: process.env.NODE_URL as string,
privateKeys: process.env.PRIVATE_KEYS === undefined ? [] : process.env.PRIVATE_KEYS.split(','),
settings: defaultSettings
}
}
}

export default configuration
19 changes: 19 additions & 0 deletions lending-protocol/contracts/authorizations.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Abstract Contract Authorization(
mut owner_: Address
) {
enum AuthorizationErrorCodes {
UNAUTHORIZED = 401
}

fn onlyOwner(caller: Address) -> () {
checkCaller!(callerAddress!() == selfAddress!(), AuthorizationErrorCodes.UNAUTHORIZED)
assert!(caller == owner_, AuthorizationErrorCodes.UNAUTHORIZED)
}

@using(updateFields = true)
pub fn changeOwner(newOwner: Address) -> () {
onlyOwner(callerAddress!())

owner_ = newOwner
}
}
66 changes: 66 additions & 0 deletions lending-protocol/contracts/lending-account.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
Contract LendingProtocolAccount(
token: Address,
_address: Address,
parentContractAddress: Address,
mut amountDeposited: U256
) {
let mut is_locked = false
enum ErrorCodes {
UNAUTHORIZED = 402
INSUFFICIENT_BALANCE = 423
INVALID_AMOUNT = 424
}

pub fn getToken() -> Address {
return token
}

pub fn getUser() -> Address {
return _address
}

pub fn getTotalDeposit() -> U256 {
return amountDeposited
}

pub isLocked() -> Bool{
return is_locked
}

@using(assetsInContract = true)
pub fn deductFromBalance(amount: U256) -> U256 {
let caller = callerAddress()
checkCaller!(caller == parentContractAddress, ErrorCodes.UNAUTHORIZED)
assert!(amount == 0, ErrorCodes.INVALID_AMOUNT)
assert!(amountDeposited < amount, ErrorCodes.INSUFFICIENT_BALANCE)

amountDeposited = amountDeposited - amount

return amount
}

@using(assetsInContract = true)
pub fn addToBalance(amount: U256) -> U256 {
let caller = callerAddress()
checkCaller!(caller == parentContractAddress, ErrorCodes.UNAUTHORIZED)
assert!(amount == 0, ErrorCodes.INVALID_AMOUNT)

amountDeposited = amountDeposited + amount

return amount
}

@using(updateFields = true)
pub fn lockAccount() -> () {
let caller = callerAddress()
checkCaller!(caller == parentContractAddress, ErrorCodes.UNAUTHORIZED)
is_locked = true
}

@using(updateFields = true)
pub fn unlockAccount() -> () {
let caller = callerAddress()
checkCaller!(caller == parentContractAddress, ErrorCodes.UNAUTHORIZED)
is_locked = false
}
}
140 changes: 140 additions & 0 deletions lending-protocol/contracts/lending-pool.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
const ZERO_ADDRESS = @0000000000000000000000000000000000

Contract LendingPool (token: Address, mut borrowInterestRate: U256, mut lendingInterestRate: U256, caller: Address) {

// Token amount record
let mut totalSupplyAmount: U256 = 0
let mut totalBorrowAmount: U256 = 0

// Error codes
enum ErrorCodes {
INVALID_CALLER_CONTRACT = 401
INVALID_BORROWER = 403
INSUFFICIENT_AMOUNT = 400
}

// Transactions Data structure
struct LenderTransaction {
user: Address,
mut amount: U256
}
struct BorrowTransaction {
user: Address,
collateral: Address,
mut amount: U256,
mut interest: U256,
mut dateLastBorrowed: U256
}

// Transactions Record
let supplies: [LenderTransaction; 1000000000] = [LenderTransaction {user: @0000000000000000000000000000000000, amount: 0}; 1000000000]
let suppliesPointer: U256 = 0
let borrows: [BorrowTransaction; 1000000000] = [
BorrowTransaction {
user: @0000000000000000000000000000000000,
collateral: @0000000000000000000000000000000000,
amount: 0,
interest: 0,
dateLastBorrowed: 0
}; 1000000000
]
let borrowsPointer: U256 = 0

fn checkCaller() {
let _caller = callerAddress!();
assert!(_caller == caller, ErrorCodes.INVALID_CALLER_CONTRACT)
}

pub fn getTotalSupply() -> U256 {
return totalSupplyAmount;
}

pub fn getTotalBorrow() -> U256 {
return totalBorrowAmount;
}

pub fn updateLendingInterestRate(value: U256){
checkCaller()
lendingInterestRate = value
}

pub fn updateBorrowInterestRate(value: U256){
checkCaller()
borrowInterestRate = value
}

pub fn getBorrowInterestRate() -> U256 {
return borrowInterestRate
}
pub fn getLendingInterestRate() -> U256 {
return lendingInterestRate
}

pub fn supply(_user: Address, amount: U256) -> () {
checkCaller()
let mut lender = getLender(_user)
if (lender.user == ZERO_ADDRESS) {
lender = LenderTransaction{
user: _user,
amount: amount
}
supplies[suppliesPointer] = lender
suppliesPointer = suppliesPointer + 1
}else{
lender.amount = lender.amount + amount
}
}

pub fn borrow(_user: Address, _collateralToken: Address, amount: U256) -> () {
checkCaller()
let mut borrowerIndex = getBorrower(_user)
if (borrowerIndex == -1) {
borrower = BorrowTransaction{
user: _user,
collateral: _collateralToken
amount: amount,
interest: borrowInterestRate,
dateLastBorrowed: blockTimeStamp!()

}
borrows[borrowsPointer] = borrower
borrowsPointer = borrowsPointer + 1
}else{
borrows[borrowerIndex].amount = borrows[borrowerIndex].amount + amount
borrows[borrowerIndex].dateLastBorrowed = blockTimeStamp!()
}
totalBorrowAmount = totalBorrowAmount + amount
totalSupplyAmount = totalSupplyAmount - amount
}

pub fn repay(_user: Address, amount: U256) -> () {
checkCaller()
let mut borrowerIndex = getBorrower(_user)
assert!(borrowerIndex != -1, ErrorCodes.INVALID_BORROWER)
let amountToPay = borrows[borrowerIndex].amount + (borrows[borrowerIndex].amount * (borrows[borrowerIndex].interest / 100))
assert!(amount >= amountToPay, ErrorCodes.INSUFFICIENT_AMOUNT)
totalBorrowAmount = totalBorrowAmount + borrows[borrowerIndex].amount
totalSupplyAmount = totalSupplyAmount + amount
borrows[borrowerIndex].amount = 0
borrows[borrowerIndex].dateLastBorrowed = 0
borrows[borrowerIndex].interest = 0
}

fn getLender(_user: Address) -> I256 {
for (let mut index = 0; index <= suppliesPointer; index = index + 1) {
if(supplies[index].user == _user){
return index
}
}
return -1
}

fn getBorrower(_user: Address) -> I256 {
for (let mut index = 0; index <= borrowsPointer; index = index + 1) {
if(borrows[index].user == _user){
return index
}
}
return -1
}
}
40 changes: 40 additions & 0 deletions lending-protocol/contracts/lending-protocol-account-factory.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Abstract Contract LendingProtocolAccountFactory(lendingProtocolTemplateId: ByteVec) {
enum ErrorCodes {
DUPLICATE_ERROR = 422
}

pub fn getTemplateId(_address: Address, _token: Address) -> ByteVec {
let (encodedImmFields, _) = encodeFields!(_address, _token);
return encodedImmFields
}

pub fn lendingProtocolAccountExists(_address: Address, _token: Address) -> Bool {
let templateId = getTemplateId(_address, _token)
let lendingProtocolAccountId = subContractId!(templateId)
return contractExists!(lendingProtocolAccountId)
}

pub fn getLendingProtocolAccount(_address: Address, _token: Address) -> LendingProtocolAccount {
let lendingProtocolAccountId = subContractId!(getTemplateId(_address, _token))
return LendingProtocolAccount(lendingProtocolAccountId)
}

@using(preapprovedAssets = true)
pub fn createLendingProtocolAccount(_address: Address, _token: Address, amount: U256) -> () {
assert!(!lendingProtocolAccountExists(_address, _token), ErrorCodes.DUPLICATE_ERROR)

let (encodedImmFields, encodedMutFields) = LendingProtocolAccount.encodeFields!(
_token,
_address,
selfAddress!(),
amount
)
let path = getTemplateId(_address, _token)
copyCreateSubContract!{caller -> ALPH: 0.1 alph}(
path,
lendingProtocolTemplateId,
encodedImmFields,
encodedMutFields
)
}
}
Loading