Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Quickstart Guide to use Examples from the Javascript SDK and add more context to examples #75

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/getting-started/Examples.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
description: Check out some of our example apps and use cases
slug: /getting-started/examples
---
Expand All @@ -23,7 +23,7 @@ That said, we have built out several example services and applications to help i
| [`rebalancer`](https://github.com/tkhq/sdk/tree/main/examples/rebalancer/) | A demo application which showcases an example of how to use Turnkey for managing multiple types of keys & users |
| [`sweeper`](https://github.com/tkhq/sdk/tree/main/examples/sweeper/) | Sweep funds from one address to a different address |
| [`trading-runner`](https://github.com/tkhq/sdk/tree/main/examples/trading-runner/) | A sample application demonstrating a trading operation, using various private keys, users, and policies, powered by Uniswap |
| [`wallet-export`](https://github.com/tkhq/sdk/tree/main/examples/wallet-export/) | A NextJS app that demonstrates how to use `@turnkey/iframe-stamper` to export a wallet as a mnemonic |
| [`wallet-export`](https://github.com/tkhq/sdk/tree/main/examples/wallet-export/) | A NextJS app that demonstrates how to use `@turnkey/iframe-stamper` to export a wallet as a mnemonic |
| [`with-ethers`](https://github.com/tkhq/sdk/tree/main/examples/with-ethers/) | Create a new Ethereum address, then sign and broadcast a transaction using the Ethers signer with Infura |
| [`with-viem`](https://github.com/tkhq/sdk/tree/main/examples/with-viem/) | Sign and broadcast a transaction using the Turnkey Custom Account and Infura |
| [`with-cosmjs`](https://github.com/tkhq/sdk/tree/main/examples/with-cosmjs/) | Create a new Cosmos address, then sign and broadcast a transaction on Celestia testnet using the CosmJS signer |
Expand Down Expand Up @@ -103,4 +103,4 @@ A simple example using Turnkey and Figment to easily automate ETH staking.
/>
</p>

See https://docs.figment.io/recipes/stake-eth-from-turnkey for the code.
See https://docs.figment.io/recipes/stake-eth-from-turnkey for the code.
2 changes: 1 addition & 1 deletion docs/getting-started/Organizations.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
description: Learn about Organizations on Turnkey
slug: /getting-started/organizations
---
Expand Down
195 changes: 195 additions & 0 deletions docs/getting-started/Quickstart-Javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
id: quickstart-javascript
sidebar_position: 2
description: Onboard and sign your first Ethereum transaction
slug: /getting-started/quickstart
---
# Quickstart

This quickstart will guide you through Turnkey’s onboarding, adding an API key, creating a wallet, and signing your first Ethereum transaction.

## Create your Turnkey Organization

- Visit [app.turnkey.com/dashboard/auth/initial](https://app.turnkey.com/dashboard/auth/initial) and enter your email address
- Confirm your email by clicking on the link inside of the confirmation email
- Follow the prompts to add your first authenticator and create your organization

## Find your Organization ID

All API requests require an organization ID. Yours can be located in the user dropdown menu at the top right corner of the dashboard.

<img
src="/img/quickstart/find_organization_id.png"
alt="Find organization ID"
style={{ width: 940 }}
/>

You'll want to save this somewhere in your code, as you'll need it to make requests to the Turnkey API.

```javascript
const TURNKEY_ORGANIZATION_ID = "<Your Org ID>";
```
AlwaysBCoding marked this conversation as resolved.
Show resolved Hide resolved

## Create an API Key

Turnkey API Keys are generic public / private key pairs that allow you to make requests to our API. You can create an API Key from your user page of the dashboard. Navigate to your user page by clicking on "User Details" in the user dropdown menu, and then click "Create an API key".

<img
src="/img/quickstart/find_user_details.png"
alt="Find user details"
style={{ width: 940 }}
/>

<img
src="/img/quickstart/create_api_key.png"
alt="Find user details"
style={{ width: 940 }}
/>

- Select "Generate API keys in-browser" and click continue.
- Give your API key pair a name and click continue.
- Save your Public and Private Key locally.
- Make sure to click "Approve" to sign the API Creation activity with your authenticator device.

A couple of notes:
- You will need both the public and private key to sign requests to the Turnkey API.
- **Any code using a Turnkey API private key should only ever be run server-side.**
- Every action on Turnkey will return an `activity`, including creating the API key pair in the previous step. You can read more about the [Turnkey Activity Model here](../policy-management/Policy-language.md#activity-breakdown).

## Require the Turnkey Libraries

There are two libraries that you will need to make API requests to Turnkey:
1. The Turnkey HTTP library.
2. A Turnkey "stamper" library.
AlwaysBCoding marked this conversation as resolved.
Show resolved Hide resolved

The stamper library is responsible for signing a request into Turnkey, and comes in 3 different flavors:
1. `api-key-stamper` which signs requests with your Turnkey API key
2. `webauthn-stamper` which signs requests with a end-user's passkey
3. `iframe-stamper` which is a wrapper around the api-key-stamper and used specifically for Email Recovery and Email Auth
AlwaysBCoding marked this conversation as resolved.
Show resolved Hide resolved

The simplest way to get started, is to use the API Key Stamper to make requests to Turnkey that are signed with the API key pair you created in the previous step.
AlwaysBCoding marked this conversation as resolved.
Show resolved Hide resolved

```shell
npm install @turnkey/http
npm install @turnkey/api-key-stamper
```

## Initialize the Turnkey Client
```javascript
import { ApiKeyStamper } from '@turnkey/api-key-stamper';
import { TurnkeyClient } from '@turnkey/http';

const TURNKEY_ORGANIZATION_ID = "<Your Org ID>";

const stamper = new ApiKeyStamper({
apiPublicKey: "<Your Public Key>",
apiPrivateKey: "<Your Private Key>"
})

const turnkeyClient = new TurnkeyClient(
{
baseUrl: 'https://api.turnkey.com'
},
stamper
);
```

## Create a Wallet

A `wallet` on Turnkey represents a multi-chain seed phrase from which many individual `accounts` can be derived. An `account` represents a specific index on a derivation path and contains the blockchain address that you can send funds to and sign on-chain transactions with. The only thing a wallet needs to be initialized is a name for the wallet.
AlwaysBCoding marked this conversation as resolved.
Show resolved Hide resolved

```javascript
const response = await turnkeyClient.createWallet({
organizationId: TURNKEY_ORGANIZATION_ID,
type: 'ACTIVITY_TYPE_CREATE_WALLET',
timestampMs: String(Date.now()),
parameters: {
walletName: "Test Wallet 1",
accounts: []
}
})

const walletId = response.activity.result.createWalletResult.walletId;
```
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should create the account in here to simplify onboarding? That way there's a single activity rather than 2

Copy link
Contributor Author

@AlwaysBCoding AlwaysBCoding Jan 24, 2024

Choose a reason for hiding this comment

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

So my thought on this is that there's already confusion with the distinction between wallets and accounts and you can clear up some of that confusion to someone reading this guide by explicitly creating the resources in separate steps. I think in general it's a good pattern for quickstart guides to avoid creating multiple resources in a single step if there's a way to create them individually. I'd prefer leaving this as is, with the note underneath:

Note: If desired, you can also create accounts in the same API call where you create the wallet by passing in the account derivation paths as parameters to the createWallet call.

Copy link
Contributor

Choose a reason for hiding this comment

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

In the context of a "quick" start there are competing priorities: time to value is very important because typically people want to run through this as quick as possible. The other priority is clarity.

In this case it's probably fine to leave things the way you have it if you think it's clearer. But it's "one more thing" to copy/paste; that's the trade-off.


## Create an Ethereum Account

Once a `wallet` has been created, `accounts` can be created against that `wallet` by passing in the derivation path information for any `accounts` that you want to derive. In this example we will derive Ethereum accounts, using the standard BIP44 Path format. The final number at the end of the path string represents the index in the derivation path that you want to derive the account for.

Note: If desired, you can also create `accounts` in the same API call where you create the `wallet` by passing in the account derivation paths as arguments to the `createWallet` call.

```javascript
await client.createWalletAccounts({
organizationId: TURNKEY_ORGANIZATION_ID,
type: 'ACTIVITY_TYPE_CREATE_WALLET_ACCOUNTS',
timestampMs: String(Date.now()),
parameters: {
walletId: "<Your wallet id>",
accounts: [
{
path: "m/44'/60'/0'/0/0", // account at index 0
pathFormat: "PATH_FORMAT_BIP32",
curve: "CURVE_SECP256K1",
addressFormat: "ADDRESS_FORMAT_ETHEREUM"
},
{
path: "m/44'/60'/0'/0/1", // account at index 1
pathFormat: "PATH_FORMAT_BIP32",
curve: "CURVE_SECP256K1",
addressFormat: "ADDRESS_FORMAT_ETHEREUM"
},
{
path: "m/44'/60'/0'/0/2", // account at index 2
pathFormat: "PATH_FORMAT_BIP32",
curve: "CURVE_SECP256K1",
addressFormat: "ADDRESS_FORMAT_ETHEREUM"
},
]
}
})
```

You can view the created accounts and their associated Ethereum addresses with

```javascript
await client.getWalletAccounts({
organizationId: TURNKEY_ORGANIZATION_ID,
walletId: "<Your wallet id>"
})
```

## Sign a Transaction

Once you have an account, you can sign a transaction with the account as follows.

```javascript
await turnkeyClient.signTransaction({
organizationId: TURNKEY_ORGANIZATION_ID,
type: "ACTIVITY_TYPE_SIGN_TRANSACTION_V2",
timestampMs: String(Date.now()),
parameters: {
signWith: "<Your ethereum address>" // i.e. 0x780ed9b6BF99908106d1bAA25b7658a80ADB5f42
unsignedTransaction: "<Your unsigned transaction>", // i.e. 02eb018084db4f550d850bb6338fa88252089470a8a81613dd06dc243de94ebdf861ff5f82b361831e848080c0
type: "TRANSACTION_TYPE_ETHEREUM"
}
})
```

Make sure to replace the `unsignedTransaction` below with your own. You can use our [simple transaction generator](https://build.tx.xyz) if you need a quick transaction for testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this needs to be replaced with code to create an unsigned transaction

If you'd like to broadcast your transaction, you can easily do so via [Etherscan](https://etherscan.io/pushTx).


## Using the Webauthn Stamper

The previous actions all had to be signed server-side in our code using a Turnkey API key, but you can also have individual end-users sign Turnkey activities using their own passkeys using the webauthn stamper library. You can learn more about the specifics of the passkey implementation in the [Passkey guide](../passkeys/introduction). You can also see an example of having an end-user sign a request with a passkey in our public [Demo Passkey Wallet](https://github.com/tkhq/demo-passkey-wallet) repository.
AlwaysBCoding marked this conversation as resolved.
Show resolved Hide resolved

## Best Practices (Using Sub-Organizations)

Due to cryptographic limitations of how much data can be signed at once, generally speaking, a common pattern is to create sub-organizations for each individual user, instead of creating wallets for each user directly on the parent organization. You can read more about how to properly do this in the [Suborganization Guide](../integration-guides/sub-organizations-as-wallets.md).

## Next Steps

- Check out our [examples](/getting-started/examples) to see what can be built.
- Learn more about [Organizations](/getting-started/organizations) and [Wallets](/getting-started/wallets).
- See our [API design](/api-introduction) or dive into our [API reference](/api).
8 changes: 4 additions & 4 deletions docs/getting-started/Sub-Organizations.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
description: Learn about sub-orgs and how you can use them
slug: /getting-started/sub-organizations
---
Expand All @@ -9,15 +9,15 @@ Using Turnkey’s flexible infrastructure, you can programmatically create and m

We envision sub-organizations being very useful to model your End-Users if you're a business using Turnkey for key management. Let's explore how.

## Creating Sub-Organizations
## Creating Sub-Organizations

Creating a new sub-organization is an activity performed by the parent organization. The activity itself takes the following attributes as inputs:
Creating a new sub-organization is an activity performed by the parent organization. The activity itself takes the following attributes as inputs:
- organization name
- a list of root users
- a root quorum threshold
- [optional] a wallet (note: in versions prior to V4, this was a private key)

Root users can be programmatic or human, with one or many credentials attached.
Root users can be programmatic or human, with one or many credentials attached.

## Using Sub-Organizations

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
id: quickstart
sidebar_position: 2
id: using-the-cli
sidebar_position: 3
description: Onboard and sign your first Ethereum transaction
slug: /getting-started/quickstart
slug: /getting-started/using-the-cli
---
# Quickstart
# Using the CLI

This quickstart will guide you through Turnkey’s onboarding, adding an API key, creating a wallet, and signing your first Ethereum transaction.

Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/Wallets.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 6
sidebar_position: 7
description: Learn about Wallets on Turnkey
slug: /getting-started/wallets
---
Expand Down Expand Up @@ -61,4 +61,4 @@ Turnkey also supports raw private keys, but we recommend using Wallets since the

## Export keys

Exporting on Turnkey enables you or your end users to export a copy of a Wallet or Private Key from our system at any time. While most Turnkey users opt to keep Wallets within Turnkey's secure infrastructure, the export functionality means you are never locked into Turnkey, and gives you the freedom to design your own backup processes as you see fit. Check out our [Export Wallet guide](../integration-guides/export-wallets.md) to allow your users to securely export their wallets.
Exporting on Turnkey enables you or your end users to export a copy of a Wallet or Private Key from our system at any time. While most Turnkey users opt to keep Wallets within Turnkey's secure infrastructure, the export functionality means you are never locked into Turnkey, and gives you the freedom to design your own backup processes as you see fit. Check out our [Export Wallet guide](../integration-guides/export-wallets.md) to allow your users to securely export their wallets.
2 changes: 1 addition & 1 deletion docs/getting-started/email-auth.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 8
sidebar_position: 9
description: Learn about Email Auth on Turnkey
slug: /getting-started/email-auth
---
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/email-recovery.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 7
sidebar_position: 8
description: Learn about Email Recovery on Turnkey
slug: /getting-started/email-recovery
---
Expand Down Expand Up @@ -34,7 +34,7 @@ Once a user receives a recovery email, recovery credential _decryption_ needs to

## Authorization

Authorization for email recovery is based on our usual activity authorization: our [policy engine](../policy-management/Policy-overview.md) controls who can and cannot execute recovery-related activities.
Authorization for email recovery is based on our usual activity authorization: our [policy engine](../policy-management/Policy-overview.md) controls who can and cannot execute recovery-related activities.
* `ACTIVITY_TYPE_INIT_USER_EMAIL_RECOVERY` can be performed by the root user or by any user in an organization if authorized by policy, but **only if the feature is enabled**. The activity can target **any user** in this organization **or any sub-organization user**. The activity will fail if a parent user tries to initiate recovery for a sub-organization which has [opted out of email recovery](#opting-out-of-email-recovery).
* `ACTIVITY_TYPE_RECOVER_USER` should be signed by the recovery credential sent via email. Even if not explicitly allowed by policy, a user is always able to add credentials to their own user. This includes adding a new authenticator when authenticated with a recovery credential. In other words, no special policy is needed to make this work: users are able to recover out-of-the-box.

Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/resource-limits.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 9
sidebar_position: 10
description: Organization resource limits
slug: /getting-started/resource-limits
---
Expand Down
4 changes: 2 additions & 2 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ const config = {
darkTheme: darkCodeTheme,
},
algolia: {
appId: '89KSB43UFT',
appId: '89KSB43UFT',
// Public API key: it is safe to commit it
apiKey: 'a0740f141135937727389d897f51fb56',
indexName: 'turnkey',
contextualSearch: true,
searchPagePath: false,
searchPagePath: false,
},
}),
};
Expand Down
Binary file added static/img/quickstart/create_api_key.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading