diff --git a/.changeset/long-falcons-peel.md b/.changeset/long-falcons-peel.md deleted file mode 100644 index 84102711b..000000000 --- a/.changeset/long-falcons-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@turnkey/viem": patch ---- - -Unpin typescript diff --git a/.changeset/rude-turtles-refuse.md b/.changeset/rude-turtles-refuse.md deleted file mode 100644 index e502a5a9f..000000000 --- a/.changeset/rude-turtles-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@turnkey/viem": patch ---- - -Bump Viem dependency to fix `getAddresses()` for LocalAccount diff --git a/examples/with-ethers-and-passkeys/.env.local.example b/examples/with-ethers-and-passkeys/.env.local.example deleted file mode 100644 index 1479747c0..000000000 --- a/examples/with-ethers-and-passkeys/.env.local.example +++ /dev/null @@ -1,4 +0,0 @@ -API_PUBLIC_KEY="" -API_PRIVATE_KEY="" -NEXT_PUBLIC_ORGANIZATION_ID="" -NEXT_PUBLIC_TURNKEY_API_BASE_URL=https://api.turnkey.com diff --git a/examples/with-ethers-and-passkeys/.eslintrc.json b/examples/with-ethers-and-passkeys/.eslintrc.json deleted file mode 100644 index bffb357a7..000000000 --- a/examples/with-ethers-and-passkeys/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/examples/with-ethers-and-passkeys/.gitignore b/examples/with-ethers-and-passkeys/.gitignore deleted file mode 100644 index 8f322f0d8..000000000 --- a/examples/with-ethers-and-passkeys/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/examples/with-ethers-and-passkeys/README.md b/examples/with-ethers-and-passkeys/README.md deleted file mode 100644 index c72be197d..000000000 --- a/examples/with-ethers-and-passkeys/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Example: `with-ethers-and-passkeys` - -This example shows how to create sub-organizations, create private keys, and sign with the [`@turnkey/ethers`](../../packages/ethers/) signer, using passkeys. - -![UI screenshot](./img/ui-screenshot.png) - -The flow showcases 3 ways to make requests to Turnkey: - -- the initial request to create a new [sub-organization](https://docs.turnkey.com/getting-started/sub-organizations) is authenticated in the NextJS backend with an API signature (using `API_PUBLIC_KEY`/`API_PRIVATE_KEY` from your `.env.local` file) -- the request to create a new ETH address is signed on the frontend with your passkey, but it's passed to the NextJS backend as a signed request (the body, stamp, and url are POSTed). This lets the backend submit this request on your behalf, and poll until the new "create private keys" activity completes. Once the activity completes it returns the new address to the frontend -- the request to sign a message is done 100% client-side via a Turnkey Ethers signer (see [@turnkey/ethers](../../packages/ethers/)): it's signed with your passkey, and submitted from the browser to the Turnkey API directly. - -If you want to see a ethers demo with API keys instead of passkeys, head to the example [`with-ethers`](../with-ethers/). - -## Getting started - -### 1/ Cloning the example - -Make sure you have `Node.js` installed locally; we recommend using Node v16+. - -```bash -$ git clone https://github.com/tkhq/sdk -$ cd sdk/ -$ corepack enable # Install `pnpm` -$ pnpm install -r # Install dependencies -$ pnpm run build-all # Compile source code -$ cd examples/with-ethers-and-passkeys/ -``` - -### 2/ Setting up Turnkey - -The first step is to set up your Turnkey organization and account. By following the [Quickstart](https://docs.turnkey.com/getting-started/quickstart) guide, you should have: - -- A public/private API key pair for Turnkey -- An organization ID - -Once you've gathered these values, add them to a new `.env.local` file. Notice that your API private key should be securely managed and **_never_** be committed to git. - -```bash -$ cp .env.local.example .env.local -``` - -Now open `.env.local` and add the missing environment variables: - -- `API_PUBLIC_KEY` -- `API_PRIVATE_KEY` -- `NEXT_PUBLIC_TURNKEY_API_BASE_URL` -- `NEXT_PUBLIC_ORGANIZATION_ID` - -### 3/ Running the app - -```bash -$ pnpm run dev -``` - -This command will start a NextJS app on localhost. If you navigate to http://localhost:3000 in your browser, you can follow the prompts to create a sub organization, create a private key for the newly created sub-organization, and sign a message using your passkey with a ethers custom account! diff --git a/examples/with-ethers-and-passkeys/img/ui-screenshot.png b/examples/with-ethers-and-passkeys/img/ui-screenshot.png deleted file mode 100644 index eca09be85..000000000 Binary files a/examples/with-ethers-and-passkeys/img/ui-screenshot.png and /dev/null differ diff --git a/examples/with-ethers-and-passkeys/next.config.js b/examples/with-ethers-and-passkeys/next.config.js deleted file mode 100644 index 658404ac6..000000000 --- a/examples/with-ethers-and-passkeys/next.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -module.exports = nextConfig; diff --git a/examples/with-ethers-and-passkeys/package.json b/examples/with-ethers-and-passkeys/package.json deleted file mode 100644 index ea2d40945..000000000 --- a/examples/with-ethers-and-passkeys/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@turnkey/example-with-ethers-and-passkeys", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "typecheck": "tsc --noEmit" - }, - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@turnkey/http": "workspace:*", - "@turnkey/api-key-stamper": "workspace:*", - "@turnkey/webauthn-stamper": "workspace:*", - "@turnkey/ethers": "workspace:*", - "@types/node": "20.3.1", - "@types/react": "18.2.14", - "@types/react-dom": "18.2.6", - "axios": "^1.4.0", - "encoding": "^0.1.13", - "eslint": "8.43.0", - "eslint-config-next": "13.4.7", - "esm": "^3.2.25", - "install": "^0.13.0", - "next": "13.4.7", - "npm": "^9.7.2", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-hook-form": "^7.45.1", - "typescript": "5.1.3", - "ethers": "^5.7.2" - } -} diff --git a/examples/with-ethers-and-passkeys/public/favicon.svg b/examples/with-ethers-and-passkeys/public/favicon.svg deleted file mode 100644 index 73948fa1a..000000000 --- a/examples/with-ethers-and-passkeys/public/favicon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/examples/with-ethers-and-passkeys/public/fonts/inter/Inter-Regular.woff2 b/examples/with-ethers-and-passkeys/public/fonts/inter/Inter-Regular.woff2 deleted file mode 100644 index 6c2b6893d..000000000 Binary files a/examples/with-ethers-and-passkeys/public/fonts/inter/Inter-Regular.woff2 and /dev/null differ diff --git a/examples/with-ethers-and-passkeys/public/logo.svg b/examples/with-ethers-and-passkeys/public/logo.svg deleted file mode 100644 index f983fd29b..000000000 --- a/examples/with-ethers-and-passkeys/public/logo.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - diff --git a/examples/with-ethers-and-passkeys/src/app/globals.css b/examples/with-ethers-and-passkeys/src/app/globals.css deleted file mode 100644 index d94d4168d..000000000 --- a/examples/with-ethers-and-passkeys/src/app/globals.css +++ /dev/null @@ -1,5 +0,0 @@ -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} diff --git a/examples/with-ethers-and-passkeys/src/pages/_document.tsx b/examples/with-ethers-and-passkeys/src/pages/_document.tsx deleted file mode 100644 index a773ac630..000000000 --- a/examples/with-ethers-and-passkeys/src/pages/_document.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Document, { Html, Head, Main, NextScript } from "next/document"; - -class EthersPasskeysDemo extends Document { - render() { - return ( - - - - - -
- - - - ); - } -} - -export default EthersPasskeysDemo; diff --git a/examples/with-ethers-and-passkeys/src/pages/api/createKey.ts b/examples/with-ethers-and-passkeys/src/pages/api/createKey.ts deleted file mode 100644 index 2ca17fbdf..000000000 --- a/examples/with-ethers-and-passkeys/src/pages/api/createKey.ts +++ /dev/null @@ -1,95 +0,0 @@ -import axios from "axios"; -import type { NextApiRequest, NextApiResponse } from "next"; -import { - TSignedRequest, - TurnkeyClient, - createActivityPoller, -} from "@turnkey/http"; -import { ApiKeyStamper } from "@turnkey/api-key-stamper"; -import { refineNonNull } from "./utils"; - -type TResponse = { - message: string; - address?: string; - privateKeyId?: string; -}; - -function sleep(ms: number): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, ms); - }); -} - -export default async function createKey( - req: NextApiRequest, - res: NextApiResponse -) { - let signedRequest = req.body as TSignedRequest; - - try { - const activityResponse = await axios.post( - signedRequest.url, - signedRequest.body, - { - headers: { - [signedRequest.stamp.stampHeaderName]: - signedRequest.stamp.stampHeaderValue, - }, - } - ); - - if (activityResponse.status !== 200) { - res.status(500).json({ - message: `expected 200, got ${activityResponse.status}`, - }); - } - - const stamper = new ApiKeyStamper({ - apiPublicKey: process.env.API_PUBLIC_KEY!, - apiPrivateKey: process.env.API_PRIVATE_KEY!, - }); - const client = new TurnkeyClient( - { baseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL! }, - stamper - ); - - const activityPoller = createActivityPoller({ - client: client, - requestFn: client.getActivity, - }); - - const activityId = refineNonNull(activityResponse.data.activity?.id); - const subOrgId = refineNonNull( - activityResponse.data.activity?.organizationId - ); - - const completedActivity = await activityPoller({ - activityId, - organizationId: subOrgId, - }); - - const privateKeys = - completedActivity.result.createPrivateKeysResultV2?.privateKeys; - - // XXX: sorry for the ugly code! We expect a single key / address returned. - // If we have more than one key / address returned, or none, this would break. - const address = privateKeys - ?.map((pk) => pk.addresses?.map((addr) => addr.address).join("")) - .join(""); - const privateKeyId = privateKeys?.map((pk) => pk.privateKeyId).join(""); - - res.status(200).json({ - message: "successfully created key", - address: address, - privateKeyId: privateKeyId, - }); - } catch (e) { - console.error(e); - - res.status(500).json({ - message: `Something went wrong, caught error: ${e}`, - }); - } -} diff --git a/examples/with-ethers-and-passkeys/src/pages/api/createSubOrg.ts b/examples/with-ethers-and-passkeys/src/pages/api/createSubOrg.ts deleted file mode 100644 index 87e8207ea..000000000 --- a/examples/with-ethers-and-passkeys/src/pages/api/createSubOrg.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { TurnkeyApiTypes, TurnkeyClient } from "@turnkey/http"; -import { createActivityPoller } from "@turnkey/http"; -import { ApiKeyStamper } from "@turnkey/api-key-stamper"; -import { refineNonNull } from "./utils"; - -type TAttestation = TurnkeyApiTypes["v1Attestation"]; - -type CreateSubOrgWithPrivateKeyRequest = { - subOrgName: string; - challenge: string; - privateKeyName: string; - attestation: TAttestation; -}; - -type CreateSubOrgResponse = { - subOrgId: string; - privateKeyId: string; - privateKeyAddress: string; -}; - -type ErrorMessage = { - message: string; -}; - -export default async function createUser( - req: NextApiRequest, - res: NextApiResponse -) { - const createSubOrgRequest = req.body as CreateSubOrgWithPrivateKeyRequest; - - try { - const turnkeyClient = new TurnkeyClient( - { baseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL! }, - new ApiKeyStamper({ - apiPublicKey: process.env.API_PUBLIC_KEY!, - apiPrivateKey: process.env.API_PRIVATE_KEY!, - }) - ); - - const activityPoller = createActivityPoller({ - client: turnkeyClient, - requestFn: turnkeyClient.createSubOrganization, - }); - - const privateKeyName = `Default ETH Key`; - - const completedActivity = await activityPoller({ - type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V3", - timestampMs: String(Date.now()), - organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!, - parameters: { - subOrganizationName: createSubOrgRequest.subOrgName, - rootQuorumThreshold: 1, - rootUsers: [ - { - userName: "New user", - apiKeys: [], - authenticators: [ - { - authenticatorName: "Passkey", - challenge: createSubOrgRequest.challenge, - attestation: createSubOrgRequest.attestation, - }, - ], - }, - ], - privateKeys: [ - { - privateKeyName: privateKeyName, - curve: "CURVE_SECP256K1", - addressFormats: ["ADDRESS_FORMAT_ETHEREUM"], - privateKeyTags: [], - }, - ], - }, - }); - - const subOrgId = refineNonNull( - completedActivity.result.createSubOrganizationResultV3?.subOrganizationId - ); - const privateKeys = refineNonNull( - completedActivity.result.createSubOrganizationResultV3?.privateKeys - ); - const privateKeyId = refineNonNull(privateKeys?.[0]?.privateKeyId); - const privateKeyAddress = refineNonNull( - privateKeys?.[0]?.addresses?.[0]?.address - ); - - res.status(200).json({ - subOrgId, - privateKeyId, - privateKeyAddress, - }); - } catch (e) { - console.error(e); - - res.status(500).json({ - message: "Something went wrong.", - }); - } -} diff --git a/examples/with-ethers-and-passkeys/src/pages/api/utils.ts b/examples/with-ethers-and-passkeys/src/pages/api/utils.ts deleted file mode 100644 index d26b4fd42..000000000 --- a/examples/with-ethers-and-passkeys/src/pages/api/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function refineNonNull( - input: T | null | undefined, - errorMessage?: string -): T { - if (input == null) { - throw new Error(errorMessage ?? `Unexpected ${JSON.stringify(input)}`); - } - - return input; -} diff --git a/examples/with-ethers-and-passkeys/src/pages/index.module.css b/examples/with-ethers-and-passkeys/src/pages/index.module.css deleted file mode 100644 index 3c4cced5d..000000000 --- a/examples/with-ethers-and-passkeys/src/pages/index.module.css +++ /dev/null @@ -1,90 +0,0 @@ -@font-face { - font-family: "Inter"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("../../public/fonts/inter/Inter-Regular.woff2?v=3.19") - format("woff2"); -} - -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - text-align: center; - padding: 2rem; - gap: 30px; - font-family: "Inter"; - max-width: 50ch; - margin: auto; -} - -.input { - width: 100%; - margin: 0; - padding: 10px 16px; - border-radius: 8px; - border-width: 1px; - border-style: solid; - border-color: rgba(216, 219, 227, 1); - font-family: "Inter"; -} - -.label { - font-family: "Inter"; -} - -.prompt { - font-family: "Inter"; -} - -.button { - display: inline-flex; - align-items: center; - justify-content: center; - margin: 0; - padding: 10px 16px; - border-radius: 8px; - border-width: 1px; - border-style: solid; - cursor: pointer; - color: white; - background-color: rgba(43, 47, 51, 1); - border-color: rgba(63, 70, 75, 1); - font-family: "Inter"; -} - -.form { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - gap: 10px; -} - -.form input { - max-width: 44ch; -} - -.info { - display: block; - font-size: 0.9em; - padding: 10px 16px; - margin: 10px; - border-radius: 8px; - background-color: rgb(240, 249, 250); - word-break: break-all; - box-shadow: inset 0 0 4px #e2e8e8; -} - -.code { - font-size: 1em; - font-weight: bold; -} - -.explainer { - line-height: 1.4em; - font-size: 0.9em; - color: rgb(134, 153, 156); -} diff --git a/examples/with-ethers-and-passkeys/src/pages/index.tsx b/examples/with-ethers-and-passkeys/src/pages/index.tsx deleted file mode 100644 index 36b8d232b..000000000 --- a/examples/with-ethers-and-passkeys/src/pages/index.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import Image from "next/image"; -import styles from "./index.module.css"; -import axios from "axios"; -import { ethers } from "ethers"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { getWebAuthnAttestation, TurnkeyClient } from "@turnkey/http"; -import { WebauthnStamper } from "@turnkey/webauthn-stamper"; -import { TurnkeySigner } from "@turnkey/ethers"; - -type subOrgFormData = { - subOrgName: string; -}; - -type privateKeyFormData = { - privateKeyName: string; -}; - -type signingFormData = { - messageToSign: string; -}; - -const generateRandomBuffer = (): ArrayBuffer => { - const arr = new Uint8Array(32); - crypto.getRandomValues(arr); - return arr.buffer; -}; - -const base64UrlEncode = (challenge: ArrayBuffer): string => { - return Buffer.from(challenge) - .toString("base64") - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); -}; - -type TPrivateKeyState = { - id: string; - address: string; -} | null; - -type TSignedMessage = { - message: string; - signature: string; -} | null; - -const humanReadableDateTime = (): string => { - return new Date().toLocaleString().replaceAll("/", "-").replaceAll(":", "."); -}; - -export default function Home() { - const [subOrgId, setSubOrgId] = useState(null); - const [privateKey, setPrivateKey] = useState(null); - const [signedMessage, setSignedMessage] = useState(null); - - const { handleSubmit: subOrgFormSubmit } = useForm(); - const { register: signingFormRegister, handleSubmit: signingFormSubmit } = - useForm(); - const { handleSubmit: privateKeyFormSubmit } = useForm(); - const { register: _loginFormRegister, handleSubmit: loginFormSubmit } = - useForm(); - - const stamper = new WebauthnStamper({ - rpId: "localhost", - }); - - const passkeyHttpClient = new TurnkeyClient( - { - baseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL!, - }, - stamper - ); - - const createPrivateKey = async () => { - if (!subOrgId) { - throw new Error("sub-org id not found"); - } - - const signedRequest = await passkeyHttpClient.stampCreatePrivateKeys({ - type: "ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2", - organizationId: subOrgId, - timestampMs: String(Date.now()), - parameters: { - privateKeys: [ - { - privateKeyName: `ETH Key ${Math.floor(Math.random() * 1000)}`, - curve: "CURVE_SECP256K1", - addressFormats: ["ADDRESS_FORMAT_ETHEREUM"], - privateKeyTags: [], - }, - ], - }, - }); - - const response = await axios.post("/api/createKey", signedRequest); - - setPrivateKey({ - id: response.data["privateKeyId"], - address: response.data["address"], - }); - }; - - const signMessage = async (data: signingFormData) => { - if (!subOrgId || !privateKey) { - throw new Error("sub-org id or private key not found"); - } - - const ethersSigner = new TurnkeySigner({ - client: passkeyHttpClient, - organizationId: subOrgId, - privateKeyId: privateKey.id, - }); - - const signedMessage = await ethersSigner.signMessage(data.messageToSign); - - setSignedMessage({ - message: data.messageToSign, - signature: signedMessage, - }); - }; - - const createSubOrg = async () => { - const challenge = generateRandomBuffer(); - const subOrgName = `Turnkey Ethers+Passkey Demo - ${humanReadableDateTime()}`; - const authenticatorUserId = generateRandomBuffer(); - - const attestation = await getWebAuthnAttestation({ - publicKey: { - rp: { - id: "localhost", - name: "Turnkey Ethers Passkey Demo", - }, - challenge, - pubKeyCredParams: [ - { - type: "public-key", - // All algorithms can be found here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms - // Turnkey only supports ES256 at the moment. - alg: -7, - }, - ], - user: { - id: authenticatorUserId, - name: subOrgName, - displayName: subOrgName, - }, - }, - }); - - const res = await axios.post("/api/createSubOrg", { - subOrgName: subOrgName, - attestation, - challenge: base64UrlEncode(challenge), - }); - - setSubOrgId(res.data.subOrgId); - }; - - const login = async () => { - // We use the parent org ID, which we know at all times, - const res = await passkeyHttpClient.getWhoami({ - organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!, - }); - // to get the sub-org ID, which we don't know at this point because we don't - // have a DB. Note that we are able to perform this lookup by using the - // credential ID from the users WebAuthn stamp. - setSubOrgId(res.organizationId); - }; - - return ( -
- - Turnkey Logo - -
- {subOrgId && ( -
- Your sub-org ID:
- {subOrgId} -
- )} - {privateKey && ( -
- ETH address:
- {privateKey.address} -
- )} - {signedMessage && ( -
- Message:
- {signedMessage.message} -
-
- Signature:
- {signedMessage.signature} -
-
- - Verify with Etherscan - -
- )} -
- {!subOrgId && ( -
-

First, create a new sub-organization

-

- We'll prompt your browser to create a new passkey. The details - (credential ID, authenticator data, client data, attestation) will - be used to create a new{" "} - - Turnkey Sub-Organization - - . -
-
- This request to Turnkey will be created and signed by the backend - API key pair. -

-
- -
-
-
-

Already created a sub-organization? Log back in

-

- Based on the parent organization ID and a stamp from your passkey - used to created the sub-organization, we can look up your - sug-organization using the{" "} - - Whoami endpoint. - -

-
- -
-
- )} - {subOrgId && !privateKey && ( -
-

Next, create a new Ethereum address using your passkey

-

- We will sign the key creation request ( - - /public/v1/submit/create_private_keys - - ) with your passkey, and forward it to Turnkey through the NextJS - backend. -
-
- This ensures we can safely poll for activity completion and handle - errors. -

-
- -
-
- )} - {subOrgId && privateKey && ( -
-

Now let's sign something!

-

- We'll use an{" "} - - Ethers signer - {" "} - to do this, using{" "} - - @turnkey/ethers - - . You can kill your NextJS server if you want, everything happens on - the client-side! -

-
- - -
-
- )} -
- ); -} diff --git a/examples/with-ethers-and-passkeys/tsconfig.json b/examples/with-ethers-and-passkeys/tsconfig.json deleted file mode 100644 index 0c7555fa7..000000000 --- a/examples/with-ethers-and-passkeys/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/examples/with-federated-passkeys/src/pages/api/createSubOrg.ts b/examples/with-federated-passkeys/src/pages/api/createSubOrg.ts index d7a3f215f..a4098cdab 100644 --- a/examples/with-federated-passkeys/src/pages/api/createSubOrg.ts +++ b/examples/with-federated-passkeys/src/pages/api/createSubOrg.ts @@ -44,10 +44,10 @@ export default async function createUser( }); try { - const privateKeyName = `Default ETH Key`; + const walletName = `Default Wallet`; const completedActivity = await activityPoller({ - type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V3", + type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4", timestampMs: String(Date.now()), organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!, parameters: { @@ -66,14 +66,17 @@ export default async function createUser( ], }, ], - privateKeys: [ - { - privateKeyName, - curve: "CURVE_SECP256K1", - addressFormats: ["ADDRESS_FORMAT_ETHEREUM"], - privateKeyTags: [], - }, - ], + wallet: { + walletName, + accounts: [ + { + curve: "CURVE_SECP256K1", + pathFormat: "PATH_FORMAT_BIP32", + path: "m/44'/60'/0'/0/0", + addressFormat: "ADDRESS_FORMAT_ETHEREUM", + }, + ], + }, }, }); diff --git a/examples/with-solana/src/createSolanaTransfer.ts b/examples/with-solana/src/createSolanaTransfer.ts index 3ae44518d..0d95f446e 100644 --- a/examples/with-solana/src/createSolanaTransfer.ts +++ b/examples/with-solana/src/createSolanaTransfer.ts @@ -46,11 +46,11 @@ export async function createAndSignTransfer(input: { const messageToSign = transferTransaction.serializeMessage(); const activity = await client.signRawPayload({ - type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD", + type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", organizationId: turnkeyOrganizationId, timestampMs: String(Date.now()), parameters: { - privateKeyId: turnkeyPrivateKeyId, + signWith: turnkeyPrivateKeyId, payload: messageToSign.toString("hex"), encoding: "PAYLOAD_ENCODING_HEXADECIMAL", // Note: unlike ECDSA, EdDSA's API does not support signing raw digests (see RFC 8032). diff --git a/examples/with-viem-and-passkeys/.env.local.example b/examples/with-viem-and-passkeys/.env.local.example deleted file mode 100644 index 1479747c0..000000000 --- a/examples/with-viem-and-passkeys/.env.local.example +++ /dev/null @@ -1,4 +0,0 @@ -API_PUBLIC_KEY="" -API_PRIVATE_KEY="" -NEXT_PUBLIC_ORGANIZATION_ID="" -NEXT_PUBLIC_TURNKEY_API_BASE_URL=https://api.turnkey.com diff --git a/examples/with-viem-and-passkeys/.eslintrc.json b/examples/with-viem-and-passkeys/.eslintrc.json deleted file mode 100644 index bffb357a7..000000000 --- a/examples/with-viem-and-passkeys/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/examples/with-viem-and-passkeys/.gitignore b/examples/with-viem-and-passkeys/.gitignore deleted file mode 100644 index 8f322f0d8..000000000 --- a/examples/with-viem-and-passkeys/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/examples/with-viem-and-passkeys/README.md b/examples/with-viem-and-passkeys/README.md deleted file mode 100644 index fc3c142d2..000000000 --- a/examples/with-viem-and-passkeys/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Example: `with-viem-and-passkeys` - -This example shows how to create sub-organizations, create private keys, and sign with the [`@turnkey/viem`](../../packages/viem/) signer, using passkeys. - -![UI screenshot](./img/ui-screenshot.png) - -The flow showcases 3 ways to make requests to Turnkey: - -- the initial request to create a new [sub-organization](https://docs.turnkey.com/getting-started/sub-organizations) is authenticated in the NextJS backend with an API signature (using `API_PUBLIC_KEY`/`API_PRIVATE_KEY` from your `.env.local` file) -- the request to create a new ETH address is signed on the frontend with your passkey, but it's passed to the NextJS backend as a signed request (the body, stamp, and url are POSTed). This lets the backend submit this request on your behalf, and poll until the new "create private keys" activity completes. Once the activity completes it returns the new address to the frontend -- the request to sign a message is done 100% client-side via a Turnkey Viem signer (see [@turnkey/viem](../../packages/viem/)): it's signed with your passkey, and submitted from the browser to the Turnkey API directly. - -If you want to see a Viem demo with API keys instead of passkeys, head to the example [`with-viem`](../with-viem/). - -## Getting started - -### 1/ Cloning the example - -Make sure you have `Node.js` installed locally; we recommend using Node v16+. - -```bash -$ git clone https://github.com/tkhq/sdk -$ cd sdk/ -$ corepack enable # Install `pnpm` -$ pnpm install -r # Install dependencies -$ pnpm run build-all # Compile source code -$ cd examples/with-viem-and-passkeys/ -``` - -### 2/ Setting up Turnkey - -The first step is to set up your Turnkey organization and account. By following the [Quickstart](https://docs.turnkey.com/getting-started/quickstart) guide, you should have: - -- A public/private API key pair for Turnkey -- An organization ID - -Once you've gathered these values, add them to a new `.env.local` file. Notice that your API private key should be securely managed and **_never_** be committed to git. - -```bash -$ cp .env.local.example .env.local -``` - -Now open `.env.local` and add the missing environment variables: - -- `API_PUBLIC_KEY` -- `API_PRIVATE_KEY` -- `NEXT_PUBLIC_TURNKEY_API_BASE_URL` -- `NEXT_PUBLIC_ORGANIZATION_ID` - -### 3/ Running the app - -```bash -$ pnpm run dev -``` - -This command will start a NextJS app on localhost. If you navigate to http://localhost:3000 in your browser, you can follow the prompts to create a sub organization, create a private key for the newly created sub-organization, and sign a message using your passkey with a Viem custom account! diff --git a/examples/with-viem-and-passkeys/img/ui-screenshot.png b/examples/with-viem-and-passkeys/img/ui-screenshot.png deleted file mode 100644 index 4c54d5ee5..000000000 Binary files a/examples/with-viem-and-passkeys/img/ui-screenshot.png and /dev/null differ diff --git a/examples/with-viem-and-passkeys/next.config.js b/examples/with-viem-and-passkeys/next.config.js deleted file mode 100644 index 658404ac6..000000000 --- a/examples/with-viem-and-passkeys/next.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -module.exports = nextConfig; diff --git a/examples/with-viem-and-passkeys/package.json b/examples/with-viem-and-passkeys/package.json deleted file mode 100644 index dbbd9ae51..000000000 --- a/examples/with-viem-and-passkeys/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@turnkey/example-with-viem-and-passkeys", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "typecheck": "tsc --noEmit" - }, - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@turnkey/http": "workspace:*", - "@turnkey/api-key-stamper": "workspace:*", - "@turnkey/webauthn-stamper": "workspace:*", - "@turnkey/viem": "workspace:*", - "@types/node": "20.3.1", - "@types/react": "18.2.14", - "@types/react-dom": "18.2.6", - "axios": "^1.4.0", - "encoding": "^0.1.13", - "eslint": "8.43.0", - "eslint-config-next": "13.4.7", - "esm": "^3.2.25", - "install": "^0.13.0", - "next": "13.4.7", - "npm": "^9.7.2", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-hook-form": "^7.45.1", - "typescript": "5.1.3", - "viem": "^1.10.0" - } -} diff --git a/examples/with-viem-and-passkeys/public/favicon.svg b/examples/with-viem-and-passkeys/public/favicon.svg deleted file mode 100644 index 73948fa1a..000000000 --- a/examples/with-viem-and-passkeys/public/favicon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/examples/with-viem-and-passkeys/public/fonts/inter/Inter-Regular.woff2 b/examples/with-viem-and-passkeys/public/fonts/inter/Inter-Regular.woff2 deleted file mode 100644 index 6c2b6893d..000000000 Binary files a/examples/with-viem-and-passkeys/public/fonts/inter/Inter-Regular.woff2 and /dev/null differ diff --git a/examples/with-viem-and-passkeys/public/logo.svg b/examples/with-viem-and-passkeys/public/logo.svg deleted file mode 100644 index f983fd29b..000000000 --- a/examples/with-viem-and-passkeys/public/logo.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - diff --git a/examples/with-viem-and-passkeys/src/app/globals.css b/examples/with-viem-and-passkeys/src/app/globals.css deleted file mode 100644 index d94d4168d..000000000 --- a/examples/with-viem-and-passkeys/src/app/globals.css +++ /dev/null @@ -1,5 +0,0 @@ -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} diff --git a/examples/with-viem-and-passkeys/src/pages/_document.tsx b/examples/with-viem-and-passkeys/src/pages/_document.tsx deleted file mode 100644 index b32a0596e..000000000 --- a/examples/with-viem-and-passkeys/src/pages/_document.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Document, { Html, Head, Main, NextScript } from "next/document"; - -class ViemPasskeysDemo extends Document { - render() { - return ( - - - - - -
- - - - ); - } -} - -export default ViemPasskeysDemo; diff --git a/examples/with-viem-and-passkeys/src/pages/api/createKey.ts b/examples/with-viem-and-passkeys/src/pages/api/createKey.ts deleted file mode 100644 index 2ca17fbdf..000000000 --- a/examples/with-viem-and-passkeys/src/pages/api/createKey.ts +++ /dev/null @@ -1,95 +0,0 @@ -import axios from "axios"; -import type { NextApiRequest, NextApiResponse } from "next"; -import { - TSignedRequest, - TurnkeyClient, - createActivityPoller, -} from "@turnkey/http"; -import { ApiKeyStamper } from "@turnkey/api-key-stamper"; -import { refineNonNull } from "./utils"; - -type TResponse = { - message: string; - address?: string; - privateKeyId?: string; -}; - -function sleep(ms: number): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, ms); - }); -} - -export default async function createKey( - req: NextApiRequest, - res: NextApiResponse -) { - let signedRequest = req.body as TSignedRequest; - - try { - const activityResponse = await axios.post( - signedRequest.url, - signedRequest.body, - { - headers: { - [signedRequest.stamp.stampHeaderName]: - signedRequest.stamp.stampHeaderValue, - }, - } - ); - - if (activityResponse.status !== 200) { - res.status(500).json({ - message: `expected 200, got ${activityResponse.status}`, - }); - } - - const stamper = new ApiKeyStamper({ - apiPublicKey: process.env.API_PUBLIC_KEY!, - apiPrivateKey: process.env.API_PRIVATE_KEY!, - }); - const client = new TurnkeyClient( - { baseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL! }, - stamper - ); - - const activityPoller = createActivityPoller({ - client: client, - requestFn: client.getActivity, - }); - - const activityId = refineNonNull(activityResponse.data.activity?.id); - const subOrgId = refineNonNull( - activityResponse.data.activity?.organizationId - ); - - const completedActivity = await activityPoller({ - activityId, - organizationId: subOrgId, - }); - - const privateKeys = - completedActivity.result.createPrivateKeysResultV2?.privateKeys; - - // XXX: sorry for the ugly code! We expect a single key / address returned. - // If we have more than one key / address returned, or none, this would break. - const address = privateKeys - ?.map((pk) => pk.addresses?.map((addr) => addr.address).join("")) - .join(""); - const privateKeyId = privateKeys?.map((pk) => pk.privateKeyId).join(""); - - res.status(200).json({ - message: "successfully created key", - address: address, - privateKeyId: privateKeyId, - }); - } catch (e) { - console.error(e); - - res.status(500).json({ - message: `Something went wrong, caught error: ${e}`, - }); - } -} diff --git a/examples/with-viem-and-passkeys/src/pages/api/createSubOrg.ts b/examples/with-viem-and-passkeys/src/pages/api/createSubOrg.ts deleted file mode 100644 index 87e8207ea..000000000 --- a/examples/with-viem-and-passkeys/src/pages/api/createSubOrg.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { TurnkeyApiTypes, TurnkeyClient } from "@turnkey/http"; -import { createActivityPoller } from "@turnkey/http"; -import { ApiKeyStamper } from "@turnkey/api-key-stamper"; -import { refineNonNull } from "./utils"; - -type TAttestation = TurnkeyApiTypes["v1Attestation"]; - -type CreateSubOrgWithPrivateKeyRequest = { - subOrgName: string; - challenge: string; - privateKeyName: string; - attestation: TAttestation; -}; - -type CreateSubOrgResponse = { - subOrgId: string; - privateKeyId: string; - privateKeyAddress: string; -}; - -type ErrorMessage = { - message: string; -}; - -export default async function createUser( - req: NextApiRequest, - res: NextApiResponse -) { - const createSubOrgRequest = req.body as CreateSubOrgWithPrivateKeyRequest; - - try { - const turnkeyClient = new TurnkeyClient( - { baseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL! }, - new ApiKeyStamper({ - apiPublicKey: process.env.API_PUBLIC_KEY!, - apiPrivateKey: process.env.API_PRIVATE_KEY!, - }) - ); - - const activityPoller = createActivityPoller({ - client: turnkeyClient, - requestFn: turnkeyClient.createSubOrganization, - }); - - const privateKeyName = `Default ETH Key`; - - const completedActivity = await activityPoller({ - type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V3", - timestampMs: String(Date.now()), - organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!, - parameters: { - subOrganizationName: createSubOrgRequest.subOrgName, - rootQuorumThreshold: 1, - rootUsers: [ - { - userName: "New user", - apiKeys: [], - authenticators: [ - { - authenticatorName: "Passkey", - challenge: createSubOrgRequest.challenge, - attestation: createSubOrgRequest.attestation, - }, - ], - }, - ], - privateKeys: [ - { - privateKeyName: privateKeyName, - curve: "CURVE_SECP256K1", - addressFormats: ["ADDRESS_FORMAT_ETHEREUM"], - privateKeyTags: [], - }, - ], - }, - }); - - const subOrgId = refineNonNull( - completedActivity.result.createSubOrganizationResultV3?.subOrganizationId - ); - const privateKeys = refineNonNull( - completedActivity.result.createSubOrganizationResultV3?.privateKeys - ); - const privateKeyId = refineNonNull(privateKeys?.[0]?.privateKeyId); - const privateKeyAddress = refineNonNull( - privateKeys?.[0]?.addresses?.[0]?.address - ); - - res.status(200).json({ - subOrgId, - privateKeyId, - privateKeyAddress, - }); - } catch (e) { - console.error(e); - - res.status(500).json({ - message: "Something went wrong.", - }); - } -} diff --git a/examples/with-viem-and-passkeys/src/pages/api/utils.ts b/examples/with-viem-and-passkeys/src/pages/api/utils.ts deleted file mode 100644 index d26b4fd42..000000000 --- a/examples/with-viem-and-passkeys/src/pages/api/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function refineNonNull( - input: T | null | undefined, - errorMessage?: string -): T { - if (input == null) { - throw new Error(errorMessage ?? `Unexpected ${JSON.stringify(input)}`); - } - - return input; -} diff --git a/examples/with-viem-and-passkeys/src/pages/index.module.css b/examples/with-viem-and-passkeys/src/pages/index.module.css deleted file mode 100644 index 3c4cced5d..000000000 --- a/examples/with-viem-and-passkeys/src/pages/index.module.css +++ /dev/null @@ -1,90 +0,0 @@ -@font-face { - font-family: "Inter"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("../../public/fonts/inter/Inter-Regular.woff2?v=3.19") - format("woff2"); -} - -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - text-align: center; - padding: 2rem; - gap: 30px; - font-family: "Inter"; - max-width: 50ch; - margin: auto; -} - -.input { - width: 100%; - margin: 0; - padding: 10px 16px; - border-radius: 8px; - border-width: 1px; - border-style: solid; - border-color: rgba(216, 219, 227, 1); - font-family: "Inter"; -} - -.label { - font-family: "Inter"; -} - -.prompt { - font-family: "Inter"; -} - -.button { - display: inline-flex; - align-items: center; - justify-content: center; - margin: 0; - padding: 10px 16px; - border-radius: 8px; - border-width: 1px; - border-style: solid; - cursor: pointer; - color: white; - background-color: rgba(43, 47, 51, 1); - border-color: rgba(63, 70, 75, 1); - font-family: "Inter"; -} - -.form { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - gap: 10px; -} - -.form input { - max-width: 44ch; -} - -.info { - display: block; - font-size: 0.9em; - padding: 10px 16px; - margin: 10px; - border-radius: 8px; - background-color: rgb(240, 249, 250); - word-break: break-all; - box-shadow: inset 0 0 4px #e2e8e8; -} - -.code { - font-size: 1em; - font-weight: bold; -} - -.explainer { - line-height: 1.4em; - font-size: 0.9em; - color: rgb(134, 153, 156); -} diff --git a/examples/with-viem-and-passkeys/src/pages/index.tsx b/examples/with-viem-and-passkeys/src/pages/index.tsx deleted file mode 100644 index f01a551c7..000000000 --- a/examples/with-viem-and-passkeys/src/pages/index.tsx +++ /dev/null @@ -1,352 +0,0 @@ -import Image from "next/image"; -import styles from "./index.module.css"; -import { getWebAuthnAttestation, TurnkeyClient } from "@turnkey/http"; -import { createAccount } from "@turnkey/viem"; -import { useForm } from "react-hook-form"; -import axios from "axios"; -import { WebauthnStamper } from "@turnkey/webauthn-stamper"; -import { useState } from "react"; -import { createWalletClient, http, type Account } from "viem"; -import { sepolia } from "viem/chains"; - -type subOrgFormData = { - subOrgName: string; -}; - -type privateKeyFormData = { - privateKeyName: string; -}; - -type signingFormData = { - messageToSign: string; -}; - -const generateRandomBuffer = (): ArrayBuffer => { - const arr = new Uint8Array(32); - crypto.getRandomValues(arr); - return arr.buffer; -}; - -const base64UrlEncode = (challenge: ArrayBuffer): string => { - return Buffer.from(challenge) - .toString("base64") - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); -}; - -type TPrivateKeyState = { - id: string; - address: string; -} | null; - -type TSignedMessage = { - message: string; - signature: string; -} | null; - -const humanReadableDateTime = (): string => { - return new Date().toLocaleString().replaceAll("/", "-").replaceAll(":", "."); -}; - -export default function Home() { - const [subOrgId, setSubOrgId] = useState(null); - const [privateKey, setPrivateKey] = useState(null); - const [signedMessage, setSignedMessage] = useState(null); - - const { handleSubmit: subOrgFormSubmit } = useForm(); - const { register: signingFormRegister, handleSubmit: signingFormSubmit } = - useForm(); - const { handleSubmit: privateKeyFormSubmit } = useForm(); - const { register: _loginFormRegister, handleSubmit: loginFormSubmit } = - useForm(); - - const stamper = new WebauthnStamper({ - rpId: "localhost", - }); - - const passkeyHttpClient = new TurnkeyClient( - { - baseUrl: process.env.NEXT_PUBLIC_TURNKEY_API_BASE_URL!, - }, - stamper - ); - - const createPrivateKey = async () => { - if (!subOrgId) { - throw new Error("sub-org id not found"); - } - - const signedRequest = await passkeyHttpClient.stampCreatePrivateKeys({ - type: "ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2", - organizationId: subOrgId, - timestampMs: String(Date.now()), - parameters: { - privateKeys: [ - { - privateKeyName: `ETH Key ${Math.floor(Math.random() * 1000)}`, - curve: "CURVE_SECP256K1", - addressFormats: ["ADDRESS_FORMAT_ETHEREUM"], - privateKeyTags: [], - }, - ], - }, - }); - - const response = await axios.post("/api/createKey", signedRequest); - - setPrivateKey({ - id: response.data["privateKeyId"], - address: response.data["address"], - }); - }; - - const signMessage = async (data: signingFormData) => { - if (!subOrgId || !privateKey) { - throw new Error("sub-org id or private key not found"); - } - - const viemAccount = await createAccount({ - client: passkeyHttpClient, - organizationId: subOrgId, - privateKeyId: privateKey.id, - ethereumAddress: privateKey.address, - }); - - const viemClient = createWalletClient({ - account: viemAccount as Account, - chain: sepolia, - transport: http(), - }); - - const signedMessage = await viemClient.signMessage({ - message: data.messageToSign, - }); - - setSignedMessage({ - message: data.messageToSign, - signature: signedMessage, - }); - }; - - const createSubOrg = async () => { - const challenge = generateRandomBuffer(); - const subOrgName = `Turnkey Viem+Passkey Demo - ${humanReadableDateTime()}`; - const authenticatorUserId = generateRandomBuffer(); - - const attestation = await getWebAuthnAttestation({ - publicKey: { - rp: { - id: "localhost", - name: "Turnkey Viem Passkey Demo", - }, - challenge, - pubKeyCredParams: [ - { - type: "public-key", - // All algorithms can be found here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms - // Turnkey only supports ES256 at the moment. - alg: -7, - }, - ], - user: { - id: authenticatorUserId, - name: subOrgName, - displayName: subOrgName, - }, - }, - }); - - const res = await axios.post("/api/createSubOrg", { - subOrgName: subOrgName, - attestation, - challenge: base64UrlEncode(challenge), - }); - - setSubOrgId(res.data.subOrgId); - }; - - const login = async () => { - // We use the parent org ID, which we know at all times, - const res = await passkeyHttpClient.getWhoami({ - organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!, - }); - // to get the sub-org ID, which we don't know at this point because we don't - // have a DB. Note that we are able to perform this lookup by using the - // credential ID from the users WebAuthn stamp. - setSubOrgId(res.organizationId); - }; - - return ( -
- - Turnkey Logo - -
- {subOrgId && ( -
- Your sub-org ID:
- {subOrgId} -
- )} - {privateKey && ( -
- ETH address:
- {privateKey.address} -
- )} - {signedMessage && ( -
- Message:
- {signedMessage.message} -
-
- Signature:
- {signedMessage.signature} -
-
- - Verify with Etherscan - -
- )} -
- {!subOrgId && ( -
-

First, create a new sub-organization

-

- We'll prompt your browser to create a new passkey. The details - (credential ID, authenticator data, client data, attestation) will - be used to create a new{" "} - - Turnkey Sub-Organization - - . -
-
- This request to Turnkey will be created and signed by the backend - API key pair. -

-
- -
-
-
-

Already created a sub-organization? Log back in

-

- Based on the parent organization ID and a stamp from your passkey - used to created the sub-organization, we can look up your - sug-organization using the{" "} - - Whoami endpoint. - -

-
- -
-
- )} - {subOrgId && !privateKey && ( -
-

Next, create a new Ethereum address using your passkey

-

- We will sign the key creation request ( - - /public/v1/submit/create_private_keys - - ) with your passkey, and forward it to Turnkey through the NextJS - backend. -
-
- This ensures we can safely poll for activity completion and handle - errors. -

-
- -
-
- )} - {subOrgId && privateKey && ( -
-

Now let's sign something!

-

- We'll use a{" "} - - Viem custom account - {" "} - to do this, using{" "} - - @turnkey/viem - - . You can kill your NextJS server if you want, everything happens on - the client-side! -

-
- - -
-
- )} -
- ); -} diff --git a/examples/with-viem-and-passkeys/tsconfig.json b/examples/with-viem-and-passkeys/tsconfig.json deleted file mode 100644 index 0c7555fa7..000000000 --- a/examples/with-viem-and-passkeys/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/packages/cosmjs/CHANGELOG.md b/packages/cosmjs/CHANGELOG.md index 4992aede2..f1b2eff42 100644 --- a/packages/cosmjs/CHANGELOG.md +++ b/packages/cosmjs/CHANGELOG.md @@ -1,5 +1,13 @@ # @turnkey/cosmjs +## 0.4.10 + +### Patch Changes + +- Updated dependencies + - @turnkey/http@2.0.0 +- Updated the shape of signing + ## 0.4.9 ### Patch Changes diff --git a/packages/cosmjs/package.json b/packages/cosmjs/package.json index 936af2326..8d3853326 100644 --- a/packages/cosmjs/package.json +++ b/packages/cosmjs/package.json @@ -1,6 +1,6 @@ { "name": "@turnkey/cosmjs", - "version": "0.4.9", + "version": "0.4.10", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "Apache-2.0", diff --git a/packages/cosmjs/src/index.ts b/packages/cosmjs/src/index.ts index af2428d27..c6ea6896e 100644 --- a/packages/cosmjs/src/index.ts +++ b/packages/cosmjs/src/index.ts @@ -141,11 +141,11 @@ export class TurnkeyDirectWallet implements OfflineDirectSigner { ): Promise { const { activity } = await TurnkeyApi.signRawPayload({ body: { - type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD", + type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", organizationId: this.organizationId, timestampMs: String(Date.now()), parameters: { - privateKeyId: this.privateKeyId, + signWith: this.privateKeyId, payload: toHex(message), encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", diff --git a/packages/ethers/CHANGELOG.md b/packages/ethers/CHANGELOG.md index f3b1c07ea..84136bcb6 100644 --- a/packages/ethers/CHANGELOG.md +++ b/packages/ethers/CHANGELOG.md @@ -1,5 +1,13 @@ # @turnkey/ethers +## 0.17.3 + +### Patch Changes + +- Updated dependencies + - @turnkey/http@2.0.0 +- Updated the shape of signing + ## 0.17.2 ### Patch Changes diff --git a/packages/ethers/package.json b/packages/ethers/package.json index 46d7ab967..3f05373b5 100644 --- a/packages/ethers/package.json +++ b/packages/ethers/package.json @@ -1,6 +1,6 @@ { "name": "@turnkey/ethers", - "version": "0.17.2", + "version": "0.17.3", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "Apache-2.0", diff --git a/packages/ethers/src/index.ts b/packages/ethers/src/index.ts index 17962ca1c..6b4f0f32e 100644 --- a/packages/ethers/src/index.ts +++ b/packages/ethers/src/index.ts @@ -72,10 +72,10 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner { private async _signTransactionImpl(message: string): Promise { const { activity } = await this.client.signTransaction({ - type: "ACTIVITY_TYPE_SIGN_TRANSACTION", + type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2", organizationId: this.organizationId, parameters: { - privateKeyId: this.privateKeyId, + signWith: this.privateKeyId, type: "TRANSACTION_TYPE_ETHEREUM", unsignedTransaction: message, }, @@ -181,10 +181,10 @@ export class TurnkeySigner extends ethers.Signer implements TypedDataSigner { async _signMessageImpl(message: string): Promise { const { activity } = await this.client.signRawPayload({ - type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD", + type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", organizationId: this.organizationId, parameters: { - privateKeyId: this.privateKeyId, + signWith: this.privateKeyId, payload: message, encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NO_OP", diff --git a/packages/http/CHANGELOG.md b/packages/http/CHANGELOG.md index 072b2e802..cf0ae6ebb 100644 --- a/packages/http/CHANGELOG.md +++ b/packages/http/CHANGELOG.md @@ -1,5 +1,17 @@ # @turnkey/http +## 2.0.0 + +### Major Changes + +- Synced protos from mono + +### Upgrade notes + +- `signRawPayload` and `signTransaction` now expect a `signWith` param instead of `privateKeyId` previously +- `signRawPayload` and `signTransaction` have been updated to expect a new type: `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2` and `ACTIVITY_TYPE_SIGN_TRANSACTION_V2`, respectively +- If you have policies authorizing `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD` or `ACTIVITY_TYPE_SIGN_TRANSACTION` specifically, they will need to be updated to authorize `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2` and `ACTIVITY_TYPE_SIGN_TRANSACTION_V2` (or better yet, update your policies to allow all signing actions categorically using policy resources and actions. See https://docs.turnkey.com/managing-policies/examples) + ## 1.3.0 ### Minor Changes diff --git a/packages/http/package.json b/packages/http/package.json index f42ee757b..88c50c35c 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -1,6 +1,6 @@ { "name": "@turnkey/http", - "version": "1.3.0", + "version": "2.0.0", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "Apache-2.0", diff --git a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.client.ts b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.client.ts index 0e91f20fc..ccc325355 100644 --- a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.client.ts +++ b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.client.ts @@ -144,18 +144,10 @@ import type { TSignRawPayloadBody, TSignRawPayloadResponse, } from "./public_api.fetcher"; -import type { - TSignRawPayloadV2Body, - TSignRawPayloadV2Response, -} from "./public_api.fetcher"; import type { TSignTransactionBody, TSignTransactionResponse, } from "./public_api.fetcher"; -import type { - TSignTransactionV2Body, - TSignTransactionV2Response, -} from "./public_api.fetcher"; import type { TUpdateAllowedOriginsBody, TUpdateAllowedOriginsResponse, @@ -929,7 +921,7 @@ export class TurnkeyClient { }; /** - * Create a Wallet + * Create a Wallet and derive addresses * * Sign the provided `TCreateWalletBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/create_wallet). * @@ -960,7 +952,7 @@ export class TurnkeyClient { }; /** - * Create Wallet accounts + * Derive additional addresses using an existing wallet * * Sign the provided `TCreateWalletAccountsBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/create_wallet_accounts). * @@ -1057,14 +1049,14 @@ export class TurnkeyClient { /** * Delete an existing Invitation * - * Sign the provided `TDeleteInvitationBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/delete_invitations). + * Sign the provided `TDeleteInvitationBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/delete_invitation). * * See also {@link stampDeleteInvitation}. */ deleteInvitation = async ( input: TDeleteInvitationBody ): Promise => { - return this.request("/public/v1/submit/delete_invitations", input); + return this.request("/public/v1/submit/delete_invitation", input); }; /** @@ -1075,8 +1067,7 @@ export class TurnkeyClient { stampDeleteInvitation = async ( input: TDeleteInvitationBody ): Promise => { - const fullUrl = - this.config.baseUrl + "/public/v1/submit/delete_invitations"; + const fullUrl = this.config.baseUrl + "/public/v1/submit/delete_invitation"; const body = JSON.stringify(input); const stamp = await this.stamper.stamp(body); return { @@ -1181,7 +1172,7 @@ export class TurnkeyClient { }; /** - * Initializes a new recovery + * Initializes a new email recovery * * Sign the provided `TInitUserEmailRecoveryBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/init_user_email_recovery). * @@ -1339,7 +1330,7 @@ export class TurnkeyClient { }; /** - * Sign a raw payload with a Private Key + * Sign a raw payload * * Sign the provided `TSignRawPayloadBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/sign_raw_payload). * @@ -1370,39 +1361,7 @@ export class TurnkeyClient { }; /** - * Sign a raw payload with a Private Key id or address - * - * Sign the provided `TSignRawPayloadV2Body` with the client's `stamp` function, and submit the request (POST /public/v1/submit/sign_raw_payload_v2). - * - * See also {@link stampSignRawPayloadV2}. - */ - signRawPayloadV2 = async ( - input: TSignRawPayloadV2Body - ): Promise => { - return this.request("/public/v1/submit/sign_raw_payload_v2", input); - }; - - /** - * Produce a `SignedRequest` from `TSignRawPayloadV2Body` by using the client's `stamp` function. - * - * See also {@link SignRawPayloadV2}. - */ - stampSignRawPayloadV2 = async ( - input: TSignRawPayloadV2Body - ): Promise => { - const fullUrl = - this.config.baseUrl + "/public/v1/submit/sign_raw_payload_v2"; - const body = JSON.stringify(input); - const stamp = await this.stamper.stamp(body); - return { - body: body, - stamp: stamp, - url: fullUrl, - }; - }; - - /** - * Sign a transaction with a Private Key + * Sign a transaction * * Sign the provided `TSignTransactionBody` with the client's `stamp` function, and submit the request (POST /public/v1/submit/sign_transaction). * @@ -1432,38 +1391,6 @@ export class TurnkeyClient { }; }; - /** - * Sign a transaction with a Private Key id or address - * - * Sign the provided `TSignTransactionV2Body` with the client's `stamp` function, and submit the request (POST /public/v1/submit/sign_transaction_v2). - * - * See also {@link stampSignTransactionV2}. - */ - signTransactionV2 = async ( - input: TSignTransactionV2Body - ): Promise => { - return this.request("/public/v1/submit/sign_transaction_v2", input); - }; - - /** - * Produce a `SignedRequest` from `TSignTransactionV2Body` by using the client's `stamp` function. - * - * See also {@link SignTransactionV2}. - */ - stampSignTransactionV2 = async ( - input: TSignTransactionV2Body - ): Promise => { - const fullUrl = - this.config.baseUrl + "/public/v1/submit/sign_transaction_v2"; - const body = JSON.stringify(input); - const stamp = await this.stamper.stamp(body); - return { - body: body, - stamp: stamp, - url: fullUrl, - }; - }; - /** * Update the allowable origins for credentials and requests * diff --git a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.fetcher.ts b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.fetcher.ts index 3eefa80bd..2b56da8b3 100644 --- a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.fetcher.ts +++ b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.fetcher.ts @@ -1133,7 +1133,7 @@ export type TCreateWalletBody = /** * Create Wallet * - * Create a Wallet + * Create a Wallet and derive addresses * * `POST /public/v1/submit/create_wallet` */ @@ -1177,9 +1177,9 @@ export type TCreateWalletAccountsBody = operations["PublicApiService_CreateWalletAccounts"]["parameters"]["body"]["body"]; /** - * Create Wallet accounts + * Create Wallet Accounts * - * Create Wallet accounts + * Derive additional addresses using an existing wallet * * `POST /public/v1/submit/create_wallet_accounts` */ @@ -1310,18 +1310,18 @@ export const signDeleteAuthenticators = ( }); /** - * `POST /public/v1/submit/delete_invitations` + * `POST /public/v1/submit/delete_invitation` */ export type TDeleteInvitationResponse = operations["PublicApiService_DeleteInvitation"]["responses"]["200"]["schema"]; /** - * `POST /public/v1/submit/delete_invitations` + * `POST /public/v1/submit/delete_invitation` */ export type TDeleteInvitationInput = { body: TDeleteInvitationBody }; /** - * `POST /public/v1/submit/delete_invitations` + * `POST /public/v1/submit/delete_invitation` */ export type TDeleteInvitationBody = operations["PublicApiService_DeleteInvitation"]["parameters"]["body"]["body"]; @@ -1331,7 +1331,7 @@ export type TDeleteInvitationBody = * * Delete an existing Invitation * - * `POST /public/v1/submit/delete_invitations` + * `POST /public/v1/submit/delete_invitation` */ export const deleteInvitation = (input: TDeleteInvitationInput) => request< @@ -1341,7 +1341,7 @@ export const deleteInvitation = (input: TDeleteInvitationInput) => never, never >({ - uri: "/public/v1/submit/delete_invitations", + uri: "/public/v1/submit/delete_invitation", method: "POST", body: input.body, }); @@ -1356,7 +1356,7 @@ export const signDeleteInvitation = ( options?: TurnkeyCredentialRequestOptions ) => signedRequest({ - uri: "/public/v1/submit/delete_invitations", + uri: "/public/v1/submit/delete_invitation", body: input.body, options, }); @@ -1523,9 +1523,9 @@ export type TInitUserEmailRecoveryBody = operations["PublicApiService_InitUserEmailRecovery"]["parameters"]["body"]["body"]; /** - * Init Recovery + * Init Email Recovery * - * Initializes a new recovery + * Initializes a new email recovery * * `POST /public/v1/submit/init_user_email_recovery` */ @@ -1779,7 +1779,7 @@ export type TSignRawPayloadBody = /** * Sign Raw Payload * - * Sign a raw payload with a Private Key + * Sign a raw payload * * `POST /public/v1/submit/sign_raw_payload` */ @@ -1805,58 +1805,6 @@ export const signSignRawPayload = ( options, }); -/** - * `POST /public/v1/submit/sign_raw_payload_v2` - */ -export type TSignRawPayloadV2Response = - operations["PublicApiService_SignRawPayloadV2"]["responses"]["200"]["schema"]; - -/** - * `POST /public/v1/submit/sign_raw_payload_v2` - */ -export type TSignRawPayloadV2Input = { body: TSignRawPayloadV2Body }; - -/** - * `POST /public/v1/submit/sign_raw_payload_v2` - */ -export type TSignRawPayloadV2Body = - operations["PublicApiService_SignRawPayloadV2"]["parameters"]["body"]["body"]; - -/** - * Sign Raw Payload - * - * Sign a raw payload with a Private Key id or address - * - * `POST /public/v1/submit/sign_raw_payload_v2` - */ -export const signRawPayloadV2 = (input: TSignRawPayloadV2Input) => - request< - TSignRawPayloadV2Response, - TSignRawPayloadV2Body, - never, - never, - never - >({ - uri: "/public/v1/submit/sign_raw_payload_v2", - method: "POST", - body: input.body, - }); - -/** - * Request a WebAuthn assertion and return a signed `SignRawPayloadV2` request, ready to be POSTed to Turnkey. - * - * See {@link SignRawPayloadV2} - */ -export const signSignRawPayloadV2 = ( - input: TSignRawPayloadV2Input, - options?: TurnkeyCredentialRequestOptions -) => - signedRequest({ - uri: "/public/v1/submit/sign_raw_payload_v2", - body: input.body, - options, - }); - /** * `POST /public/v1/submit/sign_transaction` */ @@ -1877,7 +1825,7 @@ export type TSignTransactionBody = /** * Sign Transaction * - * Sign a transaction with a Private Key + * Sign a transaction * * `POST /public/v1/submit/sign_transaction` */ @@ -1903,58 +1851,6 @@ export const signSignTransaction = ( options, }); -/** - * `POST /public/v1/submit/sign_transaction_v2` - */ -export type TSignTransactionV2Response = - operations["PublicApiService_SignTransactionV2"]["responses"]["200"]["schema"]; - -/** - * `POST /public/v1/submit/sign_transaction_v2` - */ -export type TSignTransactionV2Input = { body: TSignTransactionV2Body }; - -/** - * `POST /public/v1/submit/sign_transaction_v2` - */ -export type TSignTransactionV2Body = - operations["PublicApiService_SignTransactionV2"]["parameters"]["body"]["body"]; - -/** - * Sign Transaction - * - * Sign a transaction with a Private Key id or address - * - * `POST /public/v1/submit/sign_transaction_v2` - */ -export const signTransactionV2 = (input: TSignTransactionV2Input) => - request< - TSignTransactionV2Response, - TSignTransactionV2Body, - never, - never, - never - >({ - uri: "/public/v1/submit/sign_transaction_v2", - method: "POST", - body: input.body, - }); - -/** - * Request a WebAuthn assertion and return a signed `SignTransactionV2` request, ready to be POSTed to Turnkey. - * - * See {@link SignTransactionV2} - */ -export const signSignTransactionV2 = ( - input: TSignTransactionV2Input, - options?: TurnkeyCredentialRequestOptions -) => - signedRequest({ - uri: "/public/v1/submit/sign_transaction_v2", - body: input.body, - options, - }); - /** * `POST /public/v1/submit/update_allowed_origins` */ diff --git a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.swagger.json b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.swagger.json index e2edbfd4b..6f725040b 100644 --- a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.swagger.json +++ b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.swagger.json @@ -22,9 +22,17 @@ "name": "Policies", "description": "Policies allow for deep customization of the security of your Organization. They can be used to grant permissions or restrict usage of Users and Private Keys. The Policy Engine analyzes all of your Policies on each request to determine whether an Activity is allowed.\n\nSee [Policy Overview](../managing-policies/overview) for more information" }, + { + "name": "Wallets", + "description": "Wallets contain collections of deterministically generated cryptographic public / private key pairs that share a common seed. Turnkey securely holds the common seed, but only you can access it. In most cases, Wallets should be preferred over Private Keys since they can be represented by a mnemonic phrase, used across a variety of cryptographic curves, and can derive many addresses." + }, + { + "name": "Signatures", + "description": "Signatures are used to validate the authenticity and integrity of a digital message. Signatures are a fundamental building block in blockchains. Turnkey makes it easy to produce signatures by allowing you to sign with an address. If Turnkey doesn't yet support an address format you need, you can generate and sign with the public key instead by using the address format `ADDRESS_FORMAT_COMPRESSED`." + }, { "name": "Private Keys", - "description": "Private Keys are cryptographic public / private key pairs that can be used for cryptocurrency needs or more generalized encryption. Think of Private Keys as your own programmable wallet. Turnkey securely holds all Private Key materials for you, but only you can access them." + "description": "Private Keys are cryptographic public / private key pairs that can be used for cryptocurrency needs or more generalized encryption. Turnkey securely holds all Private Key materials for you, but only you can access them." }, { "name": "Private Key Tags", @@ -799,7 +807,7 @@ "/public/v1/submit/create_wallet": { "post": { "summary": "Create Wallet", - "description": "Create a Wallet", + "description": "Create a Wallet and derive addresses", "operationId": "PublicApiService_CreateWallet", "responses": { "200": { @@ -825,13 +833,13 @@ } } ], - "tags": ["PublicApiService"] + "tags": ["Wallets"] } }, "/public/v1/submit/create_wallet_accounts": { "post": { - "summary": "Create Wallet accounts", - "description": "Create Wallet accounts", + "summary": "Create Wallet Accounts", + "description": "Derive additional addresses using an existing wallet", "operationId": "PublicApiService_CreateWalletAccounts", "responses": { "200": { @@ -857,7 +865,7 @@ } } ], - "tags": ["PublicApiService"] + "tags": ["Wallets"] } }, "/public/v1/submit/delete_api_keys": { @@ -924,7 +932,7 @@ "tags": ["Authenticators"] } }, - "/public/v1/submit/delete_invitations": { + "/public/v1/submit/delete_invitation": { "post": { "summary": "Delete Invitation", "description": "Delete an existing Invitation", @@ -1049,13 +1057,13 @@ } } ], - "tags": ["PublicApiService"] + "tags": ["Wallets"] } }, "/public/v1/submit/init_user_email_recovery": { "post": { - "summary": "Init Recovery", - "description": "Initializes a new recovery", + "summary": "Init Email Recovery", + "description": "Initializes a new email recovery", "operationId": "PublicApiService_InitUserEmailRecovery", "responses": { "200": { @@ -1081,7 +1089,7 @@ } } ], - "tags": ["Organizations"] + "tags": ["User Recovery"] } }, "/public/v1/submit/recover_user": { @@ -1113,7 +1121,7 @@ } } ], - "tags": ["Organizations"] + "tags": ["User Recovery"] } }, "/public/v1/submit/reject_activity": { @@ -1177,7 +1185,7 @@ } } ], - "tags": ["Organizations"] + "tags": ["Features"] } }, "/public/v1/submit/set_organization_feature": { @@ -1209,13 +1217,13 @@ } } ], - "tags": ["Organizations"] + "tags": ["Features"] } }, "/public/v1/submit/sign_raw_payload": { "post": { "summary": "Sign Raw Payload", - "description": "Sign a raw payload with a Private Key", + "description": "Sign a raw payload", "operationId": "PublicApiService_SignRawPayload", "responses": { "200": { @@ -1241,45 +1249,13 @@ } } ], - "tags": ["Private Keys"] - } - }, - "/public/v1/submit/sign_raw_payload_v2": { - "post": { - "summary": "Sign Raw Payload", - "description": "Sign a raw payload with a Private Key id or address", - "operationId": "PublicApiService_SignRawPayloadV2", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1ActivityResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1SignRawPayloadV2Request" - } - } - ], - "tags": ["PublicApiService"] + "tags": ["Signatures"] } }, "/public/v1/submit/sign_transaction": { "post": { "summary": "Sign Transaction", - "description": "Sign a transaction with a Private Key", + "description": "Sign a transaction", "operationId": "PublicApiService_SignTransaction", "responses": { "200": { @@ -1305,39 +1281,7 @@ } } ], - "tags": ["Private Keys"] - } - }, - "/public/v1/submit/sign_transaction_v2": { - "post": { - "summary": "Sign Transaction", - "description": "Sign a transaction with a Private Key id or address", - "operationId": "PublicApiService_SignTransactionV2", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1ActivityResponse" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1SignTransactionV2Request" - } - } - ], - "tags": ["PublicApiService"] + "tags": ["Signatures"] } }, "/public/v1/submit/update_allowed_origins": { @@ -1369,7 +1313,7 @@ } } ], - "tags": ["Organizations"] + "tags": ["Features"] } }, "/public/v1/submit/update_policy": { @@ -2034,7 +1978,8 @@ "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", "ACTIVITY_TYPE_SIGN_TRANSACTION_V2", "ACTIVITY_TYPE_EXPORT_PRIVATE_KEY", - "ACTIVITY_TYPE_EXPORT_WALLET" + "ACTIVITY_TYPE_EXPORT_WALLET", + "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4" ] }, "v1ApiKey": { @@ -2872,12 +2817,43 @@ "privateKeys" ] }, + "v1CreateSubOrganizationIntentV4": { + "type": "object", + "properties": { + "subOrganizationName": { + "type": "string", + "description": "Name for this sub-organization" + }, + "rootUsers": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1RootUserParams" + }, + "description": "Root users to create within this sub-organization" + }, + "rootQuorumThreshold": { + "type": "integer", + "format": "int32", + "description": "The threshold of unique approvals to reach root quorum. This value must be less than or equal to the number of root users" + }, + "wallet": { + "$ref": "#/definitions/v1WalletParams", + "description": "The wallet to create for the sub-organization" + }, + "disableEmailRecovery": { + "type": "boolean", + "description": "Disable email recovery for the sub-organization" + } + }, + "required": ["subOrganizationName", "rootUsers", "rootQuorumThreshold"] + }, "v1CreateSubOrganizationRequest": { "type": "object", "properties": { "type": { "type": "string", - "enum": ["ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V3"] + "enum": ["ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4"] }, "timestampMs": { "type": "string", @@ -2888,7 +2864,7 @@ "description": "Unique identifier for a given Organization." }, "parameters": { - "$ref": "#/definitions/v1CreateSubOrganizationIntentV3" + "$ref": "#/definitions/v1CreateSubOrganizationIntentV4" } }, "required": ["type", "timestampMs", "organizationId", "parameters"] @@ -2919,6 +2895,18 @@ }, "required": ["subOrganizationId", "privateKeys"] }, + "v1CreateSubOrganizationResultV4": { + "type": "object", + "properties": { + "subOrganizationId": { + "type": "string" + }, + "wallet": { + "$ref": "#/definitions/v1WalletResult" + } + }, + "required": ["subOrganizationId"] + }, "v1CreateUserTagIntent": { "type": "object", "properties": { @@ -4119,6 +4107,9 @@ }, "exportWalletIntent": { "$ref": "#/definitions/v1ExportWalletIntent" + }, + "createSubOrganizationIntentV4": { + "$ref": "#/definitions/v1CreateSubOrganizationIntentV4" } }, "required": ["createOrganizationIntent"] @@ -4777,6 +4768,9 @@ }, "exportWalletResult": { "$ref": "#/definitions/v1ExportWalletResult" + }, + "createSubOrganizationResultV4": { + "$ref": "#/definitions/v1CreateSubOrganizationResultV4" } } }, @@ -4976,7 +4970,7 @@ "properties": { "signWith": { "type": "string", - "description": "The Private Key identifier or address." + "description": "A Wallet account address, Private Key address, or Private Key identifier." }, "payload": { "type": "string", @@ -4998,7 +4992,7 @@ "properties": { "type": { "type": "string", - "enum": ["ACTIVITY_TYPE_SIGN_RAW_PAYLOAD"] + "enum": ["ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2"] }, "timestampMs": { "type": "string", @@ -5009,7 +5003,7 @@ "description": "Unique identifier for a given Organization." }, "parameters": { - "$ref": "#/definitions/v1SignRawPayloadIntent" + "$ref": "#/definitions/v1SignRawPayloadIntentV2" } }, "required": ["type", "timestampMs", "organizationId", "parameters"] @@ -5032,27 +5026,6 @@ }, "required": ["r", "s", "v"] }, - "v1SignRawPayloadV2Request": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2"] - }, - "timestampMs": { - "type": "string", - "description": "Timestamp (in milliseconds) of the request, used to verify liveness of user requests." - }, - "organizationId": { - "type": "string", - "description": "Unique identifier for a given Organization." - }, - "parameters": { - "$ref": "#/definitions/v1SignRawPayloadIntentV2" - } - }, - "required": ["type", "timestampMs", "organizationId", "parameters"] - }, "v1SignTransactionIntent": { "type": "object", "properties": { @@ -5075,7 +5048,7 @@ "properties": { "signWith": { "type": "string", - "description": "The Private Key identifier or address." + "description": "A Wallet account address, Private Key address, or Private Key identifier." }, "unsignedTransaction": { "type": "string", @@ -5092,7 +5065,7 @@ "properties": { "type": { "type": "string", - "enum": ["ACTIVITY_TYPE_SIGN_TRANSACTION"] + "enum": ["ACTIVITY_TYPE_SIGN_TRANSACTION_V2"] }, "timestampMs": { "type": "string", @@ -5103,7 +5076,7 @@ "description": "Unique identifier for a given Organization." }, "parameters": { - "$ref": "#/definitions/v1SignTransactionIntent" + "$ref": "#/definitions/v1SignTransactionIntentV2" } }, "required": ["type", "timestampMs", "organizationId", "parameters"] @@ -5117,27 +5090,6 @@ }, "required": ["signedTransaction"] }, - "v1SignTransactionV2Request": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["ACTIVITY_TYPE_SIGN_TRANSACTION_V2"] - }, - "timestampMs": { - "type": "string", - "description": "Timestamp (in milliseconds) of the request, used to verify liveness of user requests." - }, - "organizationId": { - "type": "string", - "description": "Unique identifier for a given Organization." - }, - "parameters": { - "$ref": "#/definitions/v1SignTransactionIntentV2" - } - }, - "required": ["type", "timestampMs", "organizationId", "parameters"] - }, "v1SimpleClientExtensionResults": { "type": "object", "properties": { @@ -5736,6 +5688,40 @@ }, "required": ["curve", "pathFormat", "path", "addressFormat"] }, + "v1WalletParams": { + "type": "object", + "properties": { + "walletName": { + "type": "string", + "description": "Human-readable name for a Wallet." + }, + "accounts": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1WalletAccountParams" + }, + "description": "A list of wallet Accounts." + } + }, + "required": ["walletName", "accounts"] + }, + "v1WalletResult": { + "type": "object", + "properties": { + "walletId": { + "type": "string" + }, + "addresses": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of account addresses." + } + }, + "required": ["walletId", "addresses"] + }, "v1WebAuthnStamp": { "type": "object", "properties": { @@ -5787,15 +5773,19 @@ "x-tagGroups": [ { "name": "ORGANIZATIONS", - "tags": ["Organizations", "Invitations", "Policies"] + "tags": ["Organizations", "Invitations", "Policies", "Features"] }, { "name": "PRIVATE KEYS", - "tags": ["Private Keys", "Private Key Tags"] + "tags": ["Wallets", "Signatures", "Private Keys", "Private Key Tags"] }, { "name": "USERS", - "tags": ["Users", "User Tags", "Authenticators", "API Keys", "Who am I?"] + "tags": ["Users", "User Tags", "User Recovery"] + }, + { + "name": "CREDENTIALS", + "tags": ["Authenticators", "API Keys", "Who am I?"] }, { "name": "ACTIVITIES", diff --git a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.types.ts b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.types.ts index 983c6bcd4..e0012dea3 100644 --- a/packages/http/src/__generated__/services/coordinator/public/v1/public_api.types.ts +++ b/packages/http/src/__generated__/services/coordinator/public/v1/public_api.types.ts @@ -97,11 +97,11 @@ export type paths = { post: operations["PublicApiService_CreateUsers"]; }; "/public/v1/submit/create_wallet": { - /** Create a Wallet */ + /** Create a Wallet and derive addresses */ post: operations["PublicApiService_CreateWallet"]; }; "/public/v1/submit/create_wallet_accounts": { - /** Create Wallet accounts */ + /** Derive additional addresses using an existing wallet */ post: operations["PublicApiService_CreateWalletAccounts"]; }; "/public/v1/submit/delete_api_keys": { @@ -112,7 +112,7 @@ export type paths = { /** Remove authenticators from a User */ post: operations["PublicApiService_DeleteAuthenticators"]; }; - "/public/v1/submit/delete_invitations": { + "/public/v1/submit/delete_invitation": { /** Delete an existing Invitation */ post: operations["PublicApiService_DeleteInvitation"]; }; @@ -129,7 +129,7 @@ export type paths = { post: operations["PublicApiService_ExportWallet"]; }; "/public/v1/submit/init_user_email_recovery": { - /** Initializes a new recovery */ + /** Initializes a new email recovery */ post: operations["PublicApiService_InitUserEmailRecovery"]; }; "/public/v1/submit/recover_user": { @@ -149,21 +149,13 @@ export type paths = { post: operations["PublicApiService_SetOrganizationFeature"]; }; "/public/v1/submit/sign_raw_payload": { - /** Sign a raw payload with a Private Key */ + /** Sign a raw payload */ post: operations["PublicApiService_SignRawPayload"]; }; - "/public/v1/submit/sign_raw_payload_v2": { - /** Sign a raw payload with a Private Key id or address */ - post: operations["PublicApiService_SignRawPayloadV2"]; - }; "/public/v1/submit/sign_transaction": { - /** Sign a transaction with a Private Key */ + /** Sign a transaction */ post: operations["PublicApiService_SignTransaction"]; }; - "/public/v1/submit/sign_transaction_v2": { - /** Sign a transaction with a Private Key id or address */ - post: operations["PublicApiService_SignTransactionV2"]; - }; "/public/v1/submit/update_allowed_origins": { /** Update the allowable origins for credentials and requests */ post: operations["PublicApiService_UpdateAllowedOrigins"]; @@ -455,7 +447,8 @@ export type definitions = { | "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2" | "ACTIVITY_TYPE_SIGN_TRANSACTION_V2" | "ACTIVITY_TYPE_EXPORT_PRIVATE_KEY" - | "ACTIVITY_TYPE_EXPORT_WALLET"; + | "ACTIVITY_TYPE_EXPORT_WALLET" + | "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4"; v1ApiKey: { /** @description A User credential that can be used to authenticate to Turnkey. */ credential: definitions["externaldatav1Credential"]; @@ -768,14 +761,29 @@ export type definitions = { /** @description A list of Private Keys. */ privateKeys: definitions["v1PrivateKeyParams"][]; }; + v1CreateSubOrganizationIntentV4: { + /** @description Name for this sub-organization */ + subOrganizationName: string; + /** @description Root users to create within this sub-organization */ + rootUsers: definitions["v1RootUserParams"][]; + /** + * Format: int32 + * @description The threshold of unique approvals to reach root quorum. This value must be less than or equal to the number of root users + */ + rootQuorumThreshold: number; + /** @description The wallet to create for the sub-organization */ + wallet?: definitions["v1WalletParams"]; + /** @description Disable email recovery for the sub-organization */ + disableEmailRecovery?: boolean; + }; v1CreateSubOrganizationRequest: { /** @enum {string} */ - type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V3"; + type: "ACTIVITY_TYPE_CREATE_SUB_ORGANIZATION_V4"; /** @description Timestamp (in milliseconds) of the request, used to verify liveness of user requests. */ timestampMs: string; /** @description Unique identifier for a given Organization. */ organizationId: string; - parameters: definitions["v1CreateSubOrganizationIntentV3"]; + parameters: definitions["v1CreateSubOrganizationIntentV4"]; }; v1CreateSubOrganizationResult: { subOrganizationId: string; @@ -785,6 +793,10 @@ export type definitions = { /** @description A list of Private Key IDs and addresses. */ privateKeys: definitions["v1PrivateKeyResult"][]; }; + v1CreateSubOrganizationResultV4: { + subOrganizationId: string; + wallet?: definitions["v1WalletResult"]; + }; v1CreateUserTagIntent: { /** @description Human-readable name for a User Tag. */ userTagName: string; @@ -1234,6 +1246,7 @@ export type definitions = { signTransactionIntentV2?: definitions["v1SignTransactionIntentV2"]; exportPrivateKeyIntent?: definitions["v1ExportPrivateKeyIntent"]; exportWalletIntent?: definitions["v1ExportWalletIntent"]; + createSubOrganizationIntentV4?: definitions["v1CreateSubOrganizationIntentV4"]; }; v1Invitation: { /** @description Unique identifier for a given Invitation object. */ @@ -1460,6 +1473,7 @@ export type definitions = { removeOrganizationFeatureResult?: definitions["v1RemoveOrganizationFeatureResult"]; exportPrivateKeyResult?: definitions["v1ExportPrivateKeyResult"]; exportWalletResult?: definitions["v1ExportWalletResult"]; + createSubOrganizationResultV4?: definitions["v1CreateSubOrganizationResultV4"]; }; v1RootUserParams: { /** @description Human-readable name for a User. */ @@ -1536,7 +1550,7 @@ export type definitions = { hashFunction: definitions["immutableactivityv1HashFunction"]; }; v1SignRawPayloadIntentV2: { - /** @description The Private Key identifier or address. */ + /** @description A Wallet account address, Private Key address, or Private Key identifier. */ signWith: string; /** @description Raw unsigned payload to be signed. */ payload: string; @@ -1547,12 +1561,12 @@ export type definitions = { }; v1SignRawPayloadRequest: { /** @enum {string} */ - type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD"; + type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2"; /** @description Timestamp (in milliseconds) of the request, used to verify liveness of user requests. */ timestampMs: string; /** @description Unique identifier for a given Organization. */ organizationId: string; - parameters: definitions["v1SignRawPayloadIntent"]; + parameters: definitions["v1SignRawPayloadIntentV2"]; }; v1SignRawPayloadResult: { /** @description Component of an ECSDA signature. */ @@ -1562,15 +1576,6 @@ export type definitions = { /** @description Component of an ECSDA signature. */ v: string; }; - v1SignRawPayloadV2Request: { - /** @enum {string} */ - type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2"; - /** @description Timestamp (in milliseconds) of the request, used to verify liveness of user requests. */ - timestampMs: string; - /** @description Unique identifier for a given Organization. */ - organizationId: string; - parameters: definitions["v1SignRawPayloadIntentV2"]; - }; v1SignTransactionIntent: { /** @description Unique identifier for a given Private Key. */ privateKeyId: string; @@ -1579,7 +1584,7 @@ export type definitions = { type: definitions["v1TransactionType"]; }; v1SignTransactionIntentV2: { - /** @description The Private Key identifier or address. */ + /** @description A Wallet account address, Private Key address, or Private Key identifier. */ signWith: string; /** @description Raw unsigned transaction to be signed */ unsignedTransaction: string; @@ -1587,25 +1592,16 @@ export type definitions = { }; v1SignTransactionRequest: { /** @enum {string} */ - type: "ACTIVITY_TYPE_SIGN_TRANSACTION"; + type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2"; /** @description Timestamp (in milliseconds) of the request, used to verify liveness of user requests. */ timestampMs: string; /** @description Unique identifier for a given Organization. */ organizationId: string; - parameters: definitions["v1SignTransactionIntent"]; + parameters: definitions["v1SignTransactionIntentV2"]; }; v1SignTransactionResult: { signedTransaction: string; }; - v1SignTransactionV2Request: { - /** @enum {string} */ - type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2"; - /** @description Timestamp (in milliseconds) of the request, used to verify liveness of user requests. */ - timestampMs: string; - /** @description Unique identifier for a given Organization. */ - organizationId: string; - parameters: definitions["v1SignTransactionIntentV2"]; - }; v1SimpleClientExtensionResults: { appid?: boolean; appidExclude?: boolean; @@ -1831,6 +1827,17 @@ export type definitions = { /** @description Address format used to generate a wallet Acccount. */ addressFormat: definitions["immutablecommonv1AddressFormat"]; }; + v1WalletParams: { + /** @description Human-readable name for a Wallet. */ + walletName: string; + /** @description A list of wallet Accounts. */ + accounts: definitions["v1WalletAccountParams"][]; + }; + v1WalletResult: { + walletId: string; + /** @description A list of account addresses. */ + addresses: string[]; + }; v1WebAuthnStamp: { /** @description A base64 url encoded Unique identifier for a given credential. */ credentialId: string; @@ -2258,7 +2265,7 @@ export type operations = { }; }; }; - /** Create a Wallet */ + /** Create a Wallet and derive addresses */ PublicApiService_CreateWallet: { parameters: { body: { @@ -2276,7 +2283,7 @@ export type operations = { }; }; }; - /** Create Wallet accounts */ + /** Derive additional addresses using an existing wallet */ PublicApiService_CreateWalletAccounts: { parameters: { body: { @@ -2402,7 +2409,7 @@ export type operations = { }; }; }; - /** Initializes a new recovery */ + /** Initializes a new email recovery */ PublicApiService_InitUserEmailRecovery: { parameters: { body: { @@ -2492,7 +2499,7 @@ export type operations = { }; }; }; - /** Sign a raw payload with a Private Key */ + /** Sign a raw payload */ PublicApiService_SignRawPayload: { parameters: { body: { @@ -2510,25 +2517,7 @@ export type operations = { }; }; }; - /** Sign a raw payload with a Private Key id or address */ - PublicApiService_SignRawPayloadV2: { - parameters: { - body: { - body: definitions["v1SignRawPayloadV2Request"]; - }; - }; - responses: { - /** A successful response. */ - 200: { - schema: definitions["v1ActivityResponse"]; - }; - /** An unexpected error response. */ - default: { - schema: definitions["rpcStatus"]; - }; - }; - }; - /** Sign a transaction with a Private Key */ + /** Sign a transaction */ PublicApiService_SignTransaction: { parameters: { body: { @@ -2546,24 +2535,6 @@ export type operations = { }; }; }; - /** Sign a transaction with a Private Key id or address */ - PublicApiService_SignTransactionV2: { - parameters: { - body: { - body: definitions["v1SignTransactionV2Request"]; - }; - }; - responses: { - /** A successful response. */ - 200: { - schema: definitions["v1ActivityResponse"]; - }; - /** An unexpected error response. */ - default: { - schema: definitions["rpcStatus"]; - }; - }; - }; /** Update the allowable origins for credentials and requests */ PublicApiService_UpdateAllowedOrigins: { parameters: { diff --git a/packages/http/src/__tests__/async-test.ts b/packages/http/src/__tests__/async-test.ts index 49fce18a9..7ff0b5fef 100644 --- a/packages/http/src/__tests__/async-test.ts +++ b/packages/http/src/__tests__/async-test.ts @@ -250,9 +250,9 @@ test("`withAsyncPolling` should also work with synchronous activity endpoints", const result = await mutation({ body: { - type: "ACTIVITY_TYPE_SIGN_TRANSACTION", + type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2", parameters: { - privateKeyId: "9725c4f7-8387-4990-9128-1d2218bef256", + signWith: "9725c4f7-8387-4990-9128-1d2218bef256", type: "TRANSACTION_TYPE_ETHEREUM", unsignedTransaction: "02e801808459682f008509d4ae542e8252089440f008f4c17075efca092ae650655f6693aeced00180c0", diff --git a/packages/viem/CHANGELOG.md b/packages/viem/CHANGELOG.md index d68a879c1..79a92b704 100644 --- a/packages/viem/CHANGELOG.md +++ b/packages/viem/CHANGELOG.md @@ -1,5 +1,15 @@ # @turnkey/viem +## 0.2.6 + +### Patch Changes + +- 59dcd2f: Unpin typescript +- da7c960: Bump Viem dependency to fix `getAddresses()` for LocalAccount +- Updated dependencies + - @turnkey/http@2.0.0 +- Updated the shape of signing + ## 0.2.5 ### Patch Changes diff --git a/packages/viem/package.json b/packages/viem/package.json index fd2ca6bb5..bac80c7de 100644 --- a/packages/viem/package.json +++ b/packages/viem/package.json @@ -1,6 +1,6 @@ { "name": "@turnkey/viem", - "version": "0.2.5", + "version": "0.2.6", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "Apache-2.0", diff --git a/packages/viem/src/index.ts b/packages/viem/src/index.ts index d1c060c1d..1d70e0b14 100644 --- a/packages/viem/src/index.ts +++ b/packages/viem/src/index.ts @@ -268,10 +268,10 @@ async function signTransactionImpl( privateKeyId: string ): Promise { const { activity } = await client.signTransaction({ - type: "ACTIVITY_TYPE_SIGN_TRANSACTION", + type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2", organizationId: organizationId, parameters: { - privateKeyId: privateKeyId, + signWith: privateKeyId, type: "TRANSACTION_TYPE_ETHEREUM", unsignedTransaction: unsignedTransaction, }, @@ -329,10 +329,10 @@ async function signMessageImpl( privateKeyId: string ): Promise { const { activity } = await client.signRawPayload({ - type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD", + type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2", organizationId: organizationId, parameters: { - privateKeyId: privateKeyId, + signWith: privateKeyId, payload: message, encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NO_OP", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4afabf578..7308e1471 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -246,69 +246,6 @@ importers: specifier: ^5.7.2 version: 5.7.2 - examples/with-ethers-and-passkeys: - dependencies: - '@turnkey/api-key-stamper': - specifier: workspace:* - version: link:../../packages/api-key-stamper - '@turnkey/ethers': - specifier: workspace:* - version: link:../../packages/ethers - '@turnkey/http': - specifier: workspace:* - version: link:../../packages/http - '@turnkey/webauthn-stamper': - specifier: workspace:* - version: link:../../packages/webauthn-stamper - '@types/node': - specifier: 20.3.1 - version: 20.3.1 - '@types/react': - specifier: 18.2.14 - version: 18.2.14 - '@types/react-dom': - specifier: 18.2.6 - version: 18.2.6 - axios: - specifier: ^1.4.0 - version: 1.4.0 - encoding: - specifier: ^0.1.13 - version: 0.1.13 - eslint: - specifier: 8.43.0 - version: 8.43.0 - eslint-config-next: - specifier: 13.4.7 - version: 13.4.7(eslint@8.43.0)(typescript@5.1.3) - esm: - specifier: ^3.2.25 - version: 3.2.25 - ethers: - specifier: ^5.7.2 - version: 5.7.2 - install: - specifier: ^0.13.0 - version: 0.13.0 - next: - specifier: 13.4.7 - version: 13.4.7(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0) - npm: - specifier: ^9.7.2 - version: 9.7.2 - react: - specifier: 18.2.0 - version: 18.2.0 - react-dom: - specifier: 18.2.0 - version: 18.2.0(react@18.2.0) - react-hook-form: - specifier: ^7.45.1 - version: 7.45.1(react@18.2.0) - typescript: - specifier: 5.1.3 - version: 5.1.3 - examples/with-federated-passkeys: dependencies: '@turnkey/api-key-stamper': @@ -504,69 +441,6 @@ importers: specifier: ^1.16.6 version: 1.16.6(typescript@5.1.3) - examples/with-viem-and-passkeys: - dependencies: - '@turnkey/api-key-stamper': - specifier: workspace:* - version: link:../../packages/api-key-stamper - '@turnkey/http': - specifier: workspace:* - version: link:../../packages/http - '@turnkey/viem': - specifier: workspace:* - version: link:../../packages/viem - '@turnkey/webauthn-stamper': - specifier: workspace:* - version: link:../../packages/webauthn-stamper - '@types/node': - specifier: 20.3.1 - version: 20.3.1 - '@types/react': - specifier: 18.2.14 - version: 18.2.14 - '@types/react-dom': - specifier: 18.2.6 - version: 18.2.6 - axios: - specifier: ^1.4.0 - version: 1.4.0 - encoding: - specifier: ^0.1.13 - version: 0.1.13 - eslint: - specifier: 8.43.0 - version: 8.43.0 - eslint-config-next: - specifier: 13.4.7 - version: 13.4.7(eslint@8.43.0)(typescript@5.1.3) - esm: - specifier: ^3.2.25 - version: 3.2.25 - install: - specifier: ^0.13.0 - version: 0.13.0 - next: - specifier: 13.4.7 - version: 13.4.7(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0) - npm: - specifier: ^9.7.2 - version: 9.7.2 - react: - specifier: 18.2.0 - version: 18.2.0 - react-dom: - specifier: 18.2.0 - version: 18.2.0(react@18.2.0) - react-hook-form: - specifier: ^7.45.1 - version: 7.45.1(react@18.2.0) - typescript: - specifier: 5.1.3 - version: 5.1.3 - viem: - specifier: ^1.10.0 - version: 1.10.0(typescript@5.1.3) - internal/jest-config: dependencies: '@babel/core': @@ -692,10 +566,6 @@ packages: engines: {node: '>=0.10.0'} dev: false - /@adraffy/ens-normalize@1.9.0: - resolution: {integrity: sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==} - dev: false - /@adraffy/ens-normalize@1.9.4: resolution: {integrity: sha512-UK0bHA7hh9cR39V+4gl2/NnBBjoXIxkuWAPCaY4X7fbH4L/azIi7ilWOCjMUYfpJgraLUAqkRi2BqrjME8Rynw==} @@ -3660,12 +3530,6 @@ packages: dev: false optional: true - /@noble/curves@1.0.0: - resolution: {integrity: sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==} - dependencies: - '@noble/hashes': 1.3.0 - dev: false - /@noble/curves@1.1.0: resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} dependencies: @@ -3680,10 +3544,6 @@ packages: /@noble/hashes@1.2.0: resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} - /@noble/hashes@1.3.0: - resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} - dev: false - /@noble/hashes@1.3.1: resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} engines: {node: '>= 16'} @@ -4352,14 +4212,6 @@ packages: '@noble/secp256k1': 1.7.1 '@scure/base': 1.1.3 - /@scure/bip32@1.3.0: - resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==} - dependencies: - '@noble/curves': 1.0.0 - '@noble/hashes': 1.3.2 - '@scure/base': 1.1.1 - dev: false - /@scure/bip32@1.3.2: resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==} dependencies: @@ -4373,13 +4225,6 @@ packages: '@noble/hashes': 1.2.0 '@scure/base': 1.1.3 - /@scure/bip39@1.2.0: - resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} - dependencies: - '@noble/hashes': 1.3.2 - '@scure/base': 1.1.1 - dev: false - /@scure/bip39@1.2.1: resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} dependencies: @@ -4676,12 +4521,6 @@ packages: '@types/node': 16.18.12 dev: false - /@types/ws@8.5.5: - resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} - dependencies: - '@types/node': 16.18.12 - dev: false - /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -4901,20 +4740,6 @@ packages: through: 2.3.8 dev: false - /abitype@0.9.3(typescript@5.1.3): - resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3 >=3.19.1 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - dependencies: - typescript: 5.1.3 - dev: false - /abitype@0.9.8(typescript@5.1.3): resolution: {integrity: sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ==} peerDependencies: @@ -7614,14 +7439,6 @@ packages: ws: 7.5.9 dev: false - /isomorphic-ws@5.0.0(ws@8.12.0): - resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} - peerDependencies: - ws: '*' - dependencies: - ws: 8.12.0 - dev: false - /isows@1.0.3(ws@8.13.0): resolution: {integrity: sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==} peerDependencies: @@ -10134,30 +9951,6 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /viem@1.10.0(typescript@5.1.3): - resolution: {integrity: sha512-uV8ICcgu+3BghTpzsSiYtpyPUrZpTYgPjBkwhPoM6WsF34JAKdV7HVWiWKxrrkP5GqIRJhkT5lRflJrdv5ftng==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@adraffy/ens-normalize': 1.9.0 - '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.0 - '@scure/bip32': 1.3.0 - '@scure/bip39': 1.2.0 - '@types/ws': 8.5.5 - abitype: 0.9.3(typescript@5.1.3) - isomorphic-ws: 5.0.0(ws@8.12.0) - typescript: 5.1.3 - ws: 8.12.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - dev: false - /viem@1.16.6(typescript@5.1.3): resolution: {integrity: sha512-jcWcFQ+xzIfDwexwPJRvCuCRJKEkK9iHTStG7mpU5MmuSBpACs4nATBDyXNFtUiyYTFzLlVEwWkt68K0nCSImg==} peerDependencies: @@ -10463,19 +10256,6 @@ packages: utf-8-validate: optional: true - /ws@8.12.0: - resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false - /ws@8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10): resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} engines: {node: '>=10.0.0'}