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

add lending protocol withdraw and deposit #46

Open
wants to merge 1 commit 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
}
}
53 changes: 53 additions & 0 deletions lending-protocol/contracts/lending-account.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Contract LendingProtocolAccount(
tokenId: ByteVec,
_address: Address,
parentContractAddress: Address,
mut amountDeposited: U256
) {
enum ErrorCodes {
UNAUTHORIZED = 402
INSUFFICIENT_BALANCE = 423
INVALID_AMOUNT = 424
}

pub fn getTokenId() -> ByteVec {
return tokenId
}

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

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

@using(assetsInContract = true, preapprovedAssets = true, updateFields = true)
pub fn deposit(amount: U256) -> () {
let caller = callerAddress!()

checkCaller!(caller == parentContractAddress, ErrorCodes.UNAUTHORIZED)
assert!(amount > 0, ErrorCodes.INVALID_AMOUNT)
transferTokenToSelf!(_address, tokenId, amount)

amountDeposited = amountDeposited + amount
}

@using(assetsInContract = true, updateFields = true)
pub fn withdraw(amount: U256) -> () {
let caller = callerAddress!()

checkCaller!(caller == parentContractAddress, ErrorCodes.UNAUTHORIZED)
assert!(amount > 0, ErrorCodes.INVALID_AMOUNT)
assert!(amountDeposited >= amount, ErrorCodes.INSUFFICIENT_BALANCE)

amountDeposited = amountDeposited - amount

transferTokenFromSelf!(_address, tokenId, amount)

if (amountDeposited == 0) {
destroySelf!(_address)
}
}

}
37 changes: 37 additions & 0 deletions lending-protocol/contracts/lending-protocol-account-factory.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Abstract Contract LendingProtocolAccountFactory(
tokenId: ByteVec,
lendingProtocolAccountTemplateId: ByteVec
) {
enum ErrorCodes {
DUPLICATE_ERROR = 422
}

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

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

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

let (encodedImmFields, encodedMutFields) = LendingProtocolAccount.encodeFields!(
tokenId,
_address,
selfAddress!(),
amount
)

let _ = copyCreateSubContract!{_address -> ALPH: 1 alph, tokenId: amount}(
toByteVec!(_address),
lendingProtocolAccountTemplateId,
encodedImmFields,
encodedMutFields
)
}
}
80 changes: 80 additions & 0 deletions lending-protocol/contracts/lending-protocol.ral
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Contract LendingProtocol(
tokenId: ByteVec,
lendingProtocolAccountTemplateId: ByteVec,
mut amountDeposited: U256
mut owner_: Address
) extends LendingProtocolAccountFactory(tokenId, lendingProtocolTemplateId) {
////////////////////////
// Events
////////////////////////

event Deposit(_address: Address, amount: U256)
event Withdrawal(_address: Address, amount: U256)

////////////////////////
// Error Codes
////////////////////////

enum ErrorCodes {
INVALID_AMOUNT = 0
}

////////////////////////
// Public Functions
////////////////////////

pub fn getTokenId() -> ByteVec {
return tokenId
}

@using(preapprovedAssets = true, updateFields = true, checkExternalCaller = false)
pub fn deposit(amount: U256) -> () {
let _address = callerAddress!()

assert!(amount > 0, ErrorCodes.INVALID_AMOUNT)
if (lendingProtocolAccountExists(_address)) {
let lendingProtocolAccount = getLendingProtocolAccount(_address)
lendingProtocolAccount.deposit{_address -> tokenId: amount}(amount)
} else {
createLendingProtocolAccount{_address -> ALPH: 1 alph, tokenId: amount}(_address, amount)
}

amountDeposited = amountDeposited + amount
emit Deposit(_address, amount)
}

@using(updateFields = true, checkExternalCaller = false)
pub fn withdraw(amount: U256) -> () {
let _address = callerAddress!()

let lendingProtocolAccount = getLendingProtocolAccount(_address)

lendingProtocolAccount.withdraw(amount)

amountDeposited = amountDeposited - amount
emit Withdrawal(_address, amount)
}


pub fn upgrade(newBytecode: ByteVec) -> () {
onlyOwner(callerAddress!())

migrate!(newBytecode)
}
}

TxScript Deposit(lendingProtocol: LendingProtocol, amount: U256) {
let _address = callerAddress!()
let tokenId = lendingProtocol.getTokenId()
let lendingProtocolAccExists = lendingProtocol.lendingProtocolAccountExists(_address)

if (lendingProtocolAccExists) {
lendingProtocol.deposit{_address -> tokenId: amount}(amount)
} else {
lendingProtocol.deposit{_address -> tokenId: amount, ALPH: 1 alph}(amount)
}
}

TxScript Withdrawal(lendingProtocol: LendingProtocol, amount: U256) {
lendingProtocol.withdraw(amount)
}
7 changes: 7 additions & 0 deletions lending-protocol/jest-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"testPathIgnorePatterns": [".*/node_modules/", ".*/dist/.*"],
"transform": {
"^.+\\.(t|j)sx?$": "ts-jest"
},
"testMatch": ["<rootDir>/**/*.test.ts"]
}
48 changes: 48 additions & 0 deletions lending-protocol/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "my-dapp-template",
"version": "0.1.0",
"license": "GPL",
"scripts": {
"build": "npm run clean && npx --yes tsc --build .",
"clean": "npm run clean:windows && npm run clean:unix",
"clean:unix": "node -e \"if (process.platform !== 'win32') process.exit(1)\" || rm -rf dist",
"clean:windows": "node -e \"if (process.platform === 'win32') process.exit(1)\" || , if exist dist rmdir /Q /S dist",
"compile": "npx cli compile",
"deploy": "npx cli deploy",
"lint": "eslint . --ext ts",
"lint:fix": "eslint . --fix --ext ts",
"test": "jest -i --config ./jest-config.json"
},
"dependencies": {
"@alephium/cli": "^1.8.0",
"@alephium/web3": "^1.8.0",
"@alephium/web3-test": "^1.8.0",
"@alephium/web3-wallet": "^1.8.0"
},
"devDependencies": {
"@types/jest": "^27.5.1",
"@types/node": "^16.18.23",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.0",
"ts-jest": "^28.0.2",
"ts-node": "^10.7.0",
"typescript": "^4.4.2"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=7.0.0"
},
"prettier": {
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"bracketSameLine": false,
"trailingComma": "none"
}
}
30 changes: 30 additions & 0 deletions lending-protocol/scripts/0_deploy_faucet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Deployer, DeployFunction, Network } from '@alephium/cli'
import { Settings } from '../alephium.config'
import { TokenFaucet } from '../artifacts/ts'
import { stringToHex } from '@alephium/web3'

// This deploy function will be called by cli deployment tool automatically
// Note that deployment scripts should prefixed with numbers (starting from 0)
const deployFaucet: DeployFunction<Settings> = async (
deployer: Deployer,
network: Network<Settings>
): Promise<void> => {
// Get settings
const issueTokenAmount = network.settings.issueTokenAmount
const result = await deployer.deployContract(TokenFaucet, {
// The amount of token to be issued
issueTokenAmount: issueTokenAmount,
// The initial states of the faucet contract
initialFields: {
symbol: stringToHex('TF'),
name: stringToHex('TokenFaucet'),
decimals: 18n,
supply: issueTokenAmount,
balance: issueTokenAmount
}
})
console.log('Token faucet contract id: ' + result.contractInstance.contractId)
console.log('Token faucet contract address: ' + result.contractInstance.address)
}

export default deployFaucet
Loading