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

Stellar address uniqueness for project creation/update #1790

Merged
merged 9 commits into from
Sep 2, 2024
19 changes: 19 additions & 0 deletions migration/1725188424424-UniqueProjectAdressWithMomoForStellar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class UniqueProjectAdressWithMomoForStellar1725188424424
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE UNIQUE INDEX unique_stellar_address
ON project_address (address, memo)
WHERE "chainType" = 'STELLAR';
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
DROP INDEX unique_stellar_address;
`);
}
}
2 changes: 1 addition & 1 deletion src/entities/draftDonation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class DraftDonation extends BaseEntity {
@Column({ nullable: true })
relevantDonationTxHash?: string;

@Field()
@Field(_type => String, { nullable: true })
@Column({ nullable: true })
toWalletMemo?: string;

Expand Down
22 changes: 19 additions & 3 deletions src/repositories/projectAddressRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const isWalletAddressInPurpleList = async (
export const findRelatedAddressByWalletAddress = async (
walletAddress: string,
chainType?: ChainType,
memo?: string,
) => {
let query = ProjectAddress.createQueryBuilder('projectAddress');

Expand All @@ -50,9 +51,24 @@ export const findRelatedAddressByWalletAddress = async (
});
break;
case ChainType.STELLAR:
query = query.where(`UPPER(address) = :walletAddress`, {
walletAddress: walletAddress.toUpperCase(),
});
// If a memo is provided, check for both address and memo
if (memo) {
query = query.where(
'UPPER(address) = :walletAddress AND memo = :memo',
{
walletAddress: walletAddress.toUpperCase(),
memo,
},
);
} else {
// If no memo is provided, check only the address
query = query.where(
'UPPER(address) = :walletAddress AND memo IS NULL',
{
walletAddress: walletAddress.toUpperCase(),
},
);
}
break;
case ChainType.EVM:
default:
Expand Down
10 changes: 7 additions & 3 deletions src/resolvers/projectResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,7 @@ export class ProjectResolver {
);
}

await validateProjectWalletAddress(address, projectId, chainType);
await validateProjectWalletAddress(address, projectId, chainType, memo);

const adminUser = (await findUserById(project.adminUserId)) as User;
await addNewProjectAddress({
Expand Down Expand Up @@ -1791,8 +1791,12 @@ export class ProjectResolver {
* @returns
*/
@Query(_returns => Boolean)
async walletAddressIsValid(@Arg('address') address: string) {
return validateProjectWalletAddress(address);
async walletAddressIsValid(
@Arg('address') address: string,
@Arg('chainType', { nullable: true }) chainType?: ChainType,
@Arg('memo', { nullable: true }) memo?: string,
) {
return validateProjectWalletAddress(address, undefined, chainType, memo);
}

/**
Expand Down
14 changes: 11 additions & 3 deletions src/utils/validators/projectValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const validateProjectWalletAddress = async (
walletAddress: string,
projectId?: number,
chainType?: ChainType,
memo?: string,
): Promise<boolean> => {
if (!isWalletAddressValid(walletAddress, chainType)) {
throw new Error(
Expand All @@ -40,11 +41,18 @@ export const validateProjectWalletAddress = async (
const relatedAddress = await findRelatedAddressByWalletAddress(
walletAddress,
chainType,
memo,
);
if (relatedAddress && relatedAddress?.project?.id !== projectId) {
throw new Error(
`Address ${walletAddress} is already being used for a project`,
);
if (chainType === ChainType.STELLAR && memo) {
throw new Error(
`Address ${walletAddress} is already being used for a project with the same MEMO. Please enter a different address or a different MEMO`,
);
} else {
throw new Error(
`Address ${walletAddress} is already being used for a project`,
);
}
}
return true;
};
Expand Down
Loading