From 9550c31cac7bf51f8e05694a2b8eebc26fe0eff7 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 14:27:53 +0100 Subject: [PATCH 01/10] set type for redeemed by, change order --- .../0030-transaction_link/TransactionLink.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/database/entity/0030-transaction_link/TransactionLink.ts b/database/entity/0030-transaction_link/TransactionLink.ts index d04c00d7b..a3ab5cd1a 100644 --- a/database/entity/0030-transaction_link/TransactionLink.ts +++ b/database/entity/0030-transaction_link/TransactionLink.ts @@ -39,13 +39,6 @@ export class TransactionLink extends BaseEntity { }) validUntil: Date - @Column({ - type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', - nullable: true, - }) - redeemedAt: Date - @Column({ type: 'boolean', default: () => false, @@ -53,6 +46,13 @@ export class TransactionLink extends BaseEntity { }) showEmail: boolean - @Column({ unsigned: true, nullable: true }) - redeemedBy: number + @Column({ + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + nullable: true, + }) + redeemedAt?: Date | null + + @Column({ type: 'int', unsigned: true, nullable: true }) + redeemedBy?: number | null } From dee6fd9d0a9f73b0adea13de42f94f0c8e3fb42a Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 14:59:21 +0100 Subject: [PATCH 02/10] fix createdAt is now --- backend/src/graphql/resolver/TransactionLinkResolver.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 920e18569..46cdf5071 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -24,7 +24,8 @@ export const transactionLinkCode = (date: Date): string => { const transactionLinkExpireDate = (date: Date): Date => { // valid for 14 days - return new Date(date.setDate(date.getDate() + 14)) + const validUntil = new Date(date) + return new Date(validUntil.setDate(date.getDate() + 14)) } @Resolver() From 91c7b051850498a4c7125dbfb41b5cb885d9a51c Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:00:34 +0100 Subject: [PATCH 03/10] remove default dates --- database/entity/0030-transaction_link/TransactionLink.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/database/entity/0030-transaction_link/TransactionLink.ts b/database/entity/0030-transaction_link/TransactionLink.ts index a3ab5cd1a..5e8690270 100644 --- a/database/entity/0030-transaction_link/TransactionLink.ts +++ b/database/entity/0030-transaction_link/TransactionLink.ts @@ -27,14 +27,12 @@ export class TransactionLink extends BaseEntity { @Column({ type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', nullable: false, }) createdAt: Date @Column({ type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', nullable: false, }) validUntil: Date @@ -48,7 +46,6 @@ export class TransactionLink extends BaseEntity { @Column({ type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', nullable: true, }) redeemedAt?: Date | null From f78f2d5977df4fa0a0b98671102ff94dc46775ef Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:00:34 +0100 Subject: [PATCH 04/10] remove default dates --- database/entity/0030-transaction_link/TransactionLink.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/database/entity/0030-transaction_link/TransactionLink.ts b/database/entity/0030-transaction_link/TransactionLink.ts index a3ab5cd1a..5e8690270 100644 --- a/database/entity/0030-transaction_link/TransactionLink.ts +++ b/database/entity/0030-transaction_link/TransactionLink.ts @@ -27,14 +27,12 @@ export class TransactionLink extends BaseEntity { @Column({ type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', nullable: false, }) createdAt: Date @Column({ type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', nullable: false, }) validUntil: Date @@ -48,7 +46,6 @@ export class TransactionLink extends BaseEntity { @Column({ type: 'datetime', - default: () => 'CURRENT_TIMESTAMP', nullable: true, }) redeemedAt?: Date | null From 14a0a4344d542c4738c578063b803d121c4aa827 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:07:55 +0100 Subject: [PATCH 05/10] Add hold_available_amount column --- .../entity/0030-transaction_link/TransactionLink.ts | 10 ++++++++++ database/migrations/0030-transaction_link.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/database/entity/0030-transaction_link/TransactionLink.ts b/database/entity/0030-transaction_link/TransactionLink.ts index 5e8690270..620f19903 100644 --- a/database/entity/0030-transaction_link/TransactionLink.ts +++ b/database/entity/0030-transaction_link/TransactionLink.ts @@ -19,6 +19,16 @@ export class TransactionLink extends BaseEntity { }) amount: Decimal + @Column({ + type: 'decimal', + name: 'hold_available_amount', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + holdAvailableAmount: Decimal + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) memo: string diff --git a/database/migrations/0030-transaction_link.ts b/database/migrations/0030-transaction_link.ts index 59eba1090..83d7b072d 100644 --- a/database/migrations/0030-transaction_link.ts +++ b/database/migrations/0030-transaction_link.ts @@ -9,6 +9,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`id\` int UNSIGNED NOT NULL AUTO_INCREMENT, \`userId\` int UNSIGNED NOT NULL, \`amount\` DECIMAL(40,20) NOT NULL, + \`hold_available_amount\` DECIMAL(40,20) NOT NULL, \`memo\` varchar(255) NOT NULL, \`code\` varchar(96) NOT NULL, \`createdAt\` datetime NOT NULL, From 8bdd25cd0157752321acbbfa7a697184a275fb46 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:09:01 +0100 Subject: [PATCH 06/10] add hold available amount to model --- backend/src/graphql/model/TransactionLink.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index c46728407..f449a6f9e 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -13,6 +13,9 @@ export class TransactionLink { @Field(() => Decimal) amount: Decimal + @Field(() => Decimal) + holdAvailableAmount: Decimal + @Field(() => String) memo: string From 54dee861dd84e8e21ce75b9a27118c51a45e7381 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:12:25 +0100 Subject: [PATCH 07/10] reduce length of code --- database/entity/0030-transaction_link/TransactionLink.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/entity/0030-transaction_link/TransactionLink.ts b/database/entity/0030-transaction_link/TransactionLink.ts index 620f19903..bb12277d1 100644 --- a/database/entity/0030-transaction_link/TransactionLink.ts +++ b/database/entity/0030-transaction_link/TransactionLink.ts @@ -32,7 +32,7 @@ export class TransactionLink extends BaseEntity { @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) memo: string - @Column({ length: 96, nullable: false, collation: 'utf8mb4_unicode_ci' }) + @Column({ length: 24, nullable: false, collation: 'utf8mb4_unicode_ci' }) code: string @Column({ From 55e50b10c7d0c5e6bc80a32167d327316ec2634e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:14:15 +0100 Subject: [PATCH 08/10] reduce length of code in migration --- database/migrations/0030-transaction_link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/0030-transaction_link.ts b/database/migrations/0030-transaction_link.ts index 83d7b072d..4d72cf43b 100644 --- a/database/migrations/0030-transaction_link.ts +++ b/database/migrations/0030-transaction_link.ts @@ -11,7 +11,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`amount\` DECIMAL(40,20) NOT NULL, \`hold_available_amount\` DECIMAL(40,20) NOT NULL, \`memo\` varchar(255) NOT NULL, - \`code\` varchar(96) NOT NULL, + \`code\` varchar(24) NOT NULL, \`createdAt\` datetime NOT NULL, \`validUntil\` datetime NOT NULL, \`showEmail\` boolean NOT NULL DEFAULT false, From 6d6f58a9322ac79467f2fcd53b4ee8dc9cd4319c Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 15:43:13 +0100 Subject: [PATCH 09/10] save hold available amount --- backend/src/graphql/model/TransactionLink.ts | 1 + .../resolver/TransactionLinkResolver.test.ts | 4 ++-- .../resolver/TransactionLinkResolver.ts | 18 +++++++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index c518f0f6c..1670e9a23 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -9,6 +9,7 @@ export class TransactionLink { this.id = transactionLink.id this.user = user this.amount = transactionLink.amount + this.holdAvailableAmount = transactionLink.holdAvailableAmount this.memo = transactionLink.memo this.code = transactionLink.code this.createdAt = transactionLink.createdAt diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index 51790502d..5a1a39dca 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -3,8 +3,8 @@ import { transactionLinkCode } from './TransactionLinkResolver' describe('transactionLinkCode', () => { const date = new Date() - it('returns a string of length 96', () => { - expect(transactionLinkCode(date)).toHaveLength(96) + it('returns a string of length 24', () => { + expect(transactionLinkCode(date)).toHaveLength(24) }) it('returns a string that ends with the hex value of date', () => { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 46cdf5071..657337d74 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -11,20 +11,21 @@ import { calculateBalance } from '@/util/validate' import { RIGHTS } from '@/auth/RIGHTS' import { randomBytes } from 'crypto' import { User } from '@model/User' +import { calculateDecay } from '@/util/decay' // TODO: do not export, test it inside the resolver export const transactionLinkCode = (date: Date): string => { const time = date.getTime().toString(16) return ( - randomBytes(48) + randomBytes(12) .toString('hex') - .substring(0, 96 - time.length) + time + .substring(0, 24 - time.length) + time ) } const transactionLinkExpireDate = (date: Date): Date => { - // valid for 14 days const validUntil = new Date(date) + // valid for 14 days return new Date(validUntil.setDate(date.getDate() + 14)) } @@ -42,7 +43,13 @@ export class TransactionLinkResolver { // validate amount // TODO taken from transaction resolver, duplicate code const createdDate = new Date() - const sendBalance = await calculateBalance(user.id, amount.mul(-1), createdDate) + const validUntil = transactionLinkExpireDate(createdDate) + + const holdAvailableAmount = amount.add( + calculateDecay(amount, createdDate, validUntil).decay.mul(-1), + ) + + const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate) if (!sendBalance) { throw new Error("user hasn't enough GDD or amount is < 0") } @@ -53,9 +60,10 @@ export class TransactionLinkResolver { transactionLink.userId = user.id transactionLink.amount = amount transactionLink.memo = memo + transactionLink.holdAvailableAmount = holdAvailableAmount transactionLink.code = transactionLinkCode(createdDate) transactionLink.createdAt = createdDate - transactionLink.validUntil = transactionLinkExpireDate(createdDate) + transactionLink.validUntil = validUntil transactionLink.showEmail = showEmail await dbTransactionLink.save(transactionLink).catch((error) => { throw error From f4f63a9519ad84f224a80a984f21d53eda93e875 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Mar 2022 16:26:23 +0100 Subject: [PATCH 10/10] take pending transaction links into acount when testing for available balance --- .../resolver/TransactionLinkResolver.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 657337d74..7cd76a954 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Args, Authorized, Ctx, Mutation } from 'type-graphql' -import { getCustomRepository } from '@dbTools/typeorm' +import { getCustomRepository, MoreThan } from '@dbTools/typeorm' import { TransactionLink } from '@model/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import TransactionLinkArgs from '@arg/TransactionLinkArgs' @@ -40,8 +40,6 @@ export class TransactionLinkResolver { const userRepository = getCustomRepository(UserRepository) const user = await userRepository.findByPubkeyHex(context.pubKey) - // validate amount - // TODO taken from transaction resolver, duplicate code const createdDate = new Date() const validUntil = transactionLinkExpireDate(createdDate) @@ -49,13 +47,24 @@ export class TransactionLinkResolver { calculateDecay(amount, createdDate, validUntil).decay.mul(-1), ) - const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate) + const openTransactionLinks = await dbTransactionLink.find({ + select: ['holdAvailableAmount'], + where: { userId: user.id, redeemedAt: null, validUntil: MoreThan(createdDate) }, + }) + + const holdAvailable = openTransactionLinks.reduce( + (previousValue, currentValue) => + previousValue.add(currentValue.holdAvailableAmount.toString()), + holdAvailableAmount, + ) + + // validate amount + // TODO taken from transaction resolver, duplicate code + const sendBalance = await calculateBalance(user.id, holdAvailable.mul(-1), createdDate) if (!sendBalance) { throw new Error("user hasn't enough GDD or amount is < 0") } - // TODO!!!! Test balance for pending transaction links - const transactionLink = dbTransactionLink.create() transactionLink.userId = user.id transactionLink.amount = amount