Skip to content

Commit 8985898

Browse files
committed
feat: add getMaxLoanAmountFromCollateral and sendTransaction methods
Also update routes to new structure
1 parent 2a5f15d commit 8985898

8 files changed

+156
-71
lines changed

package.json

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "aave-js",
33
"version": "0.0.0",
4-
"description": "",
5-
"keywords": [],
4+
"description": "js wrapper for Aave ETHLend marketplace",
5+
"keywords": ["aave", "loans", "crypto", "marketplace", "js"],
66
"main": "dist/aave-js.umd.js",
77
"module": "dist/aave-js.es5.js",
88
"typings": "dist/types/aave-js.d.ts",
@@ -71,8 +71,7 @@
7171
}
7272
},
7373
"collectCoverageFrom": [
74-
"src/*.{js,ts}",
75-
"src/services/*.{js,ts}"
74+
"src/**/*.{js,ts}"
7675
]
7776
},
7877
"prettier": {

src/aave-js.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
// Import here Polyfills if needed. Recommended core-js (npm i -D core-js)
22
// import "core-js/fn/array.find"
33
// ...
4+
// tslint:disable-next-line
5+
import Web3 from 'web3'
6+
47
import { LoanAPIInstance } from './types'
58
import LoanRequest from './services/LoanRequest'
69

710
export class Marketplace implements Marketplace {
8-
public request: LoanAPIInstance
11+
public requests: LoanAPIInstance
912

10-
constructor(token: string, apiUrl?: string) {
11-
this.request = new LoanRequest(token, apiUrl)
13+
constructor(token: string, web3?: Web3, apiUrl?: string) {
14+
this.requests = new LoanRequest(token, web3, apiUrl)
1215
}
1316
}

src/services/BaseService.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import axios, { AxiosError, AxiosInstance } from 'axios'
22
import { isValidChecksumAddress } from 'ethereumjs-util'
3+
// tslint:disable-next-line
4+
import Web3 from 'web3'
35

46
import { BaseResponse } from '../types'
7+
import { Transaction } from 'web3/eth/types'
8+
import { TransactionReceipt } from 'web3-core/types'
59

610
export default class BaseService {
711
protected readonly api: AxiosInstance
12+
protected readonly web3?: Web3
813

9-
constructor(token: string, apiUrl?: string) {
14+
constructor(token: string, web3?: Web3, apiUrl?: string) {
1015
this.api = axios.create({
1116
baseURL: apiUrl || 'https://api.aave.com',
1217
headers: { Authorization: `Bearer ${token}` }
1318
})
19+
this.web3 = web3
1420
}
1521

1622
protected static checkAddressChecksum(address: string): void {
@@ -33,4 +39,33 @@ export default class BaseService {
3339
return { error: 'internal server error, please contact support', code: 500 }
3440
}
3541
}
42+
43+
protected async apiRequest(
44+
endpoint: string,
45+
resourceType: string,
46+
errorParam: string = '',
47+
method: 'get' | 'post' = 'get',
48+
params?: object
49+
): Promise<BaseResponse> {
50+
const api = method === 'post' ? this.api.post : this.api.get
51+
52+
try {
53+
const { data } = await api(endpoint, params)
54+
55+
return { data, code: 200 }
56+
} catch (e) {
57+
return BaseService.errorHandler(e, resourceType, errorParam)
58+
}
59+
}
60+
61+
private async sendTransaction(tx: Transaction): Promise<TransactionReceipt> {
62+
if (this.web3) {
63+
const currentNetwork = await this.web3.eth.net.getNetworkType()
64+
if (currentNetwork === 'kovan') {
65+
return await this.web3.eth.sendTransaction(tx)
66+
}
67+
throw `this version of API allows transactions only on kovan network, but ${currentNetwork} chosen`
68+
}
69+
throw 'web3 provider is not specified'
70+
}
3671
}

src/services/LoanRequest.ts

+37-33
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,30 @@
1+
// tslint:disable-next-line
2+
import Web3 from 'web3'
3+
14
import {
25
LoanTransactionResponse,
36
LoanMetadataResponse,
47
LoanRequestResponse,
58
LoanAddressesByBorrowerResponse,
69
LoansAddressesResponse,
710
LoanAPIInstance,
8-
BaseResponse,
9-
BaseLoanModel
11+
BaseLoanModel,
12+
MaxLoanAmountResponse
1013
} from '../types'
11-
1214
import BaseService from './BaseService'
1315

1416
export default class LoanRequest extends BaseService implements LoanAPIInstance {
15-
constructor(token: string, apiUrl?: string) {
16-
super(token, apiUrl)
17-
}
18-
19-
private async apiRequest(
20-
endpoint: string,
21-
resourceType: string,
22-
errorParam: string = '',
23-
method: 'get' | 'post' = 'get',
24-
params?: object
25-
): Promise<BaseResponse> {
26-
const api = method === 'post' ? this.api.post : this.api.get
27-
28-
try {
29-
const { data } = await api(endpoint, params)
30-
31-
return { data, code: 200 }
32-
} catch (e) {
33-
return LoanRequest.errorHandler(e, resourceType, errorParam)
34-
}
17+
constructor(token: string, web3?: Web3, apiUrl?: string) {
18+
super(token, web3, apiUrl)
3519
}
3620

37-
public async create(creatorWalletAddress: string, params: BaseLoanModel): Promise<LoanTransactionResponse> {
38-
BaseService.checkAddressChecksum(creatorWalletAddress)
21+
public async create(borrowerWalletAddress: string, params: BaseLoanModel): Promise<LoanTransactionResponse> {
22+
BaseService.checkAddressChecksum(borrowerWalletAddress)
3923

40-
return await this.apiRequest(`/request/create/${creatorWalletAddress}`, 'loan request creation', '', 'post', params)
24+
return await this.apiRequest('/request', 'loan requests creation', '', 'post', {
25+
borrower: borrowerWalletAddress,
26+
...params
27+
})
4128
}
4229

4330
public async placeCollateral(loanAddress: string, borrowerAddress: string): Promise<LoanTransactionResponse> {
@@ -46,7 +33,7 @@ export default class LoanRequest extends BaseService implements LoanAPIInstance
4633

4734
return await this.apiRequest(
4835
`/request/placecollateral/${loanAddress}/${borrowerAddress}`,
49-
'placing loan request collateral',
36+
'placing loan requests collateral',
5037
loanAddress,
5138
'post'
5239
)
@@ -58,7 +45,7 @@ export default class LoanRequest extends BaseService implements LoanAPIInstance
5845

5946
return await this.apiRequest(
6047
`/request/fund/${loanAddress}/${lenderAddress}/${amount}`,
61-
'funding loan request',
48+
'funding loan requests',
6249
loanAddress,
6350
'post'
6451
)
@@ -70,29 +57,46 @@ export default class LoanRequest extends BaseService implements LoanAPIInstance
7057

7158
return await this.apiRequest(
7259
`/request/payback/${loanAddress}/${borrowerAddress}`,
73-
'placing loan request payback',
60+
'placing loan requests payback',
7461
loanAddress,
7562
'post'
7663
)
7764
}
65+
public async getMaxLoanAmountFromCollateral(
66+
collateralAmount: number,
67+
collateralType: string,
68+
moe: string,
69+
ltv?: number
70+
): Promise<MaxLoanAmountResponse> {
71+
return await this.apiRequest('/request/maxamount/', 'getting max loan amount', collateralType, 'post', {
72+
collateralAmount,
73+
collateralType,
74+
moe,
75+
ltv
76+
})
77+
}
7878

7979
public async getLoanData(loanAddress: string): Promise<LoanRequestResponse> {
8080
BaseService.checkAddressChecksum(loanAddress)
8181

82-
return await this.apiRequest(`/request/${loanAddress}`, 'loan request', loanAddress)
82+
return await this.apiRequest(`/request/getone/${loanAddress}`, 'loan requests', loanAddress)
8383
}
8484

8585
public async getAllAddresses(): Promise<LoansAddressesResponse> {
86-
return await this.apiRequest('/requests', 'loan request addresses')
86+
return await this.apiRequest('/request', 'loan requests addresses')
8787
}
8888

8989
public async getLoansByBorrower(borrowerAddress: string): Promise<LoanAddressesByBorrowerResponse> {
9090
BaseService.checkAddressChecksum(borrowerAddress)
9191

92-
return await this.apiRequest(`/requests/${borrowerAddress}`, 'loan addresses by borrower', borrowerAddress)
92+
return await this.apiRequest(
93+
`/request/getlistbyborrower/${borrowerAddress}`,
94+
'loan addresses by borrower',
95+
borrowerAddress
96+
)
9397
}
9498

9599
public async getMetadata(): Promise<LoanMetadataResponse> {
96-
return await this.apiRequest('/requests/metadata', 'loan requests metadata')
100+
return await this.apiRequest('/request/metadata', 'loan requests metadata')
97101
}
98102
}

src/types.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TransactionObject } from 'web3/eth/types'
1+
import { Transaction } from 'web3/eth/types'
22

33
export type ResponseCodes = 200 | 401 | 404 | 500 | 504
44

@@ -43,8 +43,12 @@ export interface BaseResponse {
4343
code: ResponseCodes
4444
}
4545

46+
export interface MaxLoanAmountResponse extends BaseResponse {
47+
data?: number
48+
}
49+
4650
export interface LoanTransactionResponse extends BaseResponse {
47-
data?: TransactionObject<any>
51+
data?: Transaction
4852
}
4953

5054
export interface LoanRequestResponse extends BaseResponse {
@@ -71,6 +75,12 @@ export interface LoanAPIInstance {
7175
placeCollateral(loanAddress: string, borrowerAddress: string): Promise<LoanTransactionResponse>
7276
fund(loanAddress: string, lenderAddress: string, amount: number): Promise<LoanTransactionResponse>
7377
payback(loanAddress: string, borrowerAddress: string): Promise<LoanTransactionResponse>
78+
getMaxLoanAmountFromCollateral(
79+
collateralAmount: number,
80+
collateralType: string,
81+
moe: string,
82+
ltv?: number
83+
): Promise<MaxLoanAmountResponse>
7484
getLoanData(loanAddress: string): Promise<LoanRequestResponse>
7585
getAllAddresses(): Promise<LoansAddressesResponse>
7686
getLoansByBorrower(borrowerAddress: string): Promise<LoanAddressesByBorrowerResponse>

test/BaseService.test.ts

+55-26
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,68 @@ import BaseService from '../src/services/BaseService'
22
/**
33
* BaseService test
44
*/
5-
describe('LoanRequest test', () => {
6-
const service = new BaseService('86C019FF04C4')
5+
describe('BaseService tests', () => {
6+
const defaultService = new BaseService('86C019FF04C4')
77

8-
it('LoanRequest is instantiable', () => {
9-
expect(service).toBeInstanceOf(BaseService)
8+
it('BaseService is instantiable', () => {
9+
expect(defaultService).toBeInstanceOf(BaseService)
1010
})
1111

12-
it('should check valid eth addresses without errors', () => {
13-
expect(BaseService['checkAddressChecksum']('0x71f4CF5Cfb74a9D3c9060aC4c25070F989cFC39C')).toBeUndefined()
14-
})
12+
describe('checkAddressChecksum method', () => {
13+
it('should check valid eth addresses without errors', () => {
14+
expect(BaseService['checkAddressChecksum']('0x71f4CF5Cfb74a9D3c9060aC4c25070F989cFC39C')).toBeUndefined()
15+
})
1516

16-
it('should throw error on checking invalid eth address', () => {
17-
expect(() => BaseService['checkAddressChecksum']('0x71f4CF5Cfb74a9D3c9060aC4c25070F989cFC39c')).toThrow()
18-
})
19-
const axiosErrorMock = (status: number) => ({
20-
message: '',
21-
name: '',
22-
config: {},
23-
response: { status, data: null, headers: null, statusText: '', config: {} }
24-
})
25-
it('should return correct error codes', () => {
26-
for (const status of [401, 404, 500, 504]) {
27-
expect(BaseService['errorHandler'](axiosErrorMock(status), 'type').code).toBe(status)
28-
}
17+
it('should throw error on checking invalid eth address', () => {
18+
expect(() => BaseService['checkAddressChecksum']('0x71f4CF5Cfb74a9D3c9060aC4c25070F989cFC39c')).toThrow()
19+
})
2920
})
3021

31-
it('should return 500 code on unknown status', () => {
32-
expect(BaseService['errorHandler'](axiosErrorMock(343), 'type').code).toBe(500)
22+
describe('errorHandler method', () => {
23+
const axiosErrorMock = (status: number) => ({
24+
message: '',
25+
name: '',
26+
config: {},
27+
response: { status, data: null, headers: null, statusText: '', config: {} }
28+
})
29+
it('should return correct error codes', () => {
30+
for (const status of [401, 404, 500, 504]) {
31+
expect(BaseService['errorHandler'](axiosErrorMock(status), 'type').code).toBe(status)
32+
}
33+
})
34+
35+
it('should return 500 code on unknown status', () => {
36+
expect(BaseService['errorHandler'](axiosErrorMock(343), 'type').code).toBe(500)
37+
})
38+
39+
it('should return 504 code if no response', () => {
40+
expect(BaseService['errorHandler']({ message: '', name: '', config: {}, response: undefined }, 'type').code).toBe(
41+
504
42+
)
43+
})
3344
})
3445

35-
it('should return 504 code if no response', () => {
36-
expect(BaseService['errorHandler']({ message: '', name: '', config: {}, response: undefined }, 'type').code).toBe(
37-
504
38-
)
46+
describe('sendTransaction method', () => {
47+
it('should send transaction', async () => {
48+
const tx = { some: 1 }
49+
const web3Mock = <any>{
50+
eth: {
51+
sendTransaction: async (tx: any) => Promise.resolve(tx),
52+
net: { getNetworkType: async () => Promise.resolve('kovan') }
53+
}
54+
}
55+
const service = new BaseService('token', web3Mock)
56+
await expect(service['sendTransaction'](<any>tx)).resolves.toEqual(tx)
57+
})
58+
59+
it('should raise exception if now web3 specified', async () => {
60+
await expect(defaultService['sendTransaction'](<any>{})).rejects.toBe('web3 provider is not specified')
61+
})
62+
63+
it('should raise exception on incorrect network', async () => {
64+
const web3Mock = <any>{ eth: { net: { getNetworkType: async () => Promise.resolve('main') } } }
65+
const service = new BaseService('86C019FF04C4', web3Mock)
66+
await expect(service['sendTransaction'](<any>{})).rejects.toBeTruthy()
67+
})
3968
})
4069
})

test/LoanRequest.test.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import LoanRequest from '../src/services/LoanRequest'
33
* LoanRequest test
44
*/
55
describe('LoanRequest test', () => {
6-
const request = new LoanRequest('86C019FF04C4', 'http://localhost:3333')
6+
const request = new LoanRequest('86C019FF04C4', undefined, 'http://localhost:3333')
77

88
it('LoanRequest is instantiable', () => {
99
expect(request).toBeInstanceOf(LoanRequest)
@@ -19,6 +19,11 @@ describe('LoanRequest test', () => {
1919
expect(data.code).toBe(200)
2020
})
2121

22+
it('Should load requests metadata from api', async () => {
23+
const data = await request.getMaxLoanAmountFromCollateral(2312, 'LEND', 'ETH')
24+
expect(data.code).toBe(200)
25+
})
26+
2227
it('Should get single loan data from api', async () => {
2328
const data = await request.getLoanData('0x71f4CF5Cfb74a9D3c9060aC4c25070F989cFC39C')
2429
expect(data.code).toBe(200)

test/aave-js.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import { Marketplace } from '../src/aave-js'
55
*/
66
describe('Marketplace test', () => {
77
it('Marketplace is instantiable', () => {
8-
expect(new Marketplace('token')).toBeInstanceOf(Marketplace)
8+
expect(new Marketplace('token', undefined, undefined)).toBeInstanceOf(Marketplace)
99
})
1010
})

0 commit comments

Comments
 (0)