Skip to content

Commit

Permalink
eclipse generalization (#4242)
Browse files Browse the repository at this point in the history
  • Loading branch information
armaniferrante authored Jun 28, 2023
1 parent 72bb031 commit bdfc79a
Show file tree
Hide file tree
Showing 68 changed files with 1,296 additions and 765 deletions.
4 changes: 2 additions & 2 deletions backend/native/backpack-api/src/db/publicKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const getPublicKeyDetails = async ({
publicKey,
}: {
publicKey: string;
}): Promise<{ id: number; blockchain: "solana" | "ethereum" }> => {
}): Promise<{ id: number; blockchain: "solana" | "ethereum" | "eclipse" }> => {
const publicKeyDetails = await chain("query")(
{
auth_public_keys: [
Expand Down Expand Up @@ -42,7 +42,7 @@ export const updatePublicKey = async ({
onlyInsert,
}: {
userId: string;
blockchain: "solana" | "ethereum";
blockchain: "solana" | "ethereum" | "eclipse";
publicKeyId: number;
onlyInsert?: boolean;
}) => {
Expand Down
2 changes: 1 addition & 1 deletion backend/native/backpack-api/src/db/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export const createUser = async (
): Promise<{
id: string;
username: string;
public_keys: { blockchain: "solana" | "ethereum"; id: number }[];
public_keys: { blockchain: "solana" | "ethereum" | "eclipse"; id: number }[];
}> => {
const inviteCode = uuidv4();
await chain("mutation")(
Expand Down
54 changes: 50 additions & 4 deletions backend/native/backpack-api/src/routes/v1/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,39 @@ router.get("/", extractUserId, async (req, res) => {
const limit: number = req.query.limit ? parseInt(req.query.limit) : 20;

const isSolPublicKey = validatePublicKey(usernamePrefix, "solana");
const isEclipsePublicKey = validatePublicKey(usernamePrefix, "eclipse");
const isEthPublicKey = validatePublicKey(usernamePrefix, "ethereum");

let users;
let users: any = [];

//
// SVM.
//
if (isSolPublicKey) {
users = await getUserByPublicKeyAndChain(usernamePrefix, Blockchain.SOLANA);
} else if (isEthPublicKey) {
users = users.concat(
await getUserByPublicKeyAndChain(usernamePrefix, Blockchain.SOLANA)
);
}
if (isEclipsePublicKey) {
users = users.concat(
await getUserByPublicKeyAndChain(usernamePrefix, Blockchain.ECLIPSE)
);
}

//
// EVM.
//
if (isEthPublicKey) {
users = await getUserByPublicKeyAndChain(
usernamePrefix,
Blockchain.ETHEREUM
);
} else {
}

//
// Not a pubkey so assume it's a username.
//
if (users.length === 0) {
users = await getUsersByPrefix({ usernamePrefix, uuid, limit });
}

Expand All @@ -88,6 +110,7 @@ router.get("/", extractUserId, async (req, res) => {
remoteRequested: friendship?.remoteRequested || false,
areFriends: friendship?.areFriends || false,
searchedSolPubKey: isSolPublicKey ? usernamePrefix : undefined,
searchedEclipsePubKey: isEclipsePublicKey ? usernamePrefix : undefined,
searchedEthPubKey: isEthPublicKey ? usernamePrefix : undefined,
// TODO: fix the disambiguation with snake_case and camelCase in API responses
public_keys: public_keys.map((pk) => ({
Expand Down Expand Up @@ -270,6 +293,29 @@ router.get("/primarySolPubkey/:username", async (req, res) => {
}
});

router.get("/primaryEclipsePubkey/:username", async (req, res) => {
const username = req.params.username;
try {
const user = await getUserByUsername(username);
if (!user) {
return res.status(411).json({ msg: "User not found" });
}
const pubKey = user.publicKeys.find(
(x) => x.blockchain === Blockchain.ECLIPSE && x.primary
);
if (!pubKey)
return res
.status(411)
.json({ msg: "No active pubkey on Eclipse for this user" });

return res.json({
publicKey: pubKey.publicKey,
});
} catch (e) {
return res.status(411).json({ msg: "User not found" });
}
});

/**
* Returns the primary public keys of the user with `username`.
*/
Expand Down
8 changes: 8 additions & 0 deletions backend/native/backpack-api/src/validation/publicKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ export const validatePublicKey = (address: string, chain: BlockChain) => {
}
return true;
}
if (chain === "eclipse") {
try {
new PublicKey(address);
} catch (err) {
return false;
}
return true;
}
if (chain === "ethereum") {
try {
ethers.utils.getAddress(address);
Expand Down
9 changes: 9 additions & 0 deletions backend/native/backpack-api/src/validation/signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export const validateSolanaSignature = (
}
};

export const validateEclipseSignature = (
msg: Buffer,
encodedSignature: string,
encodedPublicKey: string
) => {
return validateSolanaSignature(msg, encodedSignature, encodedPublicKey);
};

/**
* Validate a signature
* @param msg - signed message
Expand All @@ -80,6 +88,7 @@ export const validateSignature = (
const validationMethod = {
[Blockchain.ETHEREUM]: validateEthereumSignature,
[Blockchain.SOLANA]: validateSolanaSignature,
[/*Blockchain.ECLIPSE*/ "eclipse"]: validateEclipseSignature, // todo: use enum
}[blockchain];

return validationMethod(msg, signature, publicKey);
Expand Down
19 changes: 19 additions & 0 deletions backend/native/backpack-api/src/validation/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ export const SolanaPublicKey = z.object({
blockchain: z.literal("solana"),
});

export const EclipsePublicKey = z.object({
publicKey: z.string().refine((str) => {
try {
new PublicKey(str);
return true;
} catch {
// Pass
}
return false;
}, "must be a valid Eclipse public key"),
blockchain: z.literal("eclipse"),
});

export const EthereumPublicKey = z.object({
publicKey: z.string().refine((str) => {
try {
Expand All @@ -50,6 +63,7 @@ export const EthereumPublicKey = z.object({

export const BlockchainPublicKey = z.discriminatedUnion("blockchain", [
SolanaPublicKey,
EclipsePublicKey,
EthereumPublicKey,
]);

Expand All @@ -65,9 +79,14 @@ export const CreateSolanaPublicKey = SolanaPublicKey.extend({
signature: z.string(),
});

export const CreateEclipsePublicKey = EclipsePublicKey.extend({
signature: z.string(),
});

export const CreatePublicKeys = z.discriminatedUnion("blockchain", [
CreateEthereumPublicKey,
CreateSolanaPublicKey,
CreateEclipsePublicKey,
]);

export const CreateUserWithPublicKeys = BaseCreateUser.extend({
Expand Down
2 changes: 1 addition & 1 deletion backend/workers/auth/src/onramp/validate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ethers } from "ethers";
import type { BlockChain } from "../zodTypes";

export const validatePublicKey = (address: string, chain: BlockChain) => {
if (chain === "solana") {
if (chain === "solana" || chain === "eclipse") {
try {
new PublicKey(address);
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion backend/workers/auth/src/onramp/zodTypes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

const ZodChain = z.enum(["solana", "ethereum"]);
const ZodChain = z.enum(["solana", "ethereum", "eclipse"]);
export type BlockChain = z.infer<typeof ZodChain>;

export const CreateSessionRequest = z.object({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Blockchain } from "@coral-xyz/common";
import { PrimaryButton } from "@coral-xyz/react-common";
import { useFeatureGates } from "@coral-xyz/recoil";
import { Box, Grid } from "@mui/material";

import { Header, SubtextParagraph } from "../../common";
import {
EthereumIconOnboarding as EthereumIcon,
SolanaIconOnboarding as SolanaIcon,
} from "../../common/Icon";
import { BLOCKCHAIN_COMPONENTS } from "../../common/Blockchains";
import { ActionCard } from "../../common/Layout/ActionCard";

export const BlockchainSelector = ({
Expand All @@ -20,6 +18,7 @@ export const BlockchainSelector = ({
onNext: () => void;
isRecovery?: boolean;
}) => {
const gates = useFeatureGates();
return (
<Box
sx={{
Expand Down Expand Up @@ -57,22 +56,23 @@ export const BlockchainSelector = ({
</Box>
<Box style={{ padding: "0 16px 16px" }}>
<Grid container spacing={1.5}>
<Grid item xs={6}>
<ActionCard
icon={<EthereumIcon />}
checked={selectedBlockchains.includes(Blockchain.ETHEREUM)}
text="Ethereum"
onClick={() => onClick(Blockchain.ETHEREUM)}
/>
</Grid>
<Grid item xs={6}>
<ActionCard
icon={<SolanaIcon />}
checked={selectedBlockchains.includes(Blockchain.SOLANA)}
text="Solana"
onClick={() => onClick(Blockchain.SOLANA)}
/>
</Grid>
{Object.entries(BLOCKCHAIN_COMPONENTS)
.filter(
([blockchain]) =>
gates.ECLIPSE || blockchain !== Blockchain.ECLIPSE
)
.map(([blockchain, Component]) => (
<Grid item xs={6}>
<ActionCard
icon={<Component.Icon />}
checked={selectedBlockchains.includes(
blockchain as Blockchain
)}
text={Component.Name}
onClick={() => onClick(blockchain as Blockchain)}
/>
</Grid>
))}
</Grid>
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,18 @@
// script can't communicate with a hardware device.

import { useEffect, useState } from "react";
import type { WalletDescriptor } from "@coral-xyz/common";
import type { Blockchain,WalletDescriptor } from "@coral-xyz/common";
import {
Blockchain,
getAccountRecoveryPaths,
UI_RPC_METHOD_FIND_SERVER_PUBLIC_KEY_CONFLICTS,
} from "@coral-xyz/common";
import { Loading } from "@coral-xyz/react-common";
import { useBackgroundClient } from "@coral-xyz/recoil";
import Ethereum from "@ledgerhq/hw-app-eth";
import Solana from "@ledgerhq/hw-app-solana";
import type Ethereum from "@ledgerhq/hw-app-eth";
import type Solana from "@ledgerhq/hw-app-solana";
import type Transport from "@ledgerhq/hw-transport";
import { ethers } from "ethers";

const { base58 } = ethers.utils;
import { BLOCKCHAIN_COMPONENTS } from "../../common/Blockchains";

export const HardwareDefaultWallet = ({
blockchain,
Expand All @@ -38,10 +36,8 @@ export const HardwareDefaultWallet = ({

useEffect(() => {
(async () => {
const ledgerWallet = {
[Blockchain.SOLANA]: new Solana(transport),
[Blockchain.ETHEREUM]: new Ethereum(transport),
}[blockchain];
const ledgerWallet =
BLOCKCHAIN_COMPONENTS[blockchain].LedgerApp(transport);
setLedgerWallet(ledgerWallet);
})();
}, [blockchain, transport]);
Expand All @@ -57,13 +53,9 @@ export const HardwareDefaultWallet = ({
try {
// Get the public keys for all of the recovery paths for the current account index
for (const path of recoveryPaths) {
const ledgerAddress = (
await ledgerWallet.getAddress(path.replace("m/", ""))
).address;
const publicKey =
blockchain === Blockchain.SOLANA
? base58.encode(ledgerAddress as Buffer)
: ledgerAddress.toString();
const publicKey = await BLOCKCHAIN_COMPONENTS[
blockchain
].PublicKeyFromPath(ledgerWallet, path);
publicKeys.push(publicKey);
}
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { useEffect, useState } from "react";
import type { WalletDescriptor } from "@coral-xyz/common";
import type { Blockchain,WalletDescriptor } from "@coral-xyz/common";
import {
Blockchain,
UI_RPC_METHOD_KEYRING_READ_NEXT_DERIVATION_PATH,
} from "@coral-xyz/common";
import { Loading } from "@coral-xyz/react-common";
import { useBackgroundClient } from "@coral-xyz/recoil";
import Ethereum from "@ledgerhq/hw-app-eth";
import Solana from "@ledgerhq/hw-app-solana";
import type Ethereum from "@ledgerhq/hw-app-eth";
import type Solana from "@ledgerhq/hw-app-solana";
import type Transport from "@ledgerhq/hw-transport";
import { ethers } from "ethers";

import { BLOCKCHAIN_COMPONENTS } from "../../common/Blockchains";

const { base58 } = ethers.utils;

export const HardwareDeriveWallet = ({
Expand All @@ -31,10 +32,8 @@ export const HardwareDeriveWallet = ({

useEffect(() => {
(async () => {
const ledgerWallet = {
[Blockchain.SOLANA]: new Solana(transport),
[Blockchain.ETHEREUM]: new Ethereum(transport),
}[blockchain];
const ledgerWallet =
BLOCKCHAIN_COMPONENTS[blockchain].LedgerApp(transport);
setLedgerWallet(ledgerWallet);
})();
}, [blockchain, transport]);
Expand All @@ -50,13 +49,10 @@ export const HardwareDeriveWallet = ({

let publicKey: string;
try {
const ledgerAddress = (
await ledgerWallet.getAddress(derivationPath.replace("m/", ""))
).address;
publicKey =
blockchain === Blockchain.SOLANA
? base58.encode(ledgerAddress)
: ledgerAddress.toString();
publicKey = await BLOCKCHAIN_COMPONENTS[blockchain].PublicKeyFromPath(
ledgerWallet,
derivationPath
);
} catch (error) {
if (onError) {
console.debug("hardware derive wallet transport error", error);
Expand Down
Loading

1 comment on commit bdfc79a

@vercel
Copy link

@vercel vercel bot commented on bdfc79a Jun 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.