From 59a0b88e73568f8c6a574d3aa56c12c45a1f2c59 Mon Sep 17 00:00:00 2001 From: joinu14 Date: Thu, 14 Mar 2024 19:01:36 +0300 Subject: [PATCH] fix color validation, forbid external salt for other dapps, improve add asset fn --- apps/site/src/frontend/store/assets.tsx | 14 ++++--- packages/client/src/internal.ts | 4 +- packages/shared/src/avatar.ts | 4 +- packages/shared/src/index.ts | 54 ++++++++++++++++++------- packages/shared/src/types.ts | 22 +++++----- packages/snap/src/protocols/icrc1.ts | 17 +++++--- packages/snap/src/protocols/identity.ts | 10 ++++- packages/snap/src/state.ts | 14 ++++--- packages/snap/src/utils.ts | 6 +++ 9 files changed, 99 insertions(+), 46 deletions(-) diff --git a/apps/site/src/frontend/store/assets.tsx b/apps/site/src/frontend/store/assets.tsx index 746b319..ac8b608 100644 --- a/apps/site/src/frontend/store/assets.tsx +++ b/apps/site/src/frontend/store/assets.tsx @@ -97,7 +97,9 @@ export function useTxnHistoryPageProps() { return ctx.txnHistoryPageProps; } -const PRE_DEFINED_ASSETS = Object.values(PRE_LISTED_TOKENS).map((it) => it.assetId); +const PRE_DEFINED_ASSETS: { assetId: string; name: string; symbol: string }[] = Object.values(PRE_LISTED_TOKENS).map( + (it) => ({ assetId: it.assetId, name: it.name, symbol: it.symbol }), +); export function AssetsStore(props: IChildren) { const [allAssetData, setAllAssetData] = createStore(); @@ -135,9 +137,9 @@ export function AssetsStore(props: IChildren) { // CREATE PRE-DEFINED ASSETS const assetsToCreate = []; - for (let assetId of PRE_DEFINED_ASSETS) { - if (fetchedAllAssetData[assetId]) continue; - assetsToCreate.push({ assetId }); + for (let asset of PRE_DEFINED_ASSETS) { + if (fetchedAllAssetData[asset.assetId]) continue; + assetsToCreate.push(asset); } if (assetsToCreate.length > 0) { await msq.addAsset({ assets: assetsToCreate }); @@ -201,9 +203,9 @@ export function AssetsStore(props: IChildren) { } }; - const addAccount = async (assetId: string, assetName: string, symbol: string) => { + const addAccount = async (assetId: string) => { const msq = _msq()!; - const name = await msq.addAssetAccount(assetId, assetName, symbol); + const name = await msq.addAssetAccount(assetId); if (name === null) return; diff --git a/packages/client/src/internal.ts b/packages/client/src/internal.ts index 63e6021..6c090db 100644 --- a/packages/client/src/internal.ts +++ b/packages/client/src/internal.ts @@ -96,10 +96,10 @@ export class InternalSnapClient { return await this.inner._requestSnap(SNAP_METHODS.protected.icrc1.addAsset, req); } - async addAssetAccount(assetId: string, assetName: string, assetSymbol: string): Promise { + async addAssetAccount(assetId: string): Promise { this.checkInnerSet(); - const body: IICRC1AddAssetAccountRequest = { assetId, name: assetName, symbol: assetSymbol }; + const body: IICRC1AddAssetAccountRequest = { assetId }; return await this.inner._requestSnap(SNAP_METHODS.protected.icrc1.addAssetAccount, body); } diff --git a/packages/shared/src/avatar.ts b/packages/shared/src/avatar.ts index e0b39dd..6224ab0 100644 --- a/packages/shared/src/avatar.ts +++ b/packages/shared/src/avatar.ts @@ -1,5 +1,5 @@ import { Principal } from "@dfinity/principal"; -import { escapeHtml } from "./types"; +import { ZHexColorSanitized } from "./types"; /** * Creates an SVG string for an avatar based on a given Principal object, with an optional background color. @@ -13,7 +13,7 @@ import { escapeHtml } from "./types"; * @returns {string} A string representation of the SVG for the avatar, customized based on the Principal object. */ export function makeAvatarSvg(principal: Principal, bgColor: string = "#1E1F28"): string { - bgColor = escapeHtml(bgColor); + bgColor = ZHexColorSanitized.parse(bgColor); const principalBytes = principalToBytes(principal); const bodyColor = getBodyColor(principalBytes); diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index f86abc9..f18dde6 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -149,119 +149,145 @@ export function originToHostname(origin: TOrigin): string { export const PRE_LISTED_TOKENS: Record< string, - { name: string; assetId: string; logoSrc?: string; chargingAccountId?: string } + { name: string; symbol: string; assetId: string; logoSrc?: string; chargingAccountId?: string } > = { "ryjl3-tyaaa-aaaaa-aaaba-cai": { - name: "ICP", + name: "Internet Computer", + symbol: "ICP", assetId: "ryjl3-tyaaa-aaaaa-aaaba-cai", logoSrc: "https://nns.ic0.app/_app/immutable/assets/icp-rounded.0be14f6b.svg", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "mxzaz-hqaaa-aaaar-qaada-cai": { - name: "ckBTC", + name: "Bitcoin", + symbol: "ckBTC", assetId: "mxzaz-hqaaa-aaaar-qaada-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "ss2fx-dyaaa-aaaar-qacoq-cai": { - name: "ckETH", + name: "Ethereum", + symbol: "ckETH", assetId: "ss2fx-dyaaa-aaaar-qacoq-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "2ouva-viaaa-aaaaq-aaamq-cai": { - name: "CHAT", + name: "OpenChat", + symbol: "CHAT", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/3e3x2-xyaaa-aaaaq-aaalq-cai/logo.png", assetId: "2ouva-viaaa-aaaaq-aaamq-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "qbizb-wiaaa-aaaaq-aabwq-cai": { - name: "SONIC", + name: "Sonic", + symbol: "SONIC", assetId: "qbizb-wiaaa-aaaaq-aabwq-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "zfcdd-tqaaa-aaaaq-aaaga-cai": { - name: "SNS1", + name: "Dragginz", + symbol: "SNS1", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/zxeu2-7aaaa-aaaaq-aaafa-cai/logo.png", assetId: "zfcdd-tqaaa-aaaaq-aaaga-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "jwcfb-hyaaa-aaaaj-aac4q-cai": { - name: "OGY", + name: "Origyn", + symbol: "OGY", assetId: "jwcfb-hyaaa-aaaaj-aac4q-cai", logoSrc: "https://msq.tech/ogy.svg", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "xsi2v-cyaaa-aaaaq-aabfq-cai": { - name: "MOD", + name: "Modclub", + symbol: "MOD", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/x4kx5-ziaaa-aaaaq-aabeq-cai/logo.png", assetId: "xsi2v-cyaaa-aaaaq-aabfq-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "4c4fd-caaaa-aaaaq-aaa3a-cai": { - name: "GHOST", + name: "ICGhost", + symbol: "GHOST", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/4m6il-zqaaa-aaaaq-aaa2a-cai/logo.png", assetId: "4c4fd-caaaa-aaaaq-aaa3a-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "73mez-iiaaa-aaaaq-aaasq-cai": { - name: "KINIC", + name: "Kinic", + symbol: "KINIC", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/7jkta-eyaaa-aaaaq-aaarq-cai/logo.png", assetId: "73mez-iiaaa-aaaaq-aaasq-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "6rdgd-kyaaa-aaaaq-aaavq-cai": { - name: "HOT", + name: "Hot or Not", + symbol: "HOT", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/67bll-riaaa-aaaaq-aaauq-cai/logo.png", assetId: "6rdgd-kyaaa-aaaaq-aaavq-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "uf2wh-taaaa-aaaaq-aabna-cai": { - name: "CAT", + name: "Catalyze", + symbol: "CAT", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/uly3p-iqaaa-aaaaq-aabma-cai/logo.png", assetId: "uf2wh-taaaa-aaaaq-aabna-cai", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "vtrom-gqaaa-aaaaq-aabia-cai": { name: "BOOM DAO", + symbol: "BOOM", assetId: "vtrom-gqaaa-aaaaq-aabia-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/xjngq-yaaaa-aaaaq-aabha-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "tyyy3-4aaaa-aaaaq-aab7a-cai": { - name: "GOLD DAO", + name: "Gold DAO", + symbol: "GLDGov", assetId: "tyyy3-4aaaa-aaaaq-aab7a-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/tw2vt-hqaaa-aaaaq-aab6a-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "rffwt-piaaa-aaaaq-aabqq-cai": { name: "ICX", + symbol: "ICX", assetId: "rffwt-piaaa-aaaaq-aabqq-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/u67kc-jyaaa-aaaaq-aabpq-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "f54if-eqaaa-aaaaq-aacea-cai": { name: "Neutrinite", + symbol: "NTN", assetId: "f54if-eqaaa-aaaaq-aacea-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/extk7-gaaaa-aaaaq-aacda-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "rxdbk-dyaaa-aaaaq-aabtq-cai": { name: "Nuance", + symbol: "NUA", assetId: "rxdbk-dyaaa-aaaaq-aabtq-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/rzbmc-yiaaa-aaaaq-aabsq-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "hvgxa-wqaaa-aaaaq-aacia-cai": { name: "Sneed", + symbol: "SNEED", assetId: "hvgxa-wqaaa-aaaaq-aacia-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/fp274-iaaaa-aaaaq-aacha-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, "emww2-4yaaa-aaaaq-aacbq-cai": { name: "TRAX", + symbol: "TRAX", assetId: "emww2-4yaaa-aaaaq-aacbq-cai", logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/ecu3s-hiaaa-aaaaq-aacaq-cai/logo.png", chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", }, + "hhaaz-2aaaa-aaaaq-aacla-cai": { + name: "ICLighthouse DAO", + symbol: "ICL", + assetId: "hhaaz-2aaaa-aaaaq-aacla-cai", + logoSrc: "https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io/v1/sns/root/hjcnr-bqaaa-aaaaq-aacka-cai/logo.png", + chargingAccountId: "rmapb-pzxbf-4fimd-h33qy-aydfx-wxne6-64kqi-f6nwz-cfzyq-wf7tb-bqe", + }, }; /** diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 784e4e2..618f7af 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -32,6 +32,11 @@ export const ZNonEmptyStrSanitized = z.string().min(1).transform(escapeHtml); export const ZPrincipalStrSanitized = z.string().regex(/^[0-9a-zA-Z]{1,5}(\-[0-9a-zA-Z]{1,5})*$/); export const ZICRC1Subaccount = z.instanceof(Uint8Array); +/** + * A 6-digit hex color code, e.g. #12beef + */ +export const ZHexColorSanitized = z.string().regex(/^#[a-fA-F0-9]{6}$/); + /** * Website origin, for example `https://google.com` */ @@ -117,8 +122,14 @@ export const ZStatistics = z.object({ data: ZStatisticsData, }); +export const ZAmountStrSanitized = z.string().regex(/^[0-9',\.]+$/); +const ZAssetNameSanitized = z.string().min(1).transform(escapeHtml); +export const ZTickerStrSanitized = z.string().regex(/^[A-Za-z0-9]+$/); + export type IAssetData = z.infer; export const ZAssetData = z.object({ + name: ZAssetNameSanitized, + symbol: ZTickerStrSanitized, accounts: z.record(ZNonEmptyStrSanitized, ZNonEmptyStrSanitized), }); @@ -261,9 +272,6 @@ export const ZICRC1TransferRequest = z.object({ }); export type IICRC1TransferRequest = z.infer; -export const ZAmountStrSanitized = z.string().regex(/^[0-9',\.]+$/); -export const ZTickerStrSanitized = z.string().regex(/^[A-Za-z0-9]+$/); - export const ZShowICRC1TransferConfirmRequest = z.object({ requestOrigin: ZOrigin, from: ZPrincipalStrSanitized, @@ -274,14 +282,12 @@ export const ZShowICRC1TransferConfirmRequest = z.object({ }); export type IShowICRC1TransferConfirmRequest = z.infer; -const ZAssetNameSanitized = z.string().min(1).transform(escapeHtml); - export const ZICRC1AddAssetRequest = z.object({ assets: z.array( z.object({ assetId: ZPrincipalStrSanitized, - name: z.optional(ZAssetNameSanitized), - symbol: z.optional(ZTickerStrSanitized), + name: ZAssetNameSanitized, + symbol: ZTickerStrSanitized, }), ), }); @@ -289,8 +295,6 @@ export type IICRC1AddAssetRequest = z.infer; export const ZICRC1AddAssetAccountRequest = z.object({ assetId: ZPrincipalStrSanitized, - name: ZAssetNameSanitized, - symbol: ZTickerStrSanitized, }); export type IICRC1AddAssetAccountRequest = z.infer; diff --git a/packages/snap/src/protocols/icrc1.ts b/packages/snap/src/protocols/icrc1.ts index 8da9ca8..867903a 100644 --- a/packages/snap/src/protocols/icrc1.ts +++ b/packages/snap/src/protocols/icrc1.ts @@ -8,6 +8,7 @@ import { bytesToHex, fromCBOR, originToHostname, + unreacheable, zodParse, } from "@fort-major/msq-shared"; import { divider, panel } from "@metamask/snaps-sdk"; @@ -71,7 +72,7 @@ export async function protected_handleAddAsset(bodyCBOR: string): Promise text(` - **${it}**`)), + ...assetNames.map((it) => text(`— **${it}**`)), divider(), text("**Confirm?** 🚀"), ]), @@ -82,7 +83,7 @@ export async function protected_handleAddAsset(bodyCBOR: string): Promise ({ - accounts: Object.values(manager.addAsset(it.assetId).accounts), + accounts: Object.values(manager.addAsset(it.assetId, it.name, it.symbol).accounts), })); return assetDataExternal; @@ -92,14 +93,20 @@ export async function protected_handleAddAssetAccount(bodyCBOR: string): Promise const body = zodParse(ZICRC1AddAssetAccountRequest, fromCBOR(bodyCBOR)); const manager = await StateManager.make(); + const assetData = manager.getAllAssetData()[body.assetId]; + + if (!assetData) { + unreacheable("attempt to add an account for an unknown asset"); + } + const agreed = await snap.request({ method: "snap_dialog", params: { type: "confirmation", content: panel([ - heading(`🔒 Confirm New ${body.symbol} Account 🔒`), - text(`Are you sure you want to create a new **${body.name}** (**${body.symbol}**) token account?`), - text(`This will allow you to send and receive **${body.symbol}** tokens.`), + heading(`🔒 Confirm New ${assetData.symbol} Account 🔒`), + text(`Are you sure you want to create a new **${assetData.name}** (**${assetData.symbol}**) token account?`), + text(`This will allow you to send and receive **${assetData.symbol}** tokens.`), divider(), text("**Confirm?** 🚀"), ]), diff --git a/packages/snap/src/protocols/identity.ts b/packages/snap/src/protocols/identity.ts index 39db05c..4ac0c82 100644 --- a/packages/snap/src/protocols/identity.ts +++ b/packages/snap/src/protocols/identity.ts @@ -83,8 +83,14 @@ export async function protected_handleIdentityLogin(bodyCBOR: string): Promise