2020-11-04 13:35:20 +01:00
|
|
|
import { Injectable } from '@angular/core';
|
2021-05-10 18:15:25 +02:00
|
|
|
import { first } from 'rxjs/operators';
|
|
|
|
import { BehaviorSubject, Observable } from 'rxjs';
|
|
|
|
import { environment } from '@src/environments/environment';
|
|
|
|
import { Envelope, User } from 'cic-client-meta';
|
|
|
|
import { UserService } from '@app/_services/user.service';
|
2021-03-03 14:43:33 +01:00
|
|
|
import { Keccak } from 'sha3';
|
|
|
|
import { utils } from 'ethers';
|
2021-05-10 18:15:25 +02:00
|
|
|
import { add0x, fromHex, strip0x, toHex } from '@src/assets/js/ethtx/dist/hex';
|
|
|
|
import { Tx } from '@src/assets/js/ethtx/dist';
|
|
|
|
import { toValue } from '@src/assets/js/ethtx/dist/tx';
|
2021-03-03 14:43:33 +01:00
|
|
|
import * as secp256k1 from 'secp256k1';
|
2021-05-10 18:15:25 +02:00
|
|
|
import { AuthService } from '@app/_services/auth.service';
|
|
|
|
import { defaultAccount } from '@app/_models';
|
|
|
|
import { LoggingService } from '@app/_services/logging.service';
|
|
|
|
import { HttpClient } from '@angular/common/http';
|
|
|
|
import { CICRegistry } from 'cic-client';
|
|
|
|
import { RegistryService } from '@app/_services/registry.service';
|
2021-04-29 19:10:39 +02:00
|
|
|
import Web3 from 'web3';
|
2021-05-26 10:30:38 +02:00
|
|
|
import { Web3Service } from '@app/_services/web3.service';
|
2021-03-04 16:56:14 +01:00
|
|
|
const vCard = require('vcard-parser');
|
2020-11-04 13:35:20 +01:00
|
|
|
|
|
|
|
@Injectable({
|
2021-05-10 18:15:25 +02:00
|
|
|
providedIn: 'root',
|
2020-11-04 13:35:20 +01:00
|
|
|
})
|
|
|
|
export class TransactionService {
|
2020-12-05 07:29:18 +01:00
|
|
|
transactions: any[] = [];
|
|
|
|
private transactionList = new BehaviorSubject<any[]>(this.transactions);
|
2020-11-25 08:51:15 +01:00
|
|
|
transactionsSubject = this.transactionList.asObservable();
|
|
|
|
userInfo: any;
|
2021-04-29 19:10:39 +02:00
|
|
|
web3: Web3;
|
2021-04-20 10:28:40 +02:00
|
|
|
registry: CICRegistry;
|
2020-11-04 13:35:20 +01:00
|
|
|
|
2020-11-25 08:51:15 +01:00
|
|
|
constructor(
|
2021-03-21 12:02:18 +01:00
|
|
|
private httpClient: HttpClient,
|
2021-03-03 14:43:33 +01:00
|
|
|
private authService: AuthService,
|
2021-03-14 09:19:25 +01:00
|
|
|
private userService: UserService,
|
2021-05-19 18:57:10 +02:00
|
|
|
private loggingService: LoggingService
|
2021-04-20 10:28:40 +02:00
|
|
|
) {
|
2021-05-20 20:27:06 +02:00
|
|
|
this.web3 = Web3Service.getInstance();
|
2021-05-19 18:57:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async init(): Promise<void> {
|
|
|
|
await this.authService.init();
|
|
|
|
await this.userService.init();
|
|
|
|
this.registry = await RegistryService.getRegistry();
|
2021-04-20 10:28:40 +02:00
|
|
|
}
|
2020-11-04 13:35:20 +01:00
|
|
|
|
|
|
|
getAllTransactions(offset: number, limit: number): Observable<any> {
|
2021-03-21 12:02:18 +01:00
|
|
|
return this.httpClient.get(`${environment.cicCacheUrl}/tx/${offset}/${limit}`);
|
2020-11-04 13:35:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getAddressTransactions(address: string, offset: number, limit: number): Observable<any> {
|
2021-05-18 21:10:08 +02:00
|
|
|
return this.httpClient.get(`${environment.cicCacheUrl}/tx/user/${address}/${offset}/${limit}`);
|
2020-11-04 13:35:20 +01:00
|
|
|
}
|
2020-11-08 07:31:52 +01:00
|
|
|
|
2021-03-04 16:56:14 +01:00
|
|
|
async setTransaction(transaction, cacheSize: number): Promise<void> {
|
2021-05-10 18:15:25 +02:00
|
|
|
if (this.transactions.find((cachedTx) => cachedTx.tx.txHash === transaction.tx.txHash)) {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-10 10:47:01 +01:00
|
|
|
transaction.value = Number(transaction.value);
|
2020-12-05 07:29:18 +01:00
|
|
|
transaction.type = 'transaction';
|
2021-03-10 10:47:01 +01:00
|
|
|
try {
|
2021-05-10 18:15:25 +02:00
|
|
|
this.userService
|
|
|
|
.getAccountDetailsFromMeta(await User.toKey(transaction.from))
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe(
|
|
|
|
(res) => {
|
2021-05-19 09:56:39 +02:00
|
|
|
transaction.sender = this.getAccountInfo(res, cacheSize);
|
2021-05-10 18:15:25 +02:00
|
|
|
},
|
|
|
|
(error) => {
|
|
|
|
transaction.sender = defaultAccount;
|
2021-05-19 09:56:39 +02:00
|
|
|
this.userService.addAccount(defaultAccount, cacheSize);
|
2021-05-10 18:15:25 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
this.userService
|
|
|
|
.getAccountDetailsFromMeta(await User.toKey(transaction.to))
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe(
|
|
|
|
(res) => {
|
2021-05-19 09:56:39 +02:00
|
|
|
transaction.recipient = this.getAccountInfo(res, cacheSize);
|
2021-05-10 18:15:25 +02:00
|
|
|
},
|
|
|
|
(error) => {
|
|
|
|
transaction.recipient = defaultAccount;
|
2021-05-19 09:56:39 +02:00
|
|
|
this.userService.addAccount(defaultAccount, cacheSize);
|
2021-05-10 18:15:25 +02:00
|
|
|
}
|
|
|
|
);
|
2021-03-10 10:47:01 +01:00
|
|
|
} finally {
|
2021-03-16 11:08:18 +01:00
|
|
|
this.addTransaction(transaction, cacheSize);
|
2021-03-10 10:47:01 +01:00
|
|
|
}
|
2020-11-08 07:31:52 +01:00
|
|
|
}
|
|
|
|
|
2021-03-04 16:56:14 +01:00
|
|
|
async setConversion(conversion, cacheSize): Promise<void> {
|
2021-05-10 18:15:25 +02:00
|
|
|
if (this.transactions.find((cachedTx) => cachedTx.tx.txHash === conversion.tx.txHash)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-12-05 07:29:18 +01:00
|
|
|
conversion.type = 'conversion';
|
2021-03-10 10:47:01 +01:00
|
|
|
conversion.fromValue = Number(conversion.fromValue);
|
|
|
|
conversion.toValue = Number(conversion.toValue);
|
|
|
|
try {
|
2021-05-10 18:15:25 +02:00
|
|
|
this.userService
|
|
|
|
.getAccountDetailsFromMeta(await User.toKey(conversion.trader))
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe(
|
|
|
|
(res) => {
|
2021-05-18 21:10:08 +02:00
|
|
|
conversion.sender = conversion.recipient = this.getAccountInfo(res);
|
2021-05-10 18:15:25 +02:00
|
|
|
},
|
|
|
|
(error) => {
|
|
|
|
conversion.sender = conversion.recipient = defaultAccount;
|
2021-05-19 09:56:39 +02:00
|
|
|
this.userService.addAccount(defaultAccount, cacheSize);
|
2021-05-10 18:15:25 +02:00
|
|
|
}
|
|
|
|
);
|
2021-03-10 10:47:01 +01:00
|
|
|
} finally {
|
2021-03-16 11:08:18 +01:00
|
|
|
this.addTransaction(conversion, cacheSize);
|
2021-03-10 10:47:01 +01:00
|
|
|
}
|
2020-11-25 08:51:15 +01:00
|
|
|
}
|
|
|
|
|
2021-03-16 11:08:18 +01:00
|
|
|
addTransaction(transaction, cacheSize: number): void {
|
2021-05-19 09:56:39 +02:00
|
|
|
const savedIndex = this.transactions.findIndex((tx) => tx.tx.txHash === transaction.tx.txHash);
|
|
|
|
if (savedIndex === 0) {
|
|
|
|
return;
|
|
|
|
}
|
2021-05-18 15:08:25 +02:00
|
|
|
if (savedIndex > 0) {
|
|
|
|
this.transactions.splice(savedIndex, 1);
|
|
|
|
}
|
2020-12-05 07:29:18 +01:00
|
|
|
this.transactions.unshift(transaction);
|
|
|
|
if (this.transactions.length > cacheSize) {
|
2021-05-18 15:08:25 +02:00
|
|
|
this.transactions.length = Math.min(this.transactions.length, cacheSize);
|
2020-12-05 07:29:18 +01:00
|
|
|
}
|
|
|
|
this.transactionList.next(this.transactions);
|
|
|
|
}
|
|
|
|
|
2021-03-16 11:08:18 +01:00
|
|
|
resetTransactionsList(): void {
|
|
|
|
this.transactions = [];
|
|
|
|
this.transactionList.next(this.transactions);
|
|
|
|
}
|
|
|
|
|
2021-05-19 09:56:39 +02:00
|
|
|
getAccountInfo(account: string, cacheSize: number = 100): any {
|
2021-06-04 14:16:57 +02:00
|
|
|
const accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data;
|
2021-03-10 10:47:01 +01:00
|
|
|
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
2021-05-19 09:56:39 +02:00
|
|
|
this.userService.addAccount(accountInfo, cacheSize);
|
2021-03-10 10:47:01 +01:00
|
|
|
return accountInfo;
|
2020-11-08 07:31:52 +01:00
|
|
|
}
|
2021-02-08 12:47:07 +01:00
|
|
|
|
2021-05-10 18:15:25 +02:00
|
|
|
async transferRequest(
|
|
|
|
tokenAddress: string,
|
|
|
|
senderAddress: string,
|
|
|
|
recipientAddress: string,
|
|
|
|
value: number
|
|
|
|
): Promise<any> {
|
2021-05-18 11:38:27 +02:00
|
|
|
this.registry.onload = async (addressReturned: string): Promise<void> => {
|
|
|
|
const transferAuthAddress = await this.registry.getContractAddressByName(
|
|
|
|
'TransferAuthorization'
|
|
|
|
);
|
|
|
|
const hashFunction = new Keccak(256);
|
|
|
|
hashFunction.update('createRequest(address,address,address,uint256)');
|
|
|
|
const hash = hashFunction.digest();
|
|
|
|
const methodSignature = hash.toString('hex').substring(0, 8);
|
|
|
|
const abiCoder = new utils.AbiCoder();
|
|
|
|
const abi = await abiCoder.encode(
|
|
|
|
['address', 'address', 'address', 'uint256'],
|
|
|
|
[senderAddress, recipientAddress, tokenAddress, value]
|
|
|
|
);
|
|
|
|
const data = fromHex(methodSignature + strip0x(abi));
|
|
|
|
const tx = new Tx(environment.bloxbergChainId);
|
|
|
|
tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
|
|
|
|
tx.gasPrice = Number(await this.web3.eth.getGasPrice());
|
|
|
|
tx.gasLimit = 8000000;
|
|
|
|
tx.to = fromHex(strip0x(transferAuthAddress));
|
|
|
|
tx.value = toValue(value);
|
|
|
|
tx.data = data;
|
|
|
|
const txMsg = tx.message();
|
|
|
|
const privateKey = this.authService.mutableKeyStore.getPrivateKey();
|
|
|
|
if (!privateKey.isDecrypted()) {
|
|
|
|
const password = window.prompt('password');
|
|
|
|
await privateKey.decrypt(password);
|
|
|
|
}
|
|
|
|
const signatureObject = secp256k1.ecdsaSign(txMsg, privateKey.keyPacket.privateParams.d);
|
|
|
|
const r = signatureObject.signature.slice(0, 32);
|
|
|
|
const s = signatureObject.signature.slice(32);
|
|
|
|
const v = signatureObject.recid;
|
|
|
|
tx.setSignature(r, s, v);
|
|
|
|
const txWire = add0x(toHex(tx.serializeRLP()));
|
|
|
|
const result = await this.web3.eth.sendSignedTransaction(txWire);
|
|
|
|
this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
|
|
|
|
const transaction = await this.web3.eth.getTransaction(result.transactionHash);
|
|
|
|
this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
|
|
|
|
};
|
2021-02-08 12:47:07 +01:00
|
|
|
}
|
2020-11-04 13:35:20 +01:00
|
|
|
}
|