From a74704199d8c7b8741f9c17b715dc71f46db7864 Mon Sep 17 00:00:00 2001 From: Andrew Min Date: Wed, 10 Sep 2025 01:33:12 -0400 Subject: [PATCH] wip --- examples/with-viem/src/advanced.ts | 97 ++++++++++++++++++------------ packages/viem/src/index.ts | 72 ++++++++++++---------- 2 files changed, 100 insertions(+), 69 deletions(-) diff --git a/examples/with-viem/src/advanced.ts b/examples/with-viem/src/advanced.ts index 7207e755a..668a5ae41 100644 --- a/examples/with-viem/src/advanced.ts +++ b/examples/with-viem/src/advanced.ts @@ -34,7 +34,7 @@ async function main() { new ApiKeyStamper({ apiPublicKey: process.env.API_PUBLIC_KEY!, apiPrivateKey: process.env.API_PRIVATE_KEY!, - }), + }) ); const turnkeyAccount = await createAccount({ @@ -47,7 +47,7 @@ async function main() { account: turnkeyAccount as Account, chain: sepolia, transport: http( - `https://sepolia.infura.io/v3/${process.env.INFURA_API_KEY!}`, + `https://sepolia.infura.io/v3/${process.env.INFURA_API_KEY!}` ), }); @@ -83,47 +83,70 @@ async function main() { assertEqual(address, recoveredAddress); // 3. Sign typed data (EIP-712) - const domain = { - name: "Ether Mail", - version: "1", - chainId: 1, - verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", - } as const; - - // The named list of all type definitions - const types = { - Person: [ - { name: "name", type: "string" }, - { name: "wallet", type: "address" }, - ], - Mail: [ - { name: "from", type: "Person" }, - { name: "to", type: "Person" }, - { name: "contents", type: "string" }, - ], - } as const; - const typedData = { account: turnkeyAccount as Account, - domain, - types, - primaryType: "Mail", + domain: { + name: "Test", + version: "1", + chainId: 1, + verifyingContract: "0x0000000000000000000000000000000000000000", + }, + types: { + TestMessage: [{ name: "number", type: "uint256" }], + }, + primaryType: "TestMessage", message: { - from: { - name: "Cow", - wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", - }, - to: { - name: "Bob", - wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - }, - contents: "Hello, Bob!", + number: 42n, // BigInt }, - } as const; + }; + + const types = { + TransferRequest: [ + { name: "selector", type: "bytes4" }, + { name: "destination", type: "address" }, + { name: "token", type: "address" }, + { name: "nonStandardIndex", type: "uint256" }, + { name: "tokenId", type: "uint256" }, + { name: "amount", type: "uint256" }, + { name: "amounts", type: "uint256[]" }, + { name: "tokenIds", type: "uint256[]" }, + { name: "data", type: "bytes" }, + { name: "nonce", type: "uint256" }, + ], + }; + + const request = { + destination: "0x54FFabdc775e54bc852010C4AfF553183420E6bb", + token: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", + nonStandardIndex: 0n, + tokenId: 0n, + amount: 11527707n, + amounts: [], + tokenIds: [], + data: "0x", + selector: "0xc9a4324f", + nonce: 3375574816n, + }; + + const message = { + ...request + }; + + const td = { + domain: { + name: "AccountImplementation", + version: "1", + chainId: sepolia.id, + verifyingContract: "0x0000000000000000000000000000000000000000", + }, + types, + primaryType: "TransferRequest", + message, + } - signature = await client.signTypedData(typedData); + signature = await client.signTypedData(td); recoveredAddress = await recoverTypedDataAddress({ - ...typedData, + ...td, signature, }); diff --git a/packages/viem/src/index.ts b/packages/viem/src/index.ts index 2d1129d68..d381cff70 100644 --- a/packages/viem/src/index.ts +++ b/packages/viem/src/index.ts @@ -7,6 +7,7 @@ import { hexToBytes, parseTransaction, serializeTypedData, + hashTypedData, } from "viem"; import { SignAuthorizationReturnType, @@ -14,6 +15,7 @@ import { SignAuthorizationParameters, } from "viem/accounts"; import type { + HashTypedDataParameters, Hex, LocalAccount, SerializeTransactionFn, @@ -153,7 +155,7 @@ export function createAccountWithAddress(input: { serializer?: | SerializeTransactionFn | undefined; - }, + } ): Promise { const serializer: SerializeTransactionFn = options?.serializer ?? @@ -163,16 +165,16 @@ export function createAccountWithAddress(input: { transaction, serializer, organizationId, - signWith, + signWith ); }, signTypedData: function ( - typedData: TypedData | { [key: string]: unknown }, + typedData: TypedData | { [key: string]: unknown } ): Promise { return signTypedData(client, typedData, organizationId, signWith); }, signAuthorization: function ( - parameters: TSignAuthorizationParameters, + parameters: TSignAuthorizationParameters ): Promise { return signAuthorization(client, parameters, organizationId, signWith); }, @@ -210,7 +212,7 @@ export async function createAccount(input: { }); ethereumAddress = data.privateKey.addresses.find( - (item: any) => item.format === "ADDRESS_FORMAT_ETHEREUM", + (item: any) => item.format === "ADDRESS_FORMAT_ETHEREUM" )?.address; if (typeof ethereumAddress !== "string" || !ethereumAddress) { @@ -260,7 +262,7 @@ type TApiKeyAccountConfig = { * @deprecated use {@link createAccount} instead. */ export async function createApiKeyAccount( - config: TApiKeyAccountConfig, + config: TApiKeyAccountConfig ): Promise { const { apiPublicKey, apiPrivateKey, baseUrl, organizationId, privateKeyId } = config; @@ -274,7 +276,7 @@ export async function createApiKeyAccount( { baseUrl: baseUrl, }, - stamper, + stamper ); const data = await client.getPrivateKey({ @@ -283,7 +285,7 @@ export async function createApiKeyAccount( }); const ethereumAddress = data.privateKey.addresses.find( - (item: any) => item.format === "ADDRESS_FORMAT_ETHEREUM", + (item: any) => item.format === "ADDRESS_FORMAT_ETHEREUM" )?.address; if (typeof ethereumAddress !== "string" || !ethereumAddress) { @@ -313,7 +315,7 @@ export async function createApiKeyAccount( serializer?: | SerializeTransactionFn | undefined; - }, + } ): Promise { const serializer: SerializeTransactionFn = options?.serializer ?? @@ -323,22 +325,22 @@ export async function createApiKeyAccount( transaction, serializer, organizationId, - privateKeyId, + privateKeyId ); }, signTypedData: function ( - typedData: TypedData | { [key: string]: unknown }, + typedData: TypedData | { [key: string]: unknown } ): Promise { return signTypedData(client, typedData, organizationId, privateKeyId); }, signAuthorization: function ( - parameters: TSignAuthorizationParameters, + parameters: TSignAuthorizationParameters ): Promise { return signAuthorization( client, parameters, organizationId, - privateKeyId, + privateKeyId ); }, }); @@ -348,7 +350,7 @@ export async function signAuthorization( client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient, parameters: TSignAuthorizationParameters, organizationId: string, - signWith: string, + signWith: string ): Promise { const { chainId, nonce, to = "object" } = parameters; const address = parameters.contractAddress ?? parameters.address; @@ -369,7 +371,7 @@ export async function signAuthorization( organizationId, signWith, "PAYLOAD_ENCODING_EIP7702_AUTHORIZATION", - to, + to ); if (to === "object") @@ -388,13 +390,13 @@ export async function signMessage( client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient, message: SignableMessage, organizationId: string, - signWith: string, + signWith: string ): Promise { const signedMessage = await signMessageWithErrorWrapping( client, message as Hex, organizationId, - signWith, + signWith ); return `${signedMessage}` as Hex; } @@ -406,7 +408,7 @@ export async function signTransaction< transaction: TTransactionSerializable, serializer: SerializeTransactionFn, organizationId: string, - signWith: string, + signWith: string ): Promise { // Note: for Type 3 transactions, we are specifically handling parsing for payloads containing only the transaction payload body, without any wrappers around blobs, commitments, or proofs. // See more: https://github.com/wevm/viem/blob/3ef19eac4963014fb20124d1e46d1715bed5509f/src/accounts/utils/signTransaction.ts#L54-L55 @@ -421,7 +423,7 @@ export async function signTransaction< client, nonHexPrefixedSerializedTx, organizationId, - signWith, + signWith ); if (transaction.type === "eip4844") { @@ -443,15 +445,21 @@ export async function signTypedData( client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient, data: TypedData | { [key: string]: unknown }, organizationId: string, - signWith: string, + signWith: string ): Promise { + const hashToSign = hashTypedData(data as HashTypedDataParameters); + + console.log("hash to sign", hashToSign); + return (await signMessageWithErrorWrapping( client, - serializeTypedData(data as SignTypedDataParameters), + // serializeTypedData(data as SignTypedDataParameters), + hashToSign, organizationId, signWith, - "PAYLOAD_ENCODING_EIP712", - "hex", + // "PAYLOAD_ENCODING_EIP712", + "PAYLOAD_ENCODING_HEXADECIMAL", + "hex" )) as Hex; } @@ -459,7 +467,7 @@ async function signTransactionWithErrorWrapping( client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient, unsignedTransaction: string, organizationId: string, - signWith: string, + signWith: string ): Promise { let signedTx: string; try { @@ -467,7 +475,7 @@ async function signTransactionWithErrorWrapping( client, unsignedTransaction, organizationId, - signWith, + signWith ); } catch (error: any) { // Wrap Turnkey error in Viem-specific error @@ -499,7 +507,7 @@ async function signTransactionImpl( client: TurnkeyClient | TurnkeyBrowserClient | TurnkeyServerClient, unsignedTransaction: string, organizationId: string, - signWith: string, + signWith: string ): Promise { if (isHttpClient(client)) { const { activity } = await client.signTransaction({ @@ -516,7 +524,7 @@ async function signTransactionImpl( assertActivityCompleted(activity); return assertNonNull( - activity?.result?.signTransactionResult?.signedTransaction, + activity?.result?.signTransactionResult?.signedTransaction ); } else { const { activity, signedTransaction } = await client.signTransaction({ @@ -538,7 +546,7 @@ async function signMessageWithErrorWrapping( organizationId: string, signWith: string, payloadEncoding: TPayloadEncoding = "PAYLOAD_ENCODING_HEXADECIMAL", - to: TSignatureFormat = "hex", + to: TSignatureFormat = "hex" ): Promise { let signedMessage: TSignMessageResult; @@ -549,7 +557,7 @@ async function signMessageWithErrorWrapping( organizationId, signWith, payloadEncoding, - to, + to ); } catch (error: any) { // Wrap Turnkey error in Viem-specific error @@ -583,7 +591,7 @@ async function signMessageImpl( organizationId: string, signWith: string, payloadEncoding: TPayloadEncoding, - to: TSignatureFormat, + to: TSignatureFormat ): Promise { let result: TSignature; @@ -636,7 +644,7 @@ async function signMessageImpl( // https://github.com/wevm/viem/blob/c8378d22f692f48edde100693159874702f36330/src/utils/signature/serializeSignature.ts#L38-L39 export function serializeSignature( sig: TSignature, - to: TSignatureFormat = "hex", + to: TSignatureFormat = "hex" ) { const { r: rString, s: sString, v: vString } = sig; @@ -649,7 +657,7 @@ export function serializeSignature( const signature = `0x${new secp256k1.Signature( hexToBigInt(r), - hexToBigInt(s), + hexToBigInt(s) ).toCompactHex()}${yParity_ === 0n ? "1b" : "1c"}` as const; if (to === "hex") return signature;