Skip to content

Commit

Permalink
Merge pull request #53 from zerodevapp/refactor/turnkey-from-ethers-t…
Browse files Browse the repository at this point in the history
…o-viem

refactor: turnkey from ethers to viem
  • Loading branch information
blakecduncan authored Oct 26, 2023
2 parents c9842b6 + b951a24 commit 271729a
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
cache: "yarn"

- name: Install SDK dependencies
run: yarn
run: yarn install --frozen-lockfile

- name: Build SDK
run: yarn build
Expand Down
17 changes: 10 additions & 7 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: "16.20"
node-version: "18.13"
cache: "yarn"

- name: Install dependencies
Expand All @@ -32,25 +32,28 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: "16.20"
node-version: "18.13"
cache: "yarn"

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Lint
run: yarn lint:check

- name: Build
- name: Build SDK
run: yarn build

- name: Link SDK
run: yarn --cwd packages/accounts link

- name: Install E2E tests
run: yarn --cwd e2e-tests link @zerodev/sdk && yarn --cwd e2e-tests

- name: E2E tests
env:
API_URL: ${{ secrets.API_URL }}
API_KEY: ${{ secrets.API_KEY }}
TEAM_ID: ${{ secrets.TEAM_ID }}
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
run: yarn test:e2e
run: yarn --cwd e2e-tests test:e2e

# - name: Unit Test
# run: yarn test
6 changes: 4 additions & 2 deletions packages/accounts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zerodev/sdk",
"version": "4.0.31",
"version": "4.0.32",
"description": "A collection of ERC-4337 compliant smart contract account interfaces",
"author": "ZeroDev",
"license": "MIT",
Expand Down Expand Up @@ -44,7 +44,9 @@
"test:run": "vitest run"
},
"devDependencies": {
"@turnkey/ethers": "^0.16.5",
"@turnkey/api-key-stamper": "^0.1.1",
"@turnkey/http": "^1.2.0",
"@turnkey/viem": "^0.2.4",
"typescript": "^5.0.4",
"typescript-template": "*",
"vitest": "^0.31.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const createZeroDevPublicErc4337Client = ({
}: ZeroDevClientConfig): PublicErc4337Client<HttpTransport> => {
const erc4337Transport = http(rpcUrl, {
fetchOptions: {
// @ts-ignore
headers: rpcUrl === BUNDLER_URL ? { projectId, bundlerProvider } : {},
},
name: "Connected bundler network",
Expand Down
130 changes: 94 additions & 36 deletions packages/accounts/src/kernel-zerodev/owner/getCustodialOwner.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import type { SmartAccountSigner } from "@alchemy/aa-core";
import type { SignTypedDataParams, SmartAccountSigner } from "@alchemy/aa-core";
import { API_URL } from "../constants.js";
import axios from "axios";
import type { Hex } from "viem";
import type { SignTypedDataParams } from "@alchemy/aa-core";
import { TurnkeyClient } from "@turnkey/http";

/**
* Params to get a custodial owner
* Priv / pub key combo or custodial file path should be in different types
*/
type GetCustodialOwnerParams = {
// ZeroDev api url
apiUrl?: string;
// Turnkey client (if the client want to reuse it
turnKeyClient?: TurnkeyClient;

// Direct access to priv / pub key
privateKey?: string;
publicKey?: string;
keyId?: string;

// Or read them from the custodial file path
custodialFilePath?: string;
};

/**
* Returns a signer for a custodial wallet via TurnKey
*/
export async function getCustodialOwner(
identifier: string,
{
Expand All @@ -12,14 +33,10 @@ export async function getCustodialOwner(
publicKey,
keyId,
apiUrl = API_URL,
}: {
privateKey?: string;
publicKey?: string;
keyId?: string;
custodialFilePath?: string;
apiUrl?: string;
}
turnKeyClient,
}: GetCustodialOwnerParams
): Promise<SmartAccountSigner | undefined> {
// Extract data from the custodial file path if provided
if (custodialFilePath) {
let fsModule;
try {
Expand All @@ -30,45 +47,86 @@ export async function getCustodialOwner(
}
const data = fsModule.readFileSync(custodialFilePath, "utf8");
const values = data.split("\n");
// Override the values, if they are provided, to the one from the custodial file
[privateKey, publicKey, keyId] = values;
}
let TurnkeySigner;
try {
TurnkeySigner =
require.resolve("@turnkey/ethers") &&
require("@turnkey/ethers").TurnkeySigner;
} catch (error) {
console.log(
"@turnkey/ethers module not available. Skipping FS operation..."
);
return;
}

// Ensure we have the required values
if (!privateKey || !publicKey || !keyId) {
throw new Error(
"Must provide custodialFilePath or privateKey, publicKey, and keyId."
);
}

const response = await axios.post(`${apiUrl}/wallets/${identifier}`, {
keyId,
});
// Get our turnkey client (build it if needed)
if (!turnKeyClient) {
// Try to fetch turnkey client & api key stamper
let TurnkeyClient;
let ApiKeyStamper;
// TODO: Would be cleaner with something like radash and a tryit function?
try {
TurnkeyClient =
require.resolve("@turnkey/http") &&
require("@turnkey/http").TurnkeyClient;
ApiKeyStamper =
require.resolve("@turnkey/api-key-stamper") &&
require("@turnkey/api-key-stamper").ApiKeyStamper;
} catch (error) {
console.log(
"@turnkey/http or @turnkey/api-key-stamper module not available. Skipping FS operation..."
);
return;
}

const turnkeySigner = new TurnkeySigner({
apiPublicKey: publicKey,
apiPrivateKey: privateKey,
baseUrl: "https://api.turnkey.com",
// Build the turnkey client
turnKeyClient = new TurnkeyClient(
{
baseUrl: "https://api.turnkey.com",
},
new ApiKeyStamper({
apiPublicKey: publicKey,
apiPrivateKey: privateKey,
})
);
}

// Get the wallet identifier from the API
const response = await axios.post<any, { data: { walletId: string } }>(
`${apiUrl}/wallets/${identifier}`,
{
keyId,
}
);

// Build the turnkey viem account
let createAccount;
try {
createAccount =
require.resolve("@turnkey/viem") &&
require("@turnkey/viem").createAccount;
} catch (error) {
console.log("@turnkey/viem module not available. Skipping FS operation...");
return;
}
const turnkeySigner = await createAccount({
client: turnKeyClient,
organizationId: keyId,
privateKeyId: response.data.walletId,
});

// Return an alchemy AA signer from the turnkey signer
return {
getAddress: async () => (await turnkeySigner.getAddress()) as `0x${string}`,
signMessage: async (msg: Uint8Array | string) =>
(await turnkeySigner.signMessage(msg)) as `0x${string}`,
getAddress: async () => turnkeySigner.address,
signMessage: async (msg: Uint8Array | string) => {
if (typeof msg === "string") {
// If the msg is a string, sign it directly
return turnkeySigner.signMessage({ message: msg });
} else {
// Otherwise, sign the raw data
return turnkeySigner.signMessage({ message: { raw: msg } });
}
},
signTypedData: async (params: SignTypedDataParams) =>
(await turnkeySigner.signTypedData(
params.domain,
params.types,
params.message
)) as Hex,
turnkeySigner.signTypedData(params),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export async function createPasskeyOwner({
fallback?: () => Promise<SmartAccountSigner | undefined>;
apiUrl?: string;
}): Promise<SmartAccountSigner | undefined> {
//@ts-expect-error
if (typeof window !== "undefined") {
const challenge = generateRandomBuffer();
let credentials = undefined;
Expand All @@ -43,9 +42,7 @@ export async function createPasskeyOwner({
const attestation = await getWebAuthnAttestation({
publicKey: {
rp: {
//@ts-expect-error
id: window.location.hostname,
//@ts-expect-error
name: window.location.hostname,
},
authenticatorSelection: {
Expand Down Expand Up @@ -100,7 +97,6 @@ export async function createPasskeyOwner({
};
return owner;
} catch (e) {
//@ts-expect-error
if (e instanceof DOMException && e.name === "InvalidStateError") {
if (fallback) return await fallback();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export async function getAutocompletePasskeyOwner({
return;
}

//@ts-expect-error
if (typeof window !== "undefined") {
const challenge = generateRandomBuffer();
try {
Expand All @@ -37,7 +36,6 @@ export async function getAutocompletePasskeyOwner({
await getWebAuthnAssertion(base64UrlEncode(challenge), {
mediation: "conditional",
publicKey: {
//@ts-expect-error
rpId: window.location.hostname,
userVerification: "required",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ export async function getPasskeyOwner({
apiUrl?: string;
withCredentials?: boolean;
}): Promise<SmartAccountSigner | undefined> {
//@ts-expect-error
if (typeof window !== "undefined") {
const challenge = generateRandomBuffer();
try {
abortWebauthn();
const assertion = JSON.parse(
await getWebAuthnAssertion(base64UrlEncode(challenge), {
publicKey: {
//@ts-expect-error
rpId: window.location.hostname,
userVerification: "required",
allowCredentials: withCredentials
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const es256 = -7;

export const generateRandomBuffer = (): ArrayBuffer => {
const arr = new Uint8Array(32);
//@ts-expect-error
crypto.getRandomValues(arr);
return arr.buffer;
};
Expand Down
57 changes: 46 additions & 11 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3917,21 +3917,37 @@
"@tufjs/canonical-json" "1.0.0"
minimatch "^9.0.0"

"@turnkey/ethers@^0.16.5":
version "0.16.5"
resolved "https://registry.yarnpkg.com/@turnkey/ethers/-/ethers-0.16.5.tgz#1ec887d64fcae87f67863a27587f31b044b15421"
integrity sha512-g33kCZowrh62qUCCvcG4EsDuv86sOaY0oyyeu7B39DS9xq2Oj8YDNPVh9JefKRgQaSbAmt3INz768FNAEJDCDA==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
"@turnkey/http" "1.0.1"
"@turnkey/[email protected]", "@turnkey/api-key-stamper@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@turnkey/api-key-stamper/-/api-key-stamper-0.1.1.tgz#7a5b5cd9cee7bd05059b87ccf66bfc264dbbd8a0"
integrity sha512-qs8fmWFiAhh7fAByx7j9/2F0VRe/qD7Yq3hi4FwD+jLU0CXdE+aim1cmJpAFFPKF1PBmPs6jh4XBODE40eqEEg==

"@turnkey/http@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@turnkey/http/-/http-1.0.1.tgz#21aadcbbe67c60278ce187bbe6642ffa614e7779"
integrity sha512-Tp2SKHeZa03HYMnXDFfAUw1akS+VBwTKOqKe2GOusLVQAcOXMhLIXCjipRkAfYynD/kDXPkr8sEVhDXxdSejsw==
"@turnkey/http@1.3.0", "@turnkey/http@^1.2.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@turnkey/http/-/http-1.3.0.tgz#c21389d0997a305888911b345fdcf44f16dd7878"
integrity sha512-vl/z3GSRY5ZvjHvb7gpfyq8HXj630yYViHqtstTRtGCv1QaNxgsDzZ/ECkN8fWiJJj/4SUiiKw6jSeHL0jTAew==
dependencies:
"@turnkey/api-key-stamper" "0.1.1"
"@turnkey/webauthn-stamper" "0.2.0"
cross-fetch "^3.1.5"

"@turnkey/viem@^0.2.4":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@turnkey/viem/-/viem-0.2.5.tgz#a4059d4cd43cbb80330cb7b78330ea7a58d42db7"
integrity sha512-HdXI0oi/7MfEEHDtIUjjG27/WotZBVLb8NzmqKzSnyiDvBC0UX4toba6pobKngxtehg/5DznJW961d7CRAx2Gw==
dependencies:
"@turnkey/api-key-stamper" "0.1.1"
"@turnkey/http" "1.3.0"
cross-fetch "^4.0.0"
typescript "5.1"

"@turnkey/[email protected]":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@turnkey/webauthn-stamper/-/webauthn-stamper-0.2.0.tgz#aa546391ee59cf6c5d4201d74e038adb261765d2"
integrity sha512-BoHucuoTTmvqJzqSZs6XsLHSu1rwelSHUFBDlUfYX/QzK9BpkaxMxamOxk92OLpRb6qRUngrCQeS/jPHlWgQow==
dependencies:
buffer "^6.0.3"

"@types/bn.js@^5.1.0":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682"
Expand Down Expand Up @@ -5981,6 +5997,13 @@ cross-fetch@^3.1.4, cross-fetch@^3.1.5:
dependencies:
node-fetch "^2.6.11"

cross-fetch@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983"
integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==
dependencies:
node-fetch "^2.6.12"

cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
Expand Down Expand Up @@ -9385,6 +9408,13 @@ node-fetch@^2.6.11, node-fetch@^2.6.7:
dependencies:
whatwg-url "^5.0.0"

node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"

node-fetch@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e"
Expand Down Expand Up @@ -11800,6 +11830,11 @@ typescript-template@*:
dependencies:
sitka "^1.0.6"

[email protected]:
version "5.1.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==

"typescript@^3 || ^4":
version "4.9.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
Expand Down

0 comments on commit 271729a

Please sign in to comment.