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

Fetch permits #383

Draft
wants to merge 19 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"sonarjs/no-identical-expressions": "error",
"@typescript-eslint/naming-convention": [
"error",
{ "selector": "interface", "format": ["StrictPascalCase"], "custom": { "regex": "^I[A-Z]", "match": false } },
{ "selector": "interface", "format": ["PascalCase"], "custom": { "regex": "^I[A-Z]", "match": false } },
{ "selector": "memberLike", "modifiers": ["private"], "format": ["strictCamelCase"], "leadingUnderscore": "require" },
{ "selector": "typeLike", "format": ["StrictPascalCase"] },
{ "selector": "typeParameter", "format": ["StrictPascalCase"], "prefix": ["T"] },
Expand Down
1 change: 1 addition & 0 deletions build/esbuild-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const typescriptEntries = ["static/scripts/rewards/init.ts", "static/scripts/ubi
export const entries = [...typescriptEntries];

export const esBuildContext: esbuild.BuildOptions = {
external: ["@ubiquity-os/permit-generation"],
sourcemap: true,
entryPoints: entries,
bundle: true,
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@
"@ethersproject/abi": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@ethersproject/units": "^5.7.0",
"@octokit/rest": "^21.1.1",
"@supabase/supabase-js": "^2.44.4",
"@ubiquibot/permit-generation": "^1.4.1",
"@ubiquity-dao/rpc-handler": "^1.3.0",
"@ubiquity-os/ethers-decode-error": "^1.1.0",
"countries-and-timezones": "^3.6.0",
"dotenv": "^16.4.4",
"ethers": "^5.7.2",
"@ubiquity-os/ethers-decode-error": "^1.1.0",
"npm-run-all": "^4.1.5",
"zod": "^3.23.8"
},
Expand All @@ -68,6 +69,7 @@
"@types/node": "^20.11.19",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"@typescript-eslint/parser": "^7.0.1",
"@ubiquity-os/permit-generation": "^2.0.6",
"cspell": "^8.4.0",
"cypress": "^13.7.0",
"esbuild": "^0.20.1",
Expand Down
14 changes: 14 additions & 0 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@
</div>
</a>
</header>
<div id="github-login" class="login">
<button type="button" id="github-login-button">
<span>
<svg class="github-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 98">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
/>
</svg>
</span>
<span>Login With GitHub</span>
</button>
</div>
<div class="receipt-container">
<table class="receipt" data-details-visible="false" data-make-claim-rendered="false" data-contract-loaded="false" data-make-claim="error">
<thead>
Expand Down
10 changes: 5 additions & 5 deletions static/scripts/rewards/app-state.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { JsonRpcProvider, JsonRpcSigner } from "@ethersproject/providers";
import { Permit } from "@ubiquibot/permit-generation";
import type { PermitReward } from "@ubiquity-os/permit-generation";
import { getNetworkExplorer } from "@ubiquity-dao/rpc-handler";
import { convertToNetworkId } from "../../../shared/use-rpc-handler";

export class AppState {
public claims: Permit[] = [];
public claims: PermitReward[] = [];
public claimTxs: Record<string, string> = {};
private _provider!: JsonRpcProvider;
private _currentIndex = 0;
Expand Down Expand Up @@ -34,7 +34,7 @@ export class AppState {
return this._currentIndex;
}

get reward(): Permit {
get reward(): PermitReward {
return this.rewardIndex < this.claims.length ? this.claims[this.rewardIndex] : this.claims[0];
}

Expand All @@ -51,12 +51,12 @@ export class AppState {
return getNetworkExplorer(networkId)[0].url;
}

nextPermit(): Permit | null {
nextPermit(): PermitReward | null {
this._currentIndex = Math.min(this.claims.length - 1, this.rewardIndex + 1);
return this.reward;
}

previousPermit(): Permit | null {
previousPermit(): PermitReward | null {
this._currentIndex = Math.max(0, this._currentIndex - 1);
return this.reward;
}
Expand Down
132 changes: 132 additions & 0 deletions static/scripts/rewards/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { getLocalStore, setLocalStore } from "./local-store";
import { supabase } from "./render-transaction/supabase-getters";

export const GITHUB_ACCEPT_HEADER = "application/vnd.github+json";

export interface OAuthToken {
provider_token: string;
access_token: string;
expires_in: number;
expires_at: number;
refresh_token: string;
token_type: string;
user: User;
}

export interface UserMetadata {
avatar_url: string;
email: string;
email_verified: boolean;
full_name: string;
iss: string;
name: string;
phone_verified: boolean;
preferred_username: string;
provider_id: string;
sub: string;
user_name: string;
}

export interface Identity {
id: string;
user_id: string;
identity_data: {
avatar_url: string;
email: string;
email_verified: boolean;
full_name: string;
iss: string;
name: string;
phone_verified: boolean;
preferred_username: string;
provider_id: string;
sub: string;
user_name: string;
};
provider: string;
last_sign_in_at: string;
created_at: string;
updated_at: string;
}

export interface User {
id: string;
aud: string;
role: string;
email: string;
email_confirmed_at: string;
phone: string;
confirmed_at: string;
last_sign_in_at: string;
app_metadata: { provider: string; providers: string[] };
user_metadata: UserMetadata;
identities: Array<Identity>;
created_at: string;
updated_at: string;
}

/**
* Handles GitHub login button click
* Initiates OAuth flow with required scopes
*/
export async function gitHubLoginButtonHandler() {
const { error } = await supabase.auth.signInWithOAuth({
provider: "github",
options: {
redirectTo: window.location.href,
// Request minimum required scope:
// - public_repo to create public repositories
scopes: "public_repo",
},
});
if (error) {
console.error("Error logging in:", error);
}
}

/**
* Gets session token from either:
* 1. URL hash after OAuth redirect
* 2. Cached session in local storage
*/
export function getSessionToken() {
// cSpell: ignore wfzpewmlyiozupulbuur
const cachedSessionToken = getLocalStore<OAuthToken>("sb-wfzpewmlyiozupulbuur-auth-token");
if (cachedSessionToken) {
return cachedSessionToken.provider_token;
}
const newSessionToken = getNewSessionToken();
if (newSessionToken) {
return newSessionToken;
}
return null;
}

function getNewSessionToken() {
const hash = window.location.hash;
const params = new URLSearchParams(hash.substring(1)); // remove the '#' and parse
const providerToken = params.get("provider_token");
if (!providerToken) {
return null;
}
setLocalStore("sb-wfzpewmlyiozupulbuur-auth-token", {
provider_token: providerToken,
});
return providerToken;
}

export function initializeAuth() {
const token = getSessionToken();
const loginButton = document.getElementById("github-login") as HTMLDivElement;
const gitHubLoginButton = document.getElementById("github-login-button") as HTMLButtonElement;

// Add click handler to the button
gitHubLoginButton.addEventListener("click", gitHubLoginButtonHandler);

// Show login button if not authenticated
if (!token) {
loginButton.classList.remove("invisible");
} else {
loginButton.classList.add("invisible");
}
}
6 changes: 3 additions & 3 deletions static/scripts/rewards/gift-cards/mint/mint-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async function mintGiftCard(productId: number, app: AppState) {
return;
}

const txHash: string = getIncompleteMintTx(app.reward.nonce) || (await claimPermitToCardTreasury(app));
const txHash: string = getIncompleteMintTx(app.reward.nonce.toString()) || (await claimPermitToCardTreasury(app));

if (txHash) {
let signedMessage;
Expand Down Expand Up @@ -77,7 +77,7 @@ async function mintGiftCard(productId: number, app: AppState) {

async function checkForMintingDelay(app: AppState) {
if (await hasMintingFinished(app)) {
removeIncompleteMintTx(app.reward.nonce);
removeIncompleteMintTx(app.reward.nonce.toString());
await initClaimGiftCard(app);
} else {
const interval = setInterval(async () => {
Expand Down Expand Up @@ -110,7 +110,7 @@ async function claimPermitToCardTreasury(app: AppState) {
const tx = await transferFromPermit(permit2Contract, reward, "Processing... Please wait. Do not close this page.");
if (!tx) return;

storeIncompleteMintTx(app.reward.nonce, tx.hash);
storeIncompleteMintTx(app.reward.nonce.toString(), tx.hash);
await waitForTransaction(tx, `Transaction confirmed. Minting your card now.`, app.signer.provider.network.chainId);
return tx.hash;
}
Expand Down
6 changes: 4 additions & 2 deletions static/scripts/rewards/init.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { app } from "./app-state";
import { initializeAuth } from "./auth";
import { initClaimGiftCard } from "./gift-cards/index";
import { displayCommitHash } from "./render-transaction/display-commit-hash";
import { readClaimDataFromUrl } from "./render-transaction/read-claim-data-from-url";
import { fetchPermits } from "./render-transaction/fetch-permits";
import { grid } from "./the-grid";

initializeAuth();
displayCommitHash();
grid(document.getElementById("grid") as HTMLElement, gridLoadedCallback); // @DEV: display grid background
readClaimDataFromUrl(app).catch(console.error); // @DEV: read claim data from URL
fetchPermits(app).catch(console.error); // @DEV: read claim data from URL

const footer = document.querySelector(".footer") as Element;
footer.classList.add("ready");
Expand Down
16 changes: 16 additions & 0 deletions static/scripts/rewards/local-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function getLocalStore<T>(key: string): T | null {
const cachedIssues = localStorage.getItem(key);
if (cachedIssues) {
try {
return JSON.parse(cachedIssues);
} catch (error) {
console.error(error);
}
}
return null;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function setLocalStore<T>(key: string, value: T) {
localStorage[key] = JSON.stringify(value);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { app } from "../app-state";
import { initClaimGiftCard } from "../gift-cards/index";
import { getMakeClaimButton } from "../button-controller";
import { table, updateButtonVisibility } from "./read-claim-data-from-url";
import { table, updateButtonVisibility } from "./fetch-permits";
import { renderTransaction } from "./render-transaction";
import { removeAllEventListeners } from "./utils";

Expand Down
Loading
Loading