Skip to content

Commit

Permalink
Merge pull request #10 from TLX-Protocol/remove-registrations
Browse files Browse the repository at this point in the history
Remove Registration Wndpoints and Replace with Simple Example Endpoint
  • Loading branch information
chase-manning authored Apr 14, 2024
2 parents 7a27d4e + f4f8224 commit fe781b5
Show file tree
Hide file tree
Showing 8 changed files with 18 additions and 469 deletions.
113 changes: 4 additions & 109 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,7 @@
# Scope
# TLX API

## Database
A collection of API endpoints in TS deployed on Firebase for use with the TLX Protocol.

The database will be built using Firebase Firestore Database.
## Endpoints

We will have two tables, one to store information about users, and one to store information about invite codes.

### User:

- `address` **primary key** Users's wallet address
- `twitter_access_token` The access token of the user's Twitter account
- `twitter_secret` The secret of the user's Twitter account
- `discord_access_token` The access token of the user's Discord account
- `discord_secret` The secret of the user's Discord account

### Codes:

- `invite_code` **primary key** The invite code
- `creator` Who created the invite code
- `user` **optional** The user who claimed in invite code

## API

The API will be a JavaScript Firebase Function with HTTP trigger.

### Register

The register endpoint is a POST endpoint.
It takes as an input the full user data including address, twitter info and discord info. And the invite code.
The endpoint first validates that the Twitter info is valid.
It then validates that the user is following us on Twitter.
It then validates that the Discord info is valid.
It then validates that the user is in our Discord.
It then validates that the signature is valid for the address.
It then validates that the address hasn't already been used in our database.
It then validates that the Twitter hasn't already been used in our database.
It then validates that the Discord hasn't already been used in our database.
It then validates that the invite code is valid, and not used.
It then updates that invite code as claimed and with the users address.
It then generates 5 invite codes for the user, and writes them to the database.
It then writes the user data to the database.
It then returns the invite codes in the response.
If any of the validations fail, it returns an appropriate error message to display on the UI.

### Has Registered

The Has Registered endpoint is a GET endpoint.
It takes an addres as an input.
It returns `true` or `false` based on if the user has already registered.

### Invite Codes

The Invite Codes endpoint is a GET endpoint.
It takes an addres as an input, and a signature to prove that the caller owns the address.
It returns a list of Invite codes for the user.

### Is Invite Code Used

The Is Invite Code Used endpoint is a GET endpoint.
It takes an invite code as an input.
It returns `true` or `false` based on if the invite code has been used yet.
If the invite code doesn't exist, it returns `false` (this is to prevent brute forcing to find invite codes).

## Front end

### Register button

The top right Twitter button will be replaced with a Rebalance button.

### Connect wallet popup

A popup will be added for the users to connect their wallet when they click on the Register button

### Check if user is registered

A check will be done once the user's wallet is connected to see if they have registered yet.

### Register popup

If the user has not registered yet, a Register popup will show.
The popup will have the header "Register".
The popup will have 5 steps.
There will be an input field for inputting your invite code.
There will be a section for authenticating with Twitter. This will call to the Twitter API to register the user and get their access token and secret.
There will be a section for following us on Twitter which will open our Twitter in a new tab.
There will be a section for authenticating with Discord. This will call to the Discord API to register the user and get their access token and secret.
There will be a section for joining our Discord which will open our Discord in a new tab.
There will be a "Register" button at the bottom. This will be disabled until all the steps above are complete.
As the user complete each step above, a visual indicator will update to show this is done.
When the user clicks the register button, it will make a call to our register API to attemp to register them.
If there is an error, it will show in an error popup.
If it works, then the user will be redirected to the Invite Code view.

### Sign for invite codes

If the users wallet is registered then they click on the "Register" button, it will show a popup.
The popup will have the header "Sign transaction to view invite codes".
It will have a button "View Invite Codes".
When the click that button it will make them sign a transaction, and then make a request to our api to get their invite codes.
It will then redirect to the invite codes popup.

### Invite Codes

This view will show a list of the user's invite codes.
For each invite code it will call our API endpoint to check if that Invite code has been used.
If it has been used, there will be a visual indicator for this.
Next to each invite code will be a copy button allowing users to copy the invite code.

### Privacy Policy

Our Privacy Policy will need to be updated now that we are collecting additional information from our users.
- `example` Just a read endpoint that returns "Hello world"
36 changes: 1 addition & 35 deletions functions/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1 @@
import { OauthCredentials, ServiceType } from "./types";

export interface Secrets {
twitterClientID: string;
twitterClientSecret: string;
discordClientID: string;
discordClientSecret: string;
}

export const redirectURI = "https://tlx.fi/register";

export function getCredentials(service: ServiceType, secrets: Secrets): OauthCredentials {
return {
clientID: service === ServiceType.Twitter ? secrets.twitterClientID : secrets.discordClientID,
clientSecret: service === ServiceType.Twitter ? secrets.twitterClientSecret : secrets.discordClientSecret,
};
}

export const oauthURLs = {
[ServiceType.Twitter]: "https://api.twitter.com/2/oauth2/token",
[ServiceType.Discord]: "https://discord.com/api/oauth2/token",
};

export const tlxGuidID = "1142096814573621308";

export const messageToSign =
"I agree to the TLX Terms of Service (https://tlx.fi/terms-of-service)" +
" and TLX Privacy Policy (https://tlx.fi/privacy-policiy)." +
" I acknowledge that TLX integrates with third-party applications, which may come with risks";

export const codeValidChars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
export const codeLength = 4;
export const invitesPerUser = 5;

export const partnerCodes = ["kirbycrypto", "raccooncrypto", "yungmatt", "cwesearch"];
export const EXAMPLE_CONST = "FOO";
41 changes: 9 additions & 32 deletions functions/src/db.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,19 @@
import admin from "firebase-admin";

import { createRandomString } from "./utils";
import { codeLength } from "./constants";

export async function usernameExists(key: string, username: string): Promise<boolean> {
const db = admin.database();
const snapshot = await db.ref("users").orderByChild(key).equalTo(username).get();
return snapshot.exists();
}

export async function userExists(address: string): Promise<boolean> {
const db = admin.database();
const snapshot = await db.ref("users").child(address).get();
return snapshot.exists();
}

export async function useCode(code: string, user: string): Promise<void> {
export async function exampleRead(id: string): Promise<string> {
const db = admin.database();
await db.ref("invites").child(code).update({ used: true, usedAt: Date.now(), user });
const snapshot = await db.ref("databaseName").child(id).get();
return snapshot.val().value;
}

async function generateInviteCode(): Promise<string> {
export async function exampleUpdate(id: string, value: string): Promise<void> {
const db = admin.database();
let code: string;
do {
code = createRandomString(codeLength);
} while ((await db.ref("invites").child(code).get()).exists());
return code;
await db.ref("databaseName").child(id).update({ value });
}

export async function generateAndSaveInviteCodes(user: string, count: number): Promise<string[]> {
export async function exampleWrite(id: string, value: string): Promise<string> {
const db = admin.database();
const codes = [];
for (let i = 0; i < count; i++) {
const code = await generateInviteCode();
const codeMetadata = { creator: user, used: false, createdAt: Date.now(), code };
codes.push(code);
await db.ref("invites").child(code).set(codeMetadata);
}
return codes;
const data = { id, value };
await db.ref("databaseName").child(id).set(data);
return value;
}
63 changes: 4 additions & 59 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,12 @@
import { onRequest } from "firebase-functions/v2/https";
import admin from "firebase-admin";

import registrationHandler from "./registration";
import wrapHandler, { getUserAddress, isPartnerCode, validateParams } from "./utils";
import { userExists } from "./db";
import { APIError, SignedParams } from "./types";
import { defineSecret } from "firebase-functions/params";

const twitterClientID = defineSecret("TWITTER_CLIENT_ID");
const twitterClientSecret = defineSecret("TWITTER_CLIENT_SECRET");
const discordClientID = defineSecret("DISCORD_CLIENT_ID");
const discordClientSecret = defineSecret("DISCORD_CLIENT_SECRET");
import wrapHandler from "./utils";

admin.initializeApp();

export const register = onRequest(
{ secrets: [twitterClientID, twitterClientSecret, discordClientID, discordClientSecret] },
wrapHandler(async (request) => {
await registrationHandler(request, {
twitterClientID: twitterClientID.value(),
twitterClientSecret: twitterClientSecret.value(),
discordClientID: discordClientID.value(),
discordClientSecret: discordClientSecret.value(),
});
})
);

export const inviteCodes = onRequest(
wrapHandler(async (request) => {
const { signature } = validateParams<SignedParams>(request.query, "signature");

const user = getUserAddress(signature);
if (!(await userExists(user))) {
throw new APIError("Address not registered");
}

const db = admin.database();
const snapshot = await db.ref("invites").orderByChild("creator").equalTo(user).get();
return snapshot.val();
})
);

export const hasRegistered = onRequest(
wrapHandler(async (request) => {
const { signature } = validateParams<SignedParams>(request.query, "signature");

const user = getUserAddress(signature);
return { hasRegistered: await userExists(user) };
})
);

export const inviteCodeUsed = onRequest(
wrapHandler(async (request) => {
const { inviteCode } = validateParams<{ inviteCode: string }>(request.query, "inviteCode");

const isPartner = isPartnerCode(inviteCode);
if (isPartner) return false;

const db = admin.database();
const codeSnapshot = await db.ref("invites").child(inviteCode).get();
if (!codeSnapshot.exists()) {
throw new APIError("Invalid invite code");
}
return codeSnapshot.val().used;
export const example = onRequest(
wrapHandler(async () => {
return { message: "Hello, world!" };
})
);
93 changes: 0 additions & 93 deletions functions/src/registration.ts

This file was deleted.

Loading

0 comments on commit fe781b5

Please sign in to comment.