From 52160e19d68ba806a320a048490c83f0de4e8403 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 23 Aug 2023 01:30:33 +0200 Subject: [PATCH 1/5] shift revert- and commitSendCoins to next isssue --- .../federation/client/1_0/SendCoinsClient.ts | 4 +-- .../client/1_0/query/commitSendCoins.ts | 25 ------------------- .../client/1_0/query/revertSendCoins.ts | 25 ------------------- 3 files changed, 2 insertions(+), 52 deletions(-) delete mode 100644 backend/src/federation/client/1_0/query/commitSendCoins.ts delete mode 100644 backend/src/federation/client/1_0/query/revertSendCoins.ts diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 0264d8b70..cc0cd7ca4 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -5,8 +5,6 @@ import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' import { SendCoinsArgs } from './model/SendCoinsArgs' -import { commitSendCoins } from './query/commitSendCoins' -import { revertSendCoins } from './query/revertSendCoins' import { voteForSendCoins } from './query/voteForSendCoins' // eslint-disable-next-line camelcase @@ -51,6 +49,7 @@ export class SendCoinsClient { } } + /* revertSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug(`X-Com: revertSendCoins against endpoint='${this.endpoint}'...`) try { @@ -84,4 +83,5 @@ export class SendCoinsClient { throw new LogError(`X-Com: commitSendCoins failed for endpoint=${this.endpoint}`, err) } } + */ } diff --git a/backend/src/federation/client/1_0/query/commitSendCoins.ts b/backend/src/federation/client/1_0/query/commitSendCoins.ts deleted file mode 100644 index eea934d00..000000000 --- a/backend/src/federation/client/1_0/query/commitSendCoins.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { gql } from 'graphql-request' - -export const commitSendCoins = gql` - mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! - $amount: Decimal! - $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! - ) { - commitSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier - amount: $amount - memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName - ) { - acknowledged - } - } -` diff --git a/backend/src/federation/client/1_0/query/revertSendCoins.ts b/backend/src/federation/client/1_0/query/revertSendCoins.ts deleted file mode 100644 index fd74feef1..000000000 --- a/backend/src/federation/client/1_0/query/revertSendCoins.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { gql } from 'graphql-request' - -export const revertSendCoins = gql` - mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! - $amount: Decimal! - $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! - ) { - revertSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier - amount: $amount - memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName - ) { - acknowledged - } - } -` From 53a6f1fec68f19d855149d3f5db6d1a2830351d6 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 24 Aug 2023 01:47:21 +0200 Subject: [PATCH 2/5] 1st implementation of voteForSendCoins --- .../client/1_0/model/SendCoinsArgs.ts | 5 +- federation/src/config/index.ts | 4 +- .../api/1_0/enum/PendingTransactionState.ts | 14 ++++ .../graphql/api/1_0/enum/TransactionTypeId.ts | 15 +++++ federation/src/graphql/api/1_0/model/Decay.ts | 41 ++++++++++++ .../graphql/api/1_0/model/SendCoinsArgs.ts | 29 ++++++++ .../api/1_0/resolver/SendCoinsResolver.ts | 66 +++++++++++++++++++ .../graphql/util/calculateRecepientBalance.ts | 20 ++++++ federation/src/graphql/util/decay.test.ts | 42 ++++++++++++ federation/src/graphql/util/decay.ts | 65 ++++++++++++++++++ federation/src/graphql/util/fullName.ts | 2 + .../src/graphql/util/getLastTransaction.ts | 12 ++++ federation/src/server/LogError.ts | 10 +++ 13 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 federation/src/graphql/api/1_0/enum/PendingTransactionState.ts create mode 100644 federation/src/graphql/api/1_0/enum/TransactionTypeId.ts create mode 100644 federation/src/graphql/api/1_0/model/Decay.ts create mode 100644 federation/src/graphql/api/1_0/model/SendCoinsArgs.ts create mode 100644 federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts create mode 100644 federation/src/graphql/util/calculateRecepientBalance.ts create mode 100644 federation/src/graphql/util/decay.test.ts create mode 100644 federation/src/graphql/util/decay.ts create mode 100644 federation/src/graphql/util/fullName.ts create mode 100644 federation/src/graphql/util/getLastTransaction.ts create mode 100644 federation/src/server/LogError.ts diff --git a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts index 2ba743368..3d15c04b1 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts @@ -1,5 +1,5 @@ import { Decimal } from 'decimal.js-light' -import { ArgsType, Field, Int } from 'type-graphql' +import { ArgsType, Field } from 'type-graphql' @ArgsType() export class SendCoinsArgs { @@ -9,6 +9,9 @@ export class SendCoinsArgs { @Field(() => String) userReceiverIdentifier: string + @Field(() => Date) + creationDate: Date + @Field(() => Decimal) amount: Decimal diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 29458d006..aceb15e98 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -12,13 +12,13 @@ Decimal.set({ const constants = { DB_VERSION: '0071-add-pending_transactions-table', - // DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 + DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info LOG_LEVEL: process.env.LOG_LEVEL || 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v1.2023-01-09', + EXPECTED: 'v2.2023-08-24', CURRENT: '', }, } diff --git a/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts b/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts new file mode 100644 index 000000000..6a614be96 --- /dev/null +++ b/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts @@ -0,0 +1,14 @@ +import { registerEnumType } from 'type-graphql' + +export enum PendingTransactionState { + NEW = 1, + WAIT_ON_PENDING = 2, + PENDING = 3, + WAIT_ON_CONFIRM = 4, + CONFIRMED = 5, +} + +registerEnumType(PendingTransactionState, { + name: 'PendingTransactionState', // this one is mandatory + description: 'State of the PendingTransaction', // this one is optional +}) diff --git a/federation/src/graphql/api/1_0/enum/TransactionTypeId.ts b/federation/src/graphql/api/1_0/enum/TransactionTypeId.ts new file mode 100644 index 000000000..a7e39eebc --- /dev/null +++ b/federation/src/graphql/api/1_0/enum/TransactionTypeId.ts @@ -0,0 +1,15 @@ +import { registerEnumType } from 'type-graphql' + +export enum TransactionTypeId { + CREATION = 1, + SEND = 2, + RECEIVE = 3, + // This is a virtual property, never occurring on the database + DECAY = 4, + LINK_SUMMARY = 5, +} + +registerEnumType(TransactionTypeId, { + name: 'TransactionTypeId', // this one is mandatory + description: 'Type of the transaction', // this one is optional +}) diff --git a/federation/src/graphql/api/1_0/model/Decay.ts b/federation/src/graphql/api/1_0/model/Decay.ts new file mode 100644 index 000000000..0b710c234 --- /dev/null +++ b/federation/src/graphql/api/1_0/model/Decay.ts @@ -0,0 +1,41 @@ +import { Decimal } from 'decimal.js-light' +import { ObjectType, Field, Int } from 'type-graphql' + +interface DecayInterface { + balance: Decimal + decay: Decimal + roundedDecay: Decimal + start: Date | null + end: Date | null + duration: number | null +} + +@ObjectType() +export class Decay { + constructor({ balance, decay, roundedDecay, start, end, duration }: DecayInterface) { + this.balance = balance + this.decay = decay + this.roundedDecay = roundedDecay + this.start = start + this.end = end + this.duration = duration + } + + @Field(() => Decimal) + balance: Decimal + + @Field(() => Decimal) + decay: Decimal + + @Field(() => Decimal) + roundedDecay: Decimal + + @Field(() => Date, { nullable: true }) + start: Date | null + + @Field(() => Date, { nullable: true }) + end: Date | null + + @Field(() => Int, { nullable: true }) + duration: number | null +} diff --git a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts new file mode 100644 index 000000000..3d15c04b1 --- /dev/null +++ b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts @@ -0,0 +1,29 @@ +import { Decimal } from 'decimal.js-light' +import { ArgsType, Field } from 'type-graphql' + +@ArgsType() +export class SendCoinsArgs { + @Field(() => String) + communityReceiverIdentifier: string + + @Field(() => String) + userReceiverIdentifier: string + + @Field(() => Date) + creationDate: Date + + @Field(() => Decimal) + amount: Decimal + + @Field(() => String) + memo: string + + @Field(() => String) + communitySenderIdentifier: string + + @Field(() => String) + userSenderIdentifier: string + + @Field(() => String) + userSenderName: string +} diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts new file mode 100644 index 000000000..06247a425 --- /dev/null +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -0,0 +1,66 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Mutation, Query, Resolver } from 'type-graphql' +import { federationLogger as logger } from '@/server/logger' +import { Community as DbCommunity } from '@entity/Community' +import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' +import { SendCoinsArgs } from '../model/SendCoinsArgs' +import { User as DbUser } from '@entity/User' +import { LogError } from '@/server/LogError' +import { PendingTransactionState } from '../enum/PendingTransactionState' +import { TransactionTypeId } from '../enum/TransactionTypeId' +import { calculateRecepientBalance } from '@/graphql/util/calculateRecepientBalance' +import Decimal from 'decimal.js-light' +import { fullName } from '@/graphql/util/fullName' + +@Resolver() +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export class SendCoinsResolver { + @Mutation(() => Boolean) + async voteForSendCoins(args: SendCoinsArgs): Promise { + logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`) + try { + // first check if receiver community is correct + const homeCom = await DbCommunity.findOneBy({ + communityUuid: args.communityReceiverIdentifier, + }) + if (!homeCom) { + throw new LogError(`voteForSendCoins with wrong communityReceiverIdentifier`) + } + // second check if receiver user exists in this community + const receiverUser = await DbUser.findOneBy({ gradidoID: args.userReceiverIdentifier }) + if (!receiverUser) { + throw new LogError( + `voteForSendCoins with unknown userReceiverIdentifier in the community=`, + homeCom.name, + ) + } + const receiveBalance = await calculateRecepientBalance( + receiverUser.id, + args.amount, + args.creationDate, + ) + const pendingTx = DbPendingTransaction.create() + pendingTx.amount = args.amount + pendingTx.balance = receiveBalance ? receiveBalance.balance : new Decimal(0) + pendingTx.balanceDate = args.creationDate + pendingTx.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) + pendingTx.decayStart = receiveBalance ? receiveBalance.decay.start : null + pendingTx.linkedUserCommunityUuid = args.communitySenderIdentifier + pendingTx.linkedUserGradidoID = args.userSenderIdentifier + pendingTx.linkedUserName = args.userSenderName + pendingTx.memo = args.memo + pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null + pendingTx.state = PendingTransactionState.NEW + pendingTx.typeId = TransactionTypeId.RECEIVE + pendingTx.userCommunityUuid = args.communityReceiverIdentifier + pendingTx.userGradidoID = args.userReceiverIdentifier + pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) + + await DbPendingTransaction.insert(pendingTx) + logger.debug(`voteForSendCoins()-1_0... successfull`) + } catch (err) { + throw new LogError(`Error in voteForSendCoins with args=`, args) + } + return true + } +} diff --git a/federation/src/graphql/util/calculateRecepientBalance.ts b/federation/src/graphql/util/calculateRecepientBalance.ts new file mode 100644 index 000000000..56914afba --- /dev/null +++ b/federation/src/graphql/util/calculateRecepientBalance.ts @@ -0,0 +1,20 @@ +import { Decimal } from 'decimal.js-light' + +import { getLastTransaction } from './getLastTransaction' +import { calculateDecay } from './decay' +import { Decay } from '../api/1_0/model/Decay' + +export async function calculateRecepientBalance( + userId: number, + amount: Decimal, + time: Date, +): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> { + const lastTransaction = await getLastTransaction(userId) + if (!lastTransaction) return null + + const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) + + const balance = decay.balance.add(amount.toString()) + + return { balance, lastTransactionId: lastTransaction.id, decay } +} diff --git a/federation/src/graphql/util/decay.test.ts b/federation/src/graphql/util/decay.test.ts new file mode 100644 index 000000000..1d4ebab3b --- /dev/null +++ b/federation/src/graphql/util/decay.test.ts @@ -0,0 +1,42 @@ +import { Decimal } from 'decimal.js-light' + +import { decayFormula, calculateDecay } from './decay' + +describe('utils/decay', () => { + describe('decayFormula', () => { + it('has base 0.99999997802044727', () => { + const amount = new Decimal(1.0) + const seconds = 1 + // TODO: toString() was required, we could not compare two decimals + expect(decayFormula(amount, seconds).toString()).toBe('0.999999978035040489732012') + }) + it('has correct backward calculation', () => { + const amount = new Decimal(1.0) + const seconds = -1 + expect(decayFormula(amount, seconds).toString()).toBe('1.000000021964959992727444') + }) + // we get pretty close, but not exact here, skipping + // eslint-disable-next-line jest/no-disabled-tests + it.skip('has correct forward calculation', () => { + const amount = new Decimal(1.0).div( + new Decimal('0.99999997803504048973201202316767079413460520837376'), + ) + const seconds = 1 + expect(decayFormula(amount, seconds).toString()).toBe('1.0') + }) + }) + it('has base 0.99999997802044727', () => { + const now = new Date() + now.setSeconds(1) + const oneSecondAgo = new Date(now.getTime()) + oneSecondAgo.setSeconds(0) + expect(calculateDecay(new Decimal(1.0), oneSecondAgo, now).balance.toString()).toBe( + '0.999999978035040489732012', + ) + }) + + it('returns input amount when from and to is the same', () => { + const now = new Date() + expect(calculateDecay(new Decimal(100.0), now, now).balance.toString()).toBe('100') + }) +}) diff --git a/federation/src/graphql/util/decay.ts b/federation/src/graphql/util/decay.ts new file mode 100644 index 000000000..f195075ff --- /dev/null +++ b/federation/src/graphql/util/decay.ts @@ -0,0 +1,65 @@ +import { Decimal } from 'decimal.js-light' + +import { LogError } from '@/server/LogError' +import CONFIG from '@/config' +import { Decay } from '../api/1_0/model/Decay' + +// TODO: externalize all those definitions and functions into an external decay library + +function decayFormula(value: Decimal, seconds: number): Decimal { + // TODO why do we need to convert this here to a stting to work properly? + return value.mul( + new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(), + ) +} + +function calculateDecay( + amount: Decimal, + from: Date, + to: Date, + startBlock: Date = CONFIG.DECAY_START_TIME, +): Decay { + const fromMs = from.getTime() + const toMs = to.getTime() + const startBlockMs = startBlock.getTime() + + if (toMs < fromMs) { + throw new LogError('calculateDecay: to < from, reverse decay calculation is invalid') + } + + // Initialize with no decay + const decay: Decay = { + balance: amount, + decay: new Decimal(0), + roundedDecay: new Decimal(0), + start: null, + end: null, + duration: null, + } + + // decay started after end date; no decay + if (startBlockMs > toMs) { + return decay + } + // decay started before start date; decay for full duration + if (startBlockMs < fromMs) { + decay.start = from + decay.duration = (toMs - fromMs) / 1000 + } + // decay started between start and end date; decay from decay start till end date + else { + decay.start = startBlock + decay.duration = (toMs - startBlockMs) / 1000 + } + + decay.end = to + decay.balance = decayFormula(amount, decay.duration) + decay.decay = decay.balance.minus(amount) + decay.roundedDecay = amount + .toDecimalPlaces(2, Decimal.ROUND_DOWN) + .minus(decay.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN).toString()) + .mul(-1) + return decay +} + +export { decayFormula, calculateDecay } diff --git a/federation/src/graphql/util/fullName.ts b/federation/src/graphql/util/fullName.ts new file mode 100644 index 000000000..7473f5ed0 --- /dev/null +++ b/federation/src/graphql/util/fullName.ts @@ -0,0 +1,2 @@ +export const fullName = (firstName: string, lastName: string): string => + [firstName, lastName].filter(Boolean).join(' ') diff --git a/federation/src/graphql/util/getLastTransaction.ts b/federation/src/graphql/util/getLastTransaction.ts new file mode 100644 index 000000000..0d7747088 --- /dev/null +++ b/federation/src/graphql/util/getLastTransaction.ts @@ -0,0 +1,12 @@ +import { Transaction as DbTransaction } from '@entity/Transaction' + +export const getLastTransaction = async ( + userId: number, + relations?: string[], +): Promise => { + return DbTransaction.findOne({ + where: { userId }, + order: { balanceDate: 'DESC', id: 'DESC' }, + relations, + }) +} diff --git a/federation/src/server/LogError.ts b/federation/src/server/LogError.ts new file mode 100644 index 000000000..b29e83dc8 --- /dev/null +++ b/federation/src/server/LogError.ts @@ -0,0 +1,10 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { federationLogger as logger } from './logger' + +export class LogError extends Error { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(msg: string, ...details: any[]) { + super(msg) + logger.error(msg, ...details) + } +} From 7d8b97b8611b559a4745fe9b8505486812379c29 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 24 Aug 2023 21:27:50 +0200 Subject: [PATCH 3/5] correct voteSendCoins graphql schema --- .../federation/client/1_0/SendCoinsClient.ts | 2 +- .../client/1_0/query/voteForSendCoins.ts | 6 +-- .../api/1_0/resolver/SendCoinsResolver.ts | 49 ++++++++++++------- .../scalar/{Decimal.ts.unused => Decimal.ts} | 5 +- federation/src/graphql/schema.ts | 6 +-- 5 files changed, 40 insertions(+), 28 deletions(-) rename federation/src/graphql/scalar/{Decimal.ts.unused => Decimal.ts} (74%) diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index cc0cd7ca4..bc6d9c58d 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -33,7 +33,7 @@ export class SendCoinsClient { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(voteForSendCoins, { args }) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.voteForSendCoins?.vote) { + if (!data?.voteForSendCoins) { logger.warn('X-Com: voteForSendCoins without response data from endpoint', this.endpoint) return false } diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts index 4f4dd892a..9cdae73f3 100644 --- a/backend/src/federation/client/1_0/query/voteForSendCoins.ts +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -4,6 +4,7 @@ export const voteForSendCoins = gql` mutation ( $communityReceiverIdentifier: String! $userReceiverIdentifier: String! + $creationDate: Date! $amount: Decimal! $memo: String! $communitySenderIdentifier: String! @@ -13,13 +14,12 @@ export const voteForSendCoins = gql` voteForSendCoins( communityReceiverIdentifier: $communityReceiverIdentifier userReceiverIdentifier: $userReceiverIdentifier + creationDate: $creationDate amount: $amount memo: $memo communitySenderIdentifier: $communitySenderIdentifier userSenderIdentifier: $userSenderIdentifier userSenderName: $userSenderName - ) { - vote - } + ) } ` diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 06247a425..dd8958e9f 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Mutation, Query, Resolver } from 'type-graphql' +import { Args, Mutation, Query, Resolver } from 'type-graphql' import { federationLogger as logger } from '@/server/logger' import { Community as DbCommunity } from '@entity/Community' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' @@ -16,50 +16,61 @@ import { fullName } from '@/graphql/util/fullName' // eslint-disable-next-line @typescript-eslint/no-unused-vars export class SendCoinsResolver { @Mutation(() => Boolean) - async voteForSendCoins(args: SendCoinsArgs): Promise { + async voteForSendCoins( + @Args() + { + communityReceiverIdentifier, + userReceiverIdentifier, + creationDate, + amount, + memo, + communitySenderIdentifier, + userSenderIdentifier, + userSenderName, + }: SendCoinsArgs, + ): Promise { logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`) try { // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: args.communityReceiverIdentifier, + communityUuid: communityReceiverIdentifier, }) if (!homeCom) { - throw new LogError(`voteForSendCoins with wrong communityReceiverIdentifier`) + throw new LogError( + `voteForSendCoins with wrong communityReceiverIdentifier`, + communityReceiverIdentifier, + ) } // second check if receiver user exists in this community - const receiverUser = await DbUser.findOneBy({ gradidoID: args.userReceiverIdentifier }) + const receiverUser = await DbUser.findOneBy({ gradidoID: userReceiverIdentifier }) if (!receiverUser) { throw new LogError( `voteForSendCoins with unknown userReceiverIdentifier in the community=`, homeCom.name, ) } - const receiveBalance = await calculateRecepientBalance( - receiverUser.id, - args.amount, - args.creationDate, - ) + const receiveBalance = await calculateRecepientBalance(receiverUser.id, amount, creationDate) const pendingTx = DbPendingTransaction.create() - pendingTx.amount = args.amount + pendingTx.amount = amount pendingTx.balance = receiveBalance ? receiveBalance.balance : new Decimal(0) - pendingTx.balanceDate = args.creationDate + pendingTx.balanceDate = creationDate pendingTx.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) pendingTx.decayStart = receiveBalance ? receiveBalance.decay.start : null - pendingTx.linkedUserCommunityUuid = args.communitySenderIdentifier - pendingTx.linkedUserGradidoID = args.userSenderIdentifier - pendingTx.linkedUserName = args.userSenderName - pendingTx.memo = args.memo + pendingTx.linkedUserCommunityUuid = communitySenderIdentifier + pendingTx.linkedUserGradidoID = userSenderIdentifier + pendingTx.linkedUserName = userSenderName + pendingTx.memo = memo pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null pendingTx.state = PendingTransactionState.NEW pendingTx.typeId = TransactionTypeId.RECEIVE - pendingTx.userCommunityUuid = args.communityReceiverIdentifier - pendingTx.userGradidoID = args.userReceiverIdentifier + pendingTx.userCommunityUuid = communityReceiverIdentifier + pendingTx.userGradidoID = userReceiverIdentifier pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) await DbPendingTransaction.insert(pendingTx) logger.debug(`voteForSendCoins()-1_0... successfull`) } catch (err) { - throw new LogError(`Error in voteForSendCoins with args=`, args) + throw new LogError(`Error in voteForSendCoins: `, err) } return true } diff --git a/federation/src/graphql/scalar/Decimal.ts.unused b/federation/src/graphql/scalar/Decimal.ts similarity index 74% rename from federation/src/graphql/scalar/Decimal.ts.unused rename to federation/src/graphql/scalar/Decimal.ts index da5a99e0c..96804bdfa 100644 --- a/federation/src/graphql/scalar/Decimal.ts.unused +++ b/federation/src/graphql/scalar/Decimal.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Decimal } from 'decimal.js-light' import { GraphQLScalarType, Kind } from 'graphql' -import Decimal from 'decimal.js-light' -export default new GraphQLScalarType({ +export const DecimalScalar = new GraphQLScalarType({ name: 'Decimal', description: 'The `Decimal` scalar type to represent currency values', diff --git a/federation/src/graphql/schema.ts b/federation/src/graphql/schema.ts index 015ea124f..0951c1000 100644 --- a/federation/src/graphql/schema.ts +++ b/federation/src/graphql/schema.ts @@ -2,15 +2,15 @@ import { GraphQLSchema } from 'graphql' import { buildSchema } from 'type-graphql' // import isAuthorized from './directive/isAuthorized' -// import DecimalScalar from './scalar/Decimal' -// import Decimal from 'decimal.js-light' +import { DecimalScalar } from './scalar/Decimal' +import { Decimal } from 'decimal.js-light' import { getApiResolvers } from './api/schema' const schema = async (): Promise => { return await buildSchema({ resolvers: [getApiResolvers()], // authChecker: isAuthorized, - // scalarsMap: [{ type: Decimal, scalar: DecimalScalar }], + scalarsMap: [{ type: Decimal, scalar: DecimalScalar }], }) } From 48af8a7a90e35c81a25c174125468440bf6f7949 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 25 Aug 2023 03:21:18 +0200 Subject: [PATCH 4/5] first draft of write senderPendingTX --- backend/src/config/index.ts | 5 +- .../federation/client/1_0/SendCoinsClient.ts | 19 +++-- .../client/1_0/model/SendCoinsResult.ts | 17 ++++ .../resolver/util/processXComSendCoins.ts | 80 +++++++++++++++++++ backend/src/util/calculateSenderBalance.ts | 21 +++++ .../graphql/api/1_0/model/SendCoinsResult.ts | 17 ++++ .../api/1_0/resolver/SendCoinsResolver.ts | 9 ++- 7 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 backend/src/federation/client/1_0/model/SendCoinsResult.ts create mode 100644 backend/src/graphql/resolver/util/processXComSendCoins.ts create mode 100644 backend/src/util/calculateSenderBalance.ts create mode 100644 federation/src/graphql/api/1_0/model/SendCoinsResult.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 744f1d3cc..9fbf66507 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -19,7 +19,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v18.2023-07-10', + EXPECTED: 'v19.2023-08-25', CURRENT: '', }, } @@ -124,6 +124,9 @@ if ( const federation = { FEDERATION_VALIDATE_COMMUNITY_TIMER: Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) || 60000, + // default value for community-uuid is equal uuid of stage-3 + FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID: + process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc', } export const CONFIG = { diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index bc6d9c58d..e15e13100 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -6,6 +6,7 @@ import { backendLogger as logger } from '@/server/logger' import { SendCoinsArgs } from './model/SendCoinsArgs' import { voteForSendCoins } from './query/voteForSendCoins' +import { SendCoinsResult } from './model/SendCoinsResult' // eslint-disable-next-line camelcase export class SendCoinsClient { @@ -27,23 +28,25 @@ export class SendCoinsClient { }) } - voteForSendCoins = async (args: SendCoinsArgs): Promise => { + voteForSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug('X-Com: voteForSendCoins against endpoint', this.endpoint) try { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(voteForSendCoins, { args }) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.voteForSendCoins) { - logger.warn('X-Com: voteForSendCoins without response data from endpoint', this.endpoint) - return false + if (!data?.voteForSendCoins?.SendCoinsResult.vote) { + logger.warn( + 'X-Com: voteForSendCoins failed with: ', + data?.voteForSendCoins?.SendCoinsResult, + ) + return null } logger.debug( - 'X-Com: voteForSendCoins successful from endpoint', - this.endpoint, + 'X-Com: voteForSendCoins successful with result=', // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - data.voteForSendCoins.vote, + data.voteForSendCoins.SendCoinsResult, ) - return true + return data.voteForSendCoins.SendCoinsResult } catch (err) { throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err) } diff --git a/backend/src/federation/client/1_0/model/SendCoinsResult.ts b/backend/src/federation/client/1_0/model/SendCoinsResult.ts new file mode 100644 index 000000000..1897410cc --- /dev/null +++ b/backend/src/federation/client/1_0/model/SendCoinsResult.ts @@ -0,0 +1,17 @@ +import { ArgsType, Field } from 'type-graphql' + +@ArgsType() +export class SendCoinsResult { + constructor() { + this.vote = false + } + + @Field(() => Boolean) + vote: boolean + + @Field(() => String) + receiverFirstName: string + + @Field(() => String) + receiverLastName: string +} diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts new file mode 100644 index 000000000..957522f65 --- /dev/null +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -0,0 +1,80 @@ +import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' +import { User as dbUser } from '@entity/User' +import { Decimal } from 'decimal.js-light' + +import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs' +// eslint-disable-next-line camelcase +import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' +import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory' +import { backendLogger as logger } from '@/server/logger' +import { CONFIG } from '@/config' +import { fullName } from '@/util/utilities' +import { calculateSenderBalance } from '@/util/calculateSenderBalance' +import { LogError } from '@/server/LogError' + + +export async function processXComSendCoins( + receiverFCom: DbFederatedCommunity, + senderFCom: DbFederatedCommunity, + receiverCom: DbCommunity, + senderCom: DbCommunity, + creationDate: Date, + amount: Decimal, + memo: string, + sender: dbUser, + recipient: dbUser, +): Promise { + try { + const senderBalance = await calculateSenderBalance(sender.id, amount.mul(-1), creationDate) + if (!senderBalance) { + throw new LogError('User has not enough GDD or amount is < 0', senderBalance) + } + + const client = SendCoinsClientFactory.getInstance(receiverFCom) + // eslint-disable-next-line camelcase + if (client instanceof V1_0_SendCoinsClient) { + const args = new SendCoinsArgs() + args.communityReceiverIdentifier = receiverCom.communityUuid + ? receiverCom.communityUuid + : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID + args.userReceiverIdentifier = recipient.gradidoID + args.creationDate = creationDate + args.amount = amount + args.memo = memo + args.communitySenderIdentifier = senderCom.communityUuid + ? senderCom.communityUuid + : 'homeCom-UUID' + args.userSenderIdentifier = sender.gradidoID + args.userSenderName = fullName(sender.firstName, sender.lastName) + const result = await client.voteForSendCoins(args) + if(result) { + const pendingTx = DbPendingTransaction.create() + pendingTx.amount = amount.mul(-1) + pendingTx.balance = senderBalance ? senderBalance.balance : new Decimal(0) + pendingTx.balanceDate = creationDate + pendingTx.decay = senderBalance ? senderBalance.decay.decay : new Decimal(0) + pendingTx.decayStart = senderBalance ? senderBalance.decay.start : null + pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid + ? receiverCom.communityUuid + : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID + pendingTx.linkedUserGradidoID = recipient.gradidoID + pendingTx.linkedUserName = userSenderName + pendingTx.memo = memo + pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null + pendingTx.state = PendingTransactionState.NEW + pendingTx.typeId = TransactionTypeId.RECEIVE + pendingTx.userCommunityUuid = communityReceiverIdentifier + pendingTx.userGradidoID = userReceiverIdentifier + pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) + + await DbPendingTransaction.insert(pendingTx) + logger.debug(`voteForSendCoins()-1_0... successfull`) + } + } + } catch (err) { + logger.error(`Error:`, err) + } + return true +} diff --git a/backend/src/util/calculateSenderBalance.ts b/backend/src/util/calculateSenderBalance.ts new file mode 100644 index 000000000..89e417d35 --- /dev/null +++ b/backend/src/util/calculateSenderBalance.ts @@ -0,0 +1,21 @@ +import { Decimal } from 'decimal.js-light' + +import { Decay } from '@model/Decay' + +import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction' + +import { calculateDecay } from './decay' + +export async function calculateSenderBalance( + userId: number, + amount: Decimal, + time: Date, +): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> { + const lastTransaction = await getLastTransaction(userId) + if (!lastTransaction) return null + + const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) + + const balance = decay.balance.add(amount.toString()) + return { balance, lastTransactionId: lastTransaction.id, decay } +} diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts new file mode 100644 index 000000000..1897410cc --- /dev/null +++ b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts @@ -0,0 +1,17 @@ +import { ArgsType, Field } from 'type-graphql' + +@ArgsType() +export class SendCoinsResult { + constructor() { + this.vote = false + } + + @Field(() => Boolean) + vote: boolean + + @Field(() => String) + receiverFirstName: string + + @Field(() => String) + receiverLastName: string +} diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index dd8958e9f..73c6e077c 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -11,6 +11,7 @@ import { TransactionTypeId } from '../enum/TransactionTypeId' import { calculateRecepientBalance } from '@/graphql/util/calculateRecepientBalance' import Decimal from 'decimal.js-light' import { fullName } from '@/graphql/util/fullName' +import { SendCoinsResult } from '../model/SendCoinsResult' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -28,7 +29,8 @@ export class SendCoinsResolver { userSenderIdentifier, userSenderName, }: SendCoinsArgs, - ): Promise { + ): Promise { + const result = new SendCoinsResult() logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`) try { // first check if receiver community is correct @@ -68,10 +70,13 @@ export class SendCoinsResolver { pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) await DbPendingTransaction.insert(pendingTx) + result.vote = true + result.receiverFirstName = receiverUser.firstName + result.receiverLastName = receiverUser.lastName logger.debug(`voteForSendCoins()-1_0... successfull`) } catch (err) { throw new LogError(`Error in voteForSendCoins: `, err) } - return true + return result } } From a8eaa6f35328c06e21e4acb77349cdf0e4f904b8 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 25 Aug 2023 16:50:02 +0200 Subject: [PATCH 5/5] rework writing the pendingTX on both sides --- .../federation/client/1_0/SendCoinsClient.ts | 15 ++--- .../resolver/util/processXComSendCoins.ts | 61 +++++++++++-------- .../graphql/api/1_0/model/SendCoinsResult.ts | 17 ------ .../api/1_0/resolver/SendCoinsResolver.ts | 9 +-- 4 files changed, 46 insertions(+), 56 deletions(-) delete mode 100644 federation/src/graphql/api/1_0/model/SendCoinsResult.ts diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index e15e13100..f599dbafd 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -6,7 +6,6 @@ import { backendLogger as logger } from '@/server/logger' import { SendCoinsArgs } from './model/SendCoinsArgs' import { voteForSendCoins } from './query/voteForSendCoins' -import { SendCoinsResult } from './model/SendCoinsResult' // eslint-disable-next-line camelcase export class SendCoinsClient { @@ -28,25 +27,27 @@ export class SendCoinsClient { }) } - voteForSendCoins = async (args: SendCoinsArgs): Promise => { + voteForSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug('X-Com: voteForSendCoins against endpoint', this.endpoint) try { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(voteForSendCoins, { args }) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.voteForSendCoins?.SendCoinsResult.vote) { + if (!data?.voteForSendCoins?.voteForSendCoins) { logger.warn( 'X-Com: voteForSendCoins failed with: ', - data?.voteForSendCoins?.SendCoinsResult, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + data.voteForSendCoins.voteForSendCoins, ) - return null + return } logger.debug( 'X-Com: voteForSendCoins successful with result=', // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - data.voteForSendCoins.SendCoinsResult, + data.voteForSendCoins, ) - return data.voteForSendCoins.SendCoinsResult + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access + return data.voteForSendCoins.voteForSendCoins } catch (err) { throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err) } diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 957522f65..1cd854c60 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -4,16 +4,17 @@ import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTrans import { User as dbUser } from '@entity/User' import { Decimal } from 'decimal.js-light' +import { CONFIG } from '@/config' import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs' // eslint-disable-next-line camelcase import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory' -import { backendLogger as logger } from '@/server/logger' -import { CONFIG } from '@/config' -import { fullName } from '@/util/utilities' -import { calculateSenderBalance } from '@/util/calculateSenderBalance' +import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' +import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId' import { LogError } from '@/server/LogError' - +import { backendLogger as logger } from '@/server/logger' +import { calculateSenderBalance } from '@/util/calculateSenderBalance' +import { fullName } from '@/util/utilities' export async function processXComSendCoins( receiverFCom: DbFederatedCommunity, @@ -27,6 +28,7 @@ export async function processXComSendCoins( recipient: dbUser, ): Promise { try { + // first calculate the sender balance and check if the transaction is allowed const senderBalance = await calculateSenderBalance(sender.id, amount.mul(-1), creationDate) if (!senderBalance) { throw new LogError('User has not enough GDD or amount is < 0', senderBalance) @@ -48,28 +50,35 @@ export async function processXComSendCoins( : 'homeCom-UUID' args.userSenderIdentifier = sender.gradidoID args.userSenderName = fullName(sender.firstName, sender.lastName) - const result = await client.voteForSendCoins(args) - if(result) { - const pendingTx = DbPendingTransaction.create() - pendingTx.amount = amount.mul(-1) - pendingTx.balance = senderBalance ? senderBalance.balance : new Decimal(0) - pendingTx.balanceDate = creationDate - pendingTx.decay = senderBalance ? senderBalance.decay.decay : new Decimal(0) - pendingTx.decayStart = senderBalance ? senderBalance.decay.start : null - pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid - ? receiverCom.communityUuid - : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID - pendingTx.linkedUserGradidoID = recipient.gradidoID - pendingTx.linkedUserName = userSenderName - pendingTx.memo = memo - pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null - pendingTx.state = PendingTransactionState.NEW - pendingTx.typeId = TransactionTypeId.RECEIVE - pendingTx.userCommunityUuid = communityReceiverIdentifier - pendingTx.userGradidoID = userReceiverIdentifier - pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) + const recipientName = await client.voteForSendCoins(args) + if (recipientName) { + // writing the pending transaction on receiver-side was successfull, so now write the sender side + try { + const pendingTx = DbPendingTransaction.create() + pendingTx.amount = amount.mul(-1) + pendingTx.balance = senderBalance ? senderBalance.balance : new Decimal(0) + pendingTx.balanceDate = creationDate + pendingTx.decay = senderBalance ? senderBalance.decay.decay : new Decimal(0) + pendingTx.decayStart = senderBalance ? senderBalance.decay.start : null + pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid + ? receiverCom.communityUuid + : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID + pendingTx.linkedUserGradidoID = recipient.gradidoID + pendingTx.linkedUserName = recipientName + pendingTx.memo = memo + pendingTx.previous = senderBalance ? senderBalance.lastTransactionId : null + pendingTx.state = PendingTransactionState.NEW + pendingTx.typeId = TransactionTypeId.SEND + if (senderCom.communityUuid) pendingTx.userCommunityUuid = senderCom.communityUuid + pendingTx.userGradidoID = sender.gradidoID + pendingTx.userName = fullName(sender.firstName, sender.lastName) - await DbPendingTransaction.insert(pendingTx) + await DbPendingTransaction.insert(pendingTx) + } catch (err) { + logger.error(`Error in writing sender pending transaction: `, err) + // revert the existing pending transaction on receiver side + // TODO in the issue #3186 + } logger.debug(`voteForSendCoins()-1_0... successfull`) } } diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts deleted file mode 100644 index 1897410cc..000000000 --- a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ArgsType, Field } from 'type-graphql' - -@ArgsType() -export class SendCoinsResult { - constructor() { - this.vote = false - } - - @Field(() => Boolean) - vote: boolean - - @Field(() => String) - receiverFirstName: string - - @Field(() => String) - receiverLastName: string -} diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 73c6e077c..ba23ae530 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -11,7 +11,6 @@ import { TransactionTypeId } from '../enum/TransactionTypeId' import { calculateRecepientBalance } from '@/graphql/util/calculateRecepientBalance' import Decimal from 'decimal.js-light' import { fullName } from '@/graphql/util/fullName' -import { SendCoinsResult } from '../model/SendCoinsResult' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -29,9 +28,9 @@ export class SendCoinsResolver { userSenderIdentifier, userSenderName, }: SendCoinsArgs, - ): Promise { - const result = new SendCoinsResult() + ): Promise { logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`) + let result: string | null = null try { // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ @@ -70,9 +69,7 @@ export class SendCoinsResolver { pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) await DbPendingTransaction.insert(pendingTx) - result.vote = true - result.receiverFirstName = receiverUser.firstName - result.receiverLastName = receiverUser.lastName + result = pendingTx.userName logger.debug(`voteForSendCoins()-1_0... successfull`) } catch (err) { throw new LogError(`Error in voteForSendCoins: `, err)