Skip to content

Commit

Permalink
Merge pull-request #139
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewkmin committed Oct 18, 2023
2 parents e47ac43 + a5a2843 commit 405b410
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-turtles-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@turnkey/viem": patch
---

Bump Viem dependency to fix `getAddresses()` for LocalAccount
4 changes: 2 additions & 2 deletions examples/with-viem-and-passkeys/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useForm } from "react-hook-form";
import axios from "axios";
import { WebauthnStamper } from "@turnkey/webauthn-stamper";
import { useState } from "react";
import { createWalletClient, http } from "viem";
import { createWalletClient, http, type Account } from "viem";
import { sepolia } from "viem/chains";

type subOrgFormData = {
Expand Down Expand Up @@ -114,7 +114,7 @@ export default function Home() {
});

const viemClient = createWalletClient({
account: viemAccount,
account: viemAccount as Account,
chain: sepolia,
transport: http(),
});
Expand Down
3 changes: 2 additions & 1 deletion examples/with-viem/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"scripts": {
"start": "pnpm -w run build-all && tsx src/index.ts",
"start-advanced": "pnpm -w run build-all && tsx src/advanced.ts",
"start-contracts": "pnpm -w run build-all && tsx src/contracts.ts",
"clean": "rimraf ./dist ./.cache",
"typecheck": "tsc --noEmit"
},
Expand All @@ -18,6 +19,6 @@
"dotenv": "^16.0.3",
"fetch": "^1.1.0",
"typescript": "5.1",
"viem": "^1.10.0"
"viem": "^1.16.6"
}
}
7 changes: 7 additions & 0 deletions examples/with-viem/src/advanced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@ import {
} from "viem";
import { sepolia } from "viem/chains";
import { print, assertEqual } from "./util";
import { createNewEthereumPrivateKey } from "./createNewEthereumPrivateKey";

// Load environment variables from `.env.local`
dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });

async function main() {
if (!process.env.PRIVATE_KEY_ID) {
// If you don't specify a `PRIVATE_KEY_ID`, we'll create one for you via calling the Turnkey API.
await createNewEthereumPrivateKey();
return;
}

const turnkeyClient = new TurnkeyClient(
{
baseUrl: process.env.BASE_URL!,
Expand Down
81 changes: 81 additions & 0 deletions examples/with-viem/src/contracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as path from "path";
import * as dotenv from "dotenv";

import { createAccount } from "@turnkey/viem";
import { TurnkeyClient } from "@turnkey/http";
import { ApiKeyStamper } from "@turnkey/api-key-stamper";
import {
createWalletClient,
createPublicClient,
http,
type Account,
} from "viem";
import { goerli } from "viem/chains";
import { print } from "./util";
import { createNewEthereumPrivateKey } from "./createNewEthereumPrivateKey";
import WETH_TOKEN_ABI from "./weth-contract-abi.json";
const WETH_TOKEN_ADDRESS_GOERLI = "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6";

// Load environment variables from `.env.local`
dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });

async function main() {
if (!process.env.PRIVATE_KEY_ID) {
// If you don't specify a `PRIVATE_KEY_ID`, we'll create one for you via calling the Turnkey API.
await createNewEthereumPrivateKey();
return;
}

const turnkeyClient = new TurnkeyClient(
{
baseUrl: process.env.BASE_URL!,
},
new ApiKeyStamper({
apiPublicKey: process.env.API_PUBLIC_KEY!,
apiPrivateKey: process.env.API_PRIVATE_KEY!,
})
);

const turnkeyAccount = await createAccount({
client: turnkeyClient,
organizationId: process.env.ORGANIZATION_ID!,
privateKeyId: process.env.PRIVATE_KEY_ID!,
});

const client = createWalletClient({
account: turnkeyAccount as Account,
chain: goerli,
transport: http(
`https://goerli.infura.io/v3/${process.env.INFURA_API_KEY!}`
),
});

const address = client.account.address;
print("Address:", address);

const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_goerli"),
chain: goerli,
});

const { request } = await publicClient.simulateContract({
abi: WETH_TOKEN_ABI,
address: WETH_TOKEN_ADDRESS_GOERLI,
functionName: "deposit",
chain: goerli,
value: 1n,
account: client.account,
});

const hash = await client.writeContract(request);

print(
"Successfully wrapped ETH 🥳. Transaction:",
`https://goerli.etherscan.io/tx/${hash}`
);
}

main().catch((error) => {
console.error(error);
process.exit(1);
});
83 changes: 83 additions & 0 deletions examples/with-viem/src/createNewEthereumPrivateKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
TurnkeyClient,
createActivityPoller,
TurnkeyActivityError,
} from "@turnkey/http";
import { ApiKeyStamper } from "@turnkey/api-key-stamper";
import * as crypto from "crypto";

export async function createNewEthereumPrivateKey() {
console.log("creating a new Ethereum private key on Turnkey...\n");

const privateKeyName = `ETH Key ${crypto.randomBytes(2).toString("hex")}`;

try {
const turnkeyClient = new TurnkeyClient(
{ baseUrl: process.env.BASE_URL! },
new ApiKeyStamper({
apiPublicKey: process.env.API_PUBLIC_KEY!,
apiPrivateKey: process.env.API_PRIVATE_KEY!,
})
);

const activityPoller = createActivityPoller({
client: turnkeyClient,
requestFn: turnkeyClient.createPrivateKeys,
});

const completedActivity = await activityPoller({
type: "ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2",
timestampMs: String(Date.now()),
organizationId: process.env.ORGANIZATION_ID!,
parameters: {
privateKeys: [
{
privateKeyName,
curve: "CURVE_SECP256K1",
addressFormats: ["ADDRESS_FORMAT_ETHEREUM"],
privateKeyTags: [],
},
],
},
});

const privateKey = refineNonNull(
completedActivity.result.createPrivateKeysResultV2?.privateKeys?.[0]
);
const privateKeyId = refineNonNull(privateKey.privateKeyId);
const address = refineNonNull(privateKey.addresses?.[0]?.address);

// Success!
console.log(
[
`New Ethereum private key created!`,
`- Name: ${privateKeyName}`,
`- Private key ID: ${privateKeyId}`,
`- Address: ${address}`,
``,
"Now you can take the private key ID, put it in `.env.local`, then re-run the script.",
].join("\n")
);
} catch (error) {
// If needed, you can read from `TurnkeyActivityError` to find out why the activity didn't succeed
if (error instanceof TurnkeyActivityError) {
throw error;
}

throw new TurnkeyActivityError({
message: "Failed to create a new Ethereum private key",
cause: error as Error,
});
}
}

export function refineNonNull<T>(
input: T | null | undefined,
errorMessage?: string
): T {
if (input == null) {
throw new Error(errorMessage ?? `Unexpected ${JSON.stringify(input)}`);
}

return input;
}
7 changes: 7 additions & 0 deletions examples/with-viem/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ import { ApiKeyStamper } from "@turnkey/api-key-stamper";
import { createWalletClient, http, recoverMessageAddress } from "viem";
import { sepolia } from "viem/chains";
import { print, assertEqual } from "./util";
import { createNewEthereumPrivateKey } from "./createNewEthereumPrivateKey";

// Load environment variables from `.env.local`
dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });

async function main() {
if (!process.env.PRIVATE_KEY_ID) {
// If you don't specify a `PRIVATE_KEY_ID`, we'll create one for you via calling the Turnkey API.
await createNewEthereumPrivateKey();
return;
}

const turnkeyClient = new TurnkeyClient(
{
baseUrl: process.env.BASE_URL!,
Expand Down
153 changes: 153 additions & 0 deletions examples/with-viem/src/weth-contract-abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "guy", "type": "address" },
{ "name": "wad", "type": "uint256" }
],
"name": "approve",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "src", "type": "address" },
{ "name": "dst", "type": "address" },
{ "name": "wad", "type": "uint256" }
],
"name": "transferFrom",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "wad", "type": "uint256" }],
"name": "withdraw",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint8" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "dst", "type": "address" },
{ "name": "wad", "type": "uint256" }
],
"name": "transfer",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "deposit",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "name": "", "type": "address" },
{ "name": "", "type": "address" }
],
"name": "allowance",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{ "payable": true, "stateMutability": "payable", "type": "fallback" },
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "src", "type": "address" },
{ "indexed": true, "name": "guy", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "src", "type": "address" },
{ "indexed": true, "name": "dst", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "dst", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "src", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Withdrawal",
"type": "event"
}
]
Loading

0 comments on commit 405b410

Please sign in to comment.