From 16f9f167e0a04faa5f70798912f64c81df4d0328 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Fri, 31 May 2024 12:30:11 -0700 Subject: [PATCH 01/10] hacky impl: eip712 signature for CCR --- src/chains/suave/serializers.ts | 5 +- src/chains/suave/wallet.ts | 182 +++++++++++++++++++++----------- 2 files changed, 125 insertions(+), 62 deletions(-) diff --git a/src/chains/suave/serializers.ts b/src/chains/suave/serializers.ts index 3d5f42ef..2409c4eb 100644 --- a/src/chains/suave/serializers.ts +++ b/src/chains/suave/serializers.ts @@ -1,7 +1,7 @@ import { InvalidSerializedTransactionTypeError } from '../../index.js' import type { Hex } from '../../types/misc.js' import { concatHex } from '../../utils/data/concat.js' -import { numberToHex, toHex } from '../../utils/encoding/toHex.js' +import { boolToHex, numberToHex, toHex } from '../../utils/encoding/toHex.js' import { toRlp } from '../../utils/encoding/toRlp.js' import { InvalidConfidentialRecordError, @@ -166,6 +166,9 @@ export const serializeConfidentialComputeRequest = ( transaction.kettleAddress, transaction.confidentialInputsHash, + // envelope + boolToHex(true), // TODO: add to params + numberToHex(transaction.chainId), toHex(transaction.v), transaction.r, diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index adb51329..34de5aaa 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -1,29 +1,31 @@ +// import { signTypedData } from '~viem/accounts/index.js' import { privateKeyToAccount } from '../../accounts/privateKeyToAccount.js' -import { sign } from '../../accounts/utils/sign.js' +// import { sign } from '../../accounts/utils/sign.js' import { http, type JsonRpcAccount, type PrivateKeyAccount, type PublicClient, type Transport, - type TransportConfig, + // type TransportConfig, type WalletClient, createPublicClient, createWalletClient, hexToSignature, keccak256, + // zeroAddress, } from '../../index.js' import { type Hex } from '../../types/misc.js' import { suaveRigil } from '../index.js' import { - serializeConfidentialComputeRecord, + // serializeConfidentialComputeRecord, serializeConfidentialComputeRequest, } from './serializers.js' import { SuaveTxRequestTypes, SuaveTxTypes, type TransactionRequestSuave, - type TransactionSerializableSuave, + // type TransactionSerializableSuave, } from './types.js' /// client types @@ -51,52 +53,52 @@ function formatSignature(signature: { } } -async function signConfidentialComputeRecord( - transaction: TransactionSerializableSuave, - privateKey: Hex, -): Promise { - if (transaction.type !== SuaveTxTypes.ConfidentialRecord) { - throw new Error( - `transaction.type must be ConfidentialRecord (${SuaveTxTypes.ConfidentialRecord})`, - ) - } - const serialized = serializeConfidentialComputeRecord(transaction) - const signature = await sign({ hash: keccak256(serialized), privateKey }) - return { - ...transaction, - ...formatSignature(signature), - } -} +// async function signConfidentialComputeRecord( +// transaction: TransactionSerializableSuave, +// privateKey: Hex, +// ): Promise { +// if (transaction.type !== SuaveTxTypes.ConfidentialRecord) { +// throw new Error( +// `transaction.type must be ConfidentialRecord (${SuaveTxTypes.ConfidentialRecord})`, +// ) +// } +// const serialized = serializeConfidentialComputeRecord(transaction) +// const signature = await sign({ hash: keccak256(serialized), privateKey }) +// return { +// ...transaction, +// ...formatSignature(signature), +// } +// } -function getSigningMethod( - transport: TTransport, - privateKey?: Hex, - address?: Hex, -) { - if (transport.type === 'custom') { - if (!address) { - throw new Error("param 'address' is required for custom transports") - } - return async (txRequest: TransactionSerializableSuave) => { - const rawSignature: Hex = await transport.request({ - method: 'eth_sign', - params: [ - address, - keccak256(serializeConfidentialComputeRecord(txRequest)), - ], - }) - const parsedSignature = hexToSignature(rawSignature) - return formatSignature(parsedSignature) - } - } else { - if (!privateKey) { - throw new Error('privateKey is required for non-custom transports') - } - return async (txRequest: TransactionSerializableSuave) => { - return await signConfidentialComputeRecord(txRequest, privateKey) - } - } -} +// function getSigningMethod( +// transport: TTransport, +// privateKey?: Hex, +// address?: Hex, +// ) { +// if (transport.type === 'custom') { +// if (!address) { +// throw new Error("param 'address' is required for custom transports") +// } +// return async (txRequest: TransactionSerializableSuave) => { +// const rawSignature: Hex = await transport.request({ +// method: 'eth_sign', +// params: [ +// address, +// keccak256(serializeConfidentialComputeRecord(txRequest)), +// ], +// }) +// const parsedSignature = hexToSignature(rawSignature) +// return formatSignature(parsedSignature) +// } +// } else { +// if (!privateKey) { +// throw new Error('privateKey is required for non-custom transports') +// } +// return async (txRequest: TransactionSerializableSuave) => { +// return await signConfidentialComputeRecord(txRequest, privateKey) +// } +// } +// } /** Get a SUAVE-enabled viem wallet. * @@ -137,7 +139,10 @@ export function getSuaveWallet(params: { }) } -async function prepareTx(client: any, txRequest: TransactionRequestSuave) { +async function prepareTx( + client: ReturnType, + txRequest: TransactionRequestSuave, +) { const preparedTx = await client.prepareTransactionRequest(txRequest) const payload: TransactionRequestSuave = { ...txRequest, @@ -183,24 +188,79 @@ function newSuaveWallet(params: { async signTransaction(txRequest: TransactionRequestSuave) { if (txRequest.type === SuaveTxRequestTypes.ConfidentialRequest) { const confidentialInputs = txRequest.confidentialInputs ?? '0x' - // determine signing method based on transport type - const signCcr = getSigningMethod( - client.transport, - params.privateKey, - client.account.address, - ) + // get nonce, gas price, etc const ctxParams = prepareTx(client, txRequest) + // dev note: calling (await ...) inline lets us skip the RPC request if teh data is not needed + const nonce = txRequest.nonce ?? (await ctxParams).nonce + const value = txRequest.value ?? 0n + const gas = txRequest.gas ?? (await ctxParams).gas + const gasPrice = txRequest.gasPrice ?? (await ctxParams).gasPrice + const chainId = txRequest.chainId ?? suaveRigil.id + // prepare and sign confidential compute request - const presignTx = { + if (!txRequest.to) { + throw new Error('missing `to`') + } + if (nonce === undefined) { + throw new Error('missing `nonce`') + } + if (gas === undefined) { + throw new Error('missing `gas`') + } + if (gasPrice === undefined) { + throw new Error('missing `gasPrice`') + } + if (!txRequest.kettleAddress) { + throw new Error('missing `kettleAddress`') + } + const eip712Tx = { ...txRequest, - nonce: txRequest.nonce ?? (await ctxParams).nonce, + nonce: BigInt(nonce), type: SuaveTxTypes.ConfidentialRecord, + chainId: BigInt(chainId), + to: txRequest.to, + value, + gas, + gasPrice, + data: txRequest.data ?? '0x', + kettleAddress: txRequest.kettleAddress, confidentialInputsHash: keccak256(confidentialInputs), - chainId: txRequest.chainId ?? suaveRigil.id, } - const sig = await signCcr(presignTx) - const { r, s, v } = sig + + const presignTx = { + ...eip712Tx, + nonce, + chainId, + envelope: true, + } + + const rawSig = await client.signTypedData({ + primaryType: 'ConfidentialRecord', + message: eip712Tx, + types: { + Eip712Domain: [ + { name: 'name', type: 'string' }, + // { name: 'chainId', type: 'uint256' }, + ], + ConfidentialRecord: [ + { name: 'nonce', type: 'uint64' }, + { name: 'gasPrice', type: 'uint256' }, + { name: 'gas', type: 'uint64' }, + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + { name: 'kettleAddress', type: 'address' }, + { name: 'confidentialInputsHash', type: 'bytes32' }, + ], + }, + domain: { + name: 'ConfidentialRecord', + // chainId: chainId, + }, + }) + const sig = hexToSignature(rawSig) + const { r, s, v } = formatSignature(sig) return serializeConfidentialComputeRequest({ ...presignTx, confidentialInputs, From c6549a6182375c7882c539e717ca4b558718ac5f Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Fri, 31 May 2024 16:52:39 -0700 Subject: [PATCH 02/10] use verifyingAddress in eip712 domain --- src/chains/suave/wallet.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index 34de5aaa..b58d9db6 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -241,7 +241,7 @@ function newSuaveWallet(params: { types: { Eip712Domain: [ { name: 'name', type: 'string' }, - // { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, ], ConfidentialRecord: [ { name: 'nonce', type: 'uint64' }, @@ -256,7 +256,7 @@ function newSuaveWallet(params: { }, domain: { name: 'ConfidentialRecord', - // chainId: chainId, + verifyingContract: presignTx.kettleAddress, }, }) const sig = hexToSignature(rawSig) From b9f161942372c75115421af16f36b0eafdcaa3f4 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:38:05 -0700 Subject: [PATCH 03/10] full support for EIP712 CCRs alongside legacy CCR & standard eth txs --- bun.lockb | Bin 572880 -> 572880 bytes examples/suave/bids.ts | 3 +- examples/suave/index.ts | 6 +- src/chains/suave/parsers.ts | 11 +- src/chains/suave/serializers.ts | 2 +- src/chains/suave/types.ts | 40 +++---- src/chains/suave/wallet.ts | 200 ++++++++++++++++++-------------- 7 files changed, 145 insertions(+), 117 deletions(-) diff --git a/bun.lockb b/bun.lockb index 11c0ce2e33c79b020bc6aa4bbb614e9113972682..0f3fb9c20aaf4cce0010c947783051be483e184c 100755 GIT binary patch delta 9809 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 delta 9808 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 diff --git a/examples/suave/bids.ts b/examples/suave/bids.ts index c1969838..e35f2414 100644 --- a/examples/suave/bids.ts +++ b/examples/suave/bids.ts @@ -56,13 +56,14 @@ export class OFAOrder { } /** Encodes this bid as a ConfidentialComputeRequest, which can be sent to SUAVE. */ - toConfidentialRequest(): TransactionRequestSuave { + toConfidentialRequest(isEIP712?: boolean): TransactionRequestSuave { return { to: this.OFAContract, data: this.newOrderCalldata(), type: '0x43', gas: 500000n, gasPrice: 1000000000n, + isEIP712, kettleAddress: this.kettle, confidentialInputs: this.confidentialInputsBytes(), } diff --git a/examples/suave/index.ts b/examples/suave/index.ts index 49e3f4ea..cd228fbb 100644 --- a/examples/suave/index.ts +++ b/examples/suave/index.ts @@ -3,7 +3,7 @@ import { http, Address, Hex, createPublicClient, formatEther, isHex } from 'viem import { goerli } from 'viem/chains' import { TransactionRequestSuave } from 'viem/chains/suave/types' import { OFAOrder } from './bids' -import { SuaveProvider, SuaveWallet, getSuaveProvider, getSuaveWallet } from 'viem/chains/utils' +import { SuaveProvider, SuaveWallet, getSuaveProvider, getSuaveWallet, parseTransactionSuave } from 'viem/chains/utils' import { HttpTransport } from 'viem' import BidContractDeployment from './deployedAddress.json' import { parseSignedComputeRequest } from 'viem/chains/suave/parsers' @@ -126,12 +126,12 @@ async function testSuaveBids() { KETTLE_ADDRESS, BID_CONTRACT_ADDRESS, ) - const ccr = bid.toConfidentialRequest() + const ccr = bid.toConfidentialRequest() // signs w/ EIP712 by default; pass `false` to use legacy CCR console.log('ccr', ccr) const signedCcr = await wallet.signTransaction(ccr) - const deserCcr = await parseSignedComputeRequest(signedCcr) console.log("signedCcr", signedCcr) + const deserCcr = await parseTransactionSuave(signedCcr) console.log("deserialized signed ccr", deserCcr) // deserCcr should be the same as ccr, but with any missing fields filled in, such as gasPrice & nonce diff --git a/src/chains/suave/parsers.ts b/src/chains/suave/parsers.ts index 3f79350a..547df5f8 100644 --- a/src/chains/suave/parsers.ts +++ b/src/chains/suave/parsers.ts @@ -25,7 +25,7 @@ const safeHexToNumber = (hex: Hex) => { return hexToNumber(hex) } -export const parseSignedComputeRequest = (signedComputeRequest: Hex) => { +export const parseSignedComputeRequest = (signedComputeRequest: Hex): Partial => { const serializedType = signedComputeRequest.slice(0, 4) if (serializedType !== SuaveTxRequestTypes.ConfidentialRequest) { throw new InvalidSerializedTransactionTypeError({ @@ -43,6 +43,7 @@ export const parseSignedComputeRequest = (signedComputeRequest: Hex) => { data, kettleAddress, confidentialInputsHash, + isEIP712, chainId, v, r, @@ -50,7 +51,7 @@ export const parseSignedComputeRequest = (signedComputeRequest: Hex) => { ], confidentialInputs, ] = txArray - if (txArray.length !== 2 || txArray[0].length !== 12) { + if (txArray.length !== 2 || txArray[0].length !== 13) { throw new InvalidSerializedTransactionError({ attributes: { nonce, @@ -59,6 +60,7 @@ export const parseSignedComputeRequest = (signedComputeRequest: Hex) => { gas, kettleAddress, confidentialInputsHash, + isEIP712, value, gasPrice, chainId, @@ -78,6 +80,7 @@ export const parseSignedComputeRequest = (signedComputeRequest: Hex) => { gas: hexToBigInt(gas as Hex), kettleAddress: kettleAddress as Hex, confidentialInputs: confidentialInputs as Hex, + isEIP712: isEIP712 === '0x01', value: safeHexToBigInt(value as Hex), gasPrice: safeHexToBigInt(gasPrice as Hex), chainId: hexToNumber(chainId as Hex), @@ -100,7 +103,7 @@ export type ParseTransactionSuaveReturnType = /** Parse a serialized transaction into a SUAVE Transaction object. */ export function parseTransactionSuave( - serializedTransaction: TransactionSerializedSuave, + serializedTransaction: TransactionSerializedSuave | Hex, ): ParseTransactionSuaveReturnType { const serializedType = serializedTransaction.slice(0, 4) const parsedTx = @@ -130,12 +133,14 @@ export function assertTransactionSuave( maxFeePerGas, confidentialInputs, confidentialInputsHash, + isEIP712, kettleAddress, to, r, s, v, } = transaction + if (isEIP712 === undefined) throw new Error('isEIP712 is required') if (chainId && chainId <= 0) throw new Error('invalid chain ID') if (to && !isAddress(to)) throw new Error('invalid to address') if (!gasPrice) throw new Error('gasPrice is required') diff --git a/src/chains/suave/serializers.ts b/src/chains/suave/serializers.ts index 2409c4eb..df93a1bb 100644 --- a/src/chains/suave/serializers.ts +++ b/src/chains/suave/serializers.ts @@ -167,7 +167,7 @@ export const serializeConfidentialComputeRequest = ( transaction.confidentialInputsHash, // envelope - boolToHex(true), // TODO: add to params + boolToHex(transaction.isEIP712 ?? true), numberToHex(transaction.chainId), toHex(transaction.v), diff --git a/src/chains/suave/types.ts b/src/chains/suave/types.ts index 53181ae7..93afc419 100644 --- a/src/chains/suave/types.ts +++ b/src/chains/suave/types.ts @@ -37,7 +37,8 @@ export type SuaveTxRequestType = `${(typeof SuaveTxRequestTypes)[keyof typeof SuaveTxRequestTypes]}` type ConfidentialOverrides = { - kettleAddress?: Address + kettleAddress?: Address, + isEIP712?: boolean, } type ConfidentialComputeRequestOverrides = ConfidentialOverrides & { @@ -45,7 +46,7 @@ type ConfidentialComputeRequestOverrides = ConfidentialOverrides & { } type ConfidentialComputeRecordOverrides = ConfidentialOverrides & { - confidentialInputsHash?: Hash + confidentialInputsHash?: Hash, } export type SuaveBlockOverrides = {} // Add any specific block overrides if necessary for Suave @@ -136,24 +137,14 @@ export type ConfidentialComputeRecord< TQuantity = bigint, TIndex = number, > = Omit< - Omit< - Omit< - Omit< - TransactionBase< - TQuantity, - TIndex, - SuaveTxTypes.ConfidentialRecord, - TPending - >, - 'blockHash' - >, - 'transactionIndex' - >, - 'blockNumber' + TransactionBase< + TQuantity, + TIndex, + SuaveTxTypes.ConfidentialRecord, + TPending >, - 'from' -> & - ConfidentialComputeRecordOverrides + 'blockHash' | 'transactionIndex' | 'blockNumber' | 'from' +> & ConfidentialComputeRecordOverrides export type ConfidentialComputeRecordRpc = ConfidentialComputeRecord @@ -164,9 +155,17 @@ export type TransactionRequestSuave< TType extends SuaveTxRequestType = SuaveTxRequestType, > = TransactionRequestBase & ConfidentialComputeRequestOverrides & { - from?: Address + from?: Address, } +export type PreparedConfidentialRecord = Omit & { + data: Hex, + to: Address, + gasPrice: bigint, + kettleAddress: Address, + confidentialInputsHash: Hash, +} + export type RpcTransactionRequestSuave = TransactionRequestSuave & { type: TType @@ -195,7 +194,6 @@ export type TransactionSerializableSuave< > = TransactionSerializableEIP2930 & ConfidentialComputeRecordOverrides & ConfidentialComputeRequestOverrides & { - signedComputeRecord?: Hex type: TType } diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index 6bd18b70..ba016d2b 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -1,31 +1,32 @@ -// import { signTypedData } from '~viem/accounts/index.js' +import { sign } from '../../accounts/index.js' import { privateKeyToAccount } from '../../accounts/privateKeyToAccount.js' -// import { sign } from '../../accounts/utils/sign.js' import { http, type JsonRpcAccount, type PrivateKeyAccount, type PublicClient, type Transport, - // type TransportConfig, type WalletClient, createPublicClient, createWalletClient, hexToSignature, keccak256, - // zeroAddress, + TransportConfig, + TransactionType, } from '../../index.js' import type { Hex } from '../../types/misc.js' import { suaveRigil } from '../index.js' import { - // serializeConfidentialComputeRecord, + serializeConfidentialComputeRecord, serializeConfidentialComputeRequest, } from './serializers.js' import { SuaveTxRequestTypes, SuaveTxTypes, type TransactionRequestSuave, - // type TransactionSerializableSuave, + TransactionSerializableSuave, + PreparedConfidentialRecord, + SuaveTxType, } from './types.js' /// client types @@ -53,52 +54,59 @@ function formatSignature(signature: { } } -// async function signConfidentialComputeRecord( -// transaction: TransactionSerializableSuave, -// privateKey: Hex, -// ): Promise { -// if (transaction.type !== SuaveTxTypes.ConfidentialRecord) { -// throw new Error( -// `transaction.type must be ConfidentialRecord (${SuaveTxTypes.ConfidentialRecord})`, -// ) -// } -// const serialized = serializeConfidentialComputeRecord(transaction) -// const signature = await sign({ hash: keccak256(serialized), privateKey }) -// return { -// ...transaction, -// ...formatSignature(signature), -// } -// } +/** Sign a CCR with a private key. */ +async function signConfidentialComputeRecord( + transaction: TransactionSerializableSuave, + privateKey: Hex, +): Promise { + if (transaction.type !== SuaveTxTypes.ConfidentialRecord) { + throw new Error( + `transaction.type must be ConfidentialRecord (${SuaveTxTypes.ConfidentialRecord})`, + ) + } + const serialized = serializeConfidentialComputeRecord(transaction) + const signature = await sign({ hash: keccak256(serialized), privateKey }) + return { + ...transaction, + ...formatSignature(signature), + } +} -// function getSigningFunction( -// transport: TTransport, -// privateKey?: Hex, -// address?: Hex, -// ) { -// if (transport.type === 'custom') { -// if (!address) { -// throw new Error("param 'address' is required for custom transports") -// } -// return async (txRequest: TransactionSerializableSuave) => { -// const rawSignature: Hex = await transport.request({ -// method: 'eth_sign', -// params: [ -// address, -// keccak256(serializeConfidentialComputeRecord(txRequest)), -// ], -// }) -// const parsedSignature = hexToSignature(rawSignature) -// return formatSignature(parsedSignature) -// } -// } else { -// if (!privateKey) { -// throw new Error('privateKey is required for non-custom transports') -// } -// return async (txRequest: TransactionSerializableSuave) => { -// return await signConfidentialComputeRecord(txRequest, privateKey) -// } -// } -// } +/** Returns the appropriate function for signing a CCR, as determined by the given transport. + * If the transport is `custom`, `address` must be provided. + * If the transport is not `custom`, `privateKey` must be provided instead. + */ +function getSigningFunction( + transport: TTransport, + privateKey?: Hex, + address?: Hex, +): (txRequest: TransactionSerializableSuave) => Promise> { + if (transport.type === 'custom') { + if (!address) { + throw new Error("'address' is required for custom transports") + } + return async (txRequest: TransactionSerializableSuave) => { + const rawSignature: Hex = await transport.request({ + method: 'eth_sign', + params: [ + address, + keccak256(serializeConfidentialComputeRecord(txRequest)), + ], + }) + const parsedSignature = hexToSignature(rawSignature) + return formatSignature(parsedSignature) + } + } else { + if (!privateKey) { + throw new Error("'privateKey' is required for non-custom transports") + } + return async (txRequest: TransactionSerializableSuave) => { + const {r,s,v} = await signConfidentialComputeRecord(txRequest, privateKey) + if (!r || !s || !v) throw new Error('failed to sign') + return {r,s,v} + } + } +} /** Get a SUAVE-enabled viem wallet. * @@ -167,6 +175,7 @@ function newSuaveWallet(params: { if (params.jsonRpcAccount && params.privateKey) { throw new Error("Cannot provide both 'jsonRpcAccount' and 'privateKey'") } + // Overrides viem wallet methods with SUAVE equivalents. const privateKeyAccount = params.privateKey ? privateKeyToAccount(params.privateKey) @@ -177,6 +186,40 @@ function newSuaveWallet(params: { transport: params.transport, chain: suaveRigil, }).extend((client) => ({ + /** Sign a prepared Confidential Compute Record; like a request, but with `confidentialInputsHash` and `type=0x42` */ + async signEIP712ConfidentialRequest(request: PreparedConfidentialRecord): Promise> { + if (request.isEIP712 === false) throw new Error('cannot sign an EIP712 CCR with isEIP712=false') + + const eip712Tx = { + ...request, + nonce: BigInt(request.nonce), + } + const rawSig = await client.signTypedData({ + primaryType: 'ConfidentialRecord', + message: eip712Tx, + types: { + Eip712Domain: [ + { name: 'name', type: 'string' }, + { name: 'verifyingContract', type: 'address' }, + ], + ConfidentialRecord: [ + { name: 'nonce', type: 'uint64' }, + { name: 'gasPrice', type: 'uint256' }, + { name: 'gas', type: 'uint64' }, + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + { name: 'kettleAddress', type: 'address' }, + { name: 'confidentialInputsHash', type: 'bytes32' }, + ], + }, + domain: { + name: 'ConfidentialRecord', + verifyingContract: eip712Tx.kettleAddress, + }, + }) + return hexToSignature(rawSig) + }, async sendTransaction(txRequest: TransactionRequestSuave) { const payload = await prepareTx(client, txRequest) const signedTx = await this.signTransaction(payload) @@ -185,7 +228,7 @@ function newSuaveWallet(params: { params: [signedTx], }) }, - async signTransaction(txRequest: TransactionRequestSuave) { + async signTransaction(txRequest: TransactionRequestSuave): Promise { if ( txRequest.type === SuaveTxRequestTypes.ConfidentialRequest || txRequest.kettleAddress || @@ -199,6 +242,11 @@ function newSuaveWallet(params: { if (!txRequest.kettleAddress) { throw new Error('kettleAddress is required for confidential requests') } + if (txRequest.maxFeePerGas || txRequest.maxPriorityFeePerGas) { + throw new Error( + 'maxFeePerGas and maxPriorityFeePerGas are not supported for confidential requests', + ) + } const confidentialInputs = txRequest.confidentialInputs ?? '0x' // get nonce, gas price, etc @@ -209,6 +257,7 @@ function newSuaveWallet(params: { const gas = txRequest.gas ?? (await ctxParams).gas const gasPrice = txRequest.gasPrice ?? (await ctxParams).gasPrice const chainId = txRequest.chainId ?? suaveRigil.id + const isEIP712 = txRequest.isEIP712 ?? true // prepare and sign confidential compute request if (!txRequest.to) { @@ -226,11 +275,12 @@ function newSuaveWallet(params: { if (!txRequest.kettleAddress) { throw new Error('missing `kettleAddress`') } - const eip712Tx = { - ...txRequest, - nonce: BigInt(nonce), + + const ccRecord: PreparedConfidentialRecord = { + // ...txRequest, + nonce, type: SuaveTxTypes.ConfidentialRecord, - chainId: BigInt(chainId), + chainId, to: txRequest.to, value, gas, @@ -238,43 +288,17 @@ function newSuaveWallet(params: { data: txRequest.data ?? '0x', kettleAddress: txRequest.kettleAddress, confidentialInputsHash: keccak256(confidentialInputs), + isEIP712, } - const presignTx = { - ...eip712Tx, - nonce, - chainId, - envelope: true, - } + const sig = isEIP712 ? await this.signEIP712ConfidentialRequest(ccRecord) : await (async () => { + const signCcr = getSigningFunction(client.transport, params.privateKey, params.jsonRpcAccount?.address) + return await signCcr(ccRecord) + })() - const rawSig = await client.signTypedData({ - primaryType: 'ConfidentialRecord', - message: eip712Tx, - types: { - Eip712Domain: [ - { name: 'name', type: 'string' }, - { name: 'verifyingContract', type: 'address' }, - ], - ConfidentialRecord: [ - { name: 'nonce', type: 'uint64' }, - { name: 'gasPrice', type: 'uint256' }, - { name: 'gas', type: 'uint64' }, - { name: 'to', type: 'address' }, - { name: 'value', type: 'uint256' }, - { name: 'data', type: 'bytes' }, - { name: 'kettleAddress', type: 'address' }, - { name: 'confidentialInputsHash', type: 'bytes32' }, - ], - }, - domain: { - name: 'ConfidentialRecord', - verifyingContract: presignTx.kettleAddress, - }, - }) - const sig = hexToSignature(rawSig) const { r, s, v } = formatSignature(sig) return serializeConfidentialComputeRequest({ - ...presignTx, + ...ccRecord, confidentialInputs, type: SuaveTxRequestTypes.ConfidentialRequest, r, From 007084390d57255f8328af6fac9903034299b2b4 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:38:34 -0700 Subject: [PATCH 04/10] lint --- src/chains/suave/parsers.ts | 4 +++- src/chains/suave/types.ts | 33 ++++++++++++++-------------- src/chains/suave/wallet.ts | 44 +++++++++++++++++++++++++------------ 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/chains/suave/parsers.ts b/src/chains/suave/parsers.ts index 547df5f8..accd5e99 100644 --- a/src/chains/suave/parsers.ts +++ b/src/chains/suave/parsers.ts @@ -25,7 +25,9 @@ const safeHexToNumber = (hex: Hex) => { return hexToNumber(hex) } -export const parseSignedComputeRequest = (signedComputeRequest: Hex): Partial => { +export const parseSignedComputeRequest = ( + signedComputeRequest: Hex, +): Partial => { const serializedType = signedComputeRequest.slice(0, 4) if (serializedType !== SuaveTxRequestTypes.ConfidentialRequest) { throw new InvalidSerializedTransactionTypeError({ diff --git a/src/chains/suave/types.ts b/src/chains/suave/types.ts index 93afc419..d15d3fbb 100644 --- a/src/chains/suave/types.ts +++ b/src/chains/suave/types.ts @@ -37,8 +37,8 @@ export type SuaveTxRequestType = `${(typeof SuaveTxRequestTypes)[keyof typeof SuaveTxRequestTypes]}` type ConfidentialOverrides = { - kettleAddress?: Address, - isEIP712?: boolean, + kettleAddress?: Address + isEIP712?: boolean } type ConfidentialComputeRequestOverrides = ConfidentialOverrides & { @@ -46,7 +46,7 @@ type ConfidentialComputeRequestOverrides = ConfidentialOverrides & { } type ConfidentialComputeRecordOverrides = ConfidentialOverrides & { - confidentialInputsHash?: Hash, + confidentialInputsHash?: Hash } export type SuaveBlockOverrides = {} // Add any specific block overrides if necessary for Suave @@ -137,14 +137,10 @@ export type ConfidentialComputeRecord< TQuantity = bigint, TIndex = number, > = Omit< - TransactionBase< - TQuantity, - TIndex, - SuaveTxTypes.ConfidentialRecord, - TPending - >, + TransactionBase, 'blockHash' | 'transactionIndex' | 'blockNumber' | 'from' -> & ConfidentialComputeRecordOverrides +> & + ConfidentialComputeRecordOverrides export type ConfidentialComputeRecordRpc = ConfidentialComputeRecord @@ -155,15 +151,18 @@ export type TransactionRequestSuave< TType extends SuaveTxRequestType = SuaveTxRequestType, > = TransactionRequestBase & ConfidentialComputeRequestOverrides & { - from?: Address, + from?: Address } -export type PreparedConfidentialRecord = Omit & { - data: Hex, - to: Address, - gasPrice: bigint, - kettleAddress: Address, - confidentialInputsHash: Hash, +export type PreparedConfidentialRecord = Omit< + ConfidentialComputeRecord, + 'input' | 'typeHex' | 'hash' | 'r' | 's' | 'v' +> & { + data: Hex + to: Address + gasPrice: bigint + kettleAddress: Address + confidentialInputsHash: Hash } export type RpcTransactionRequestSuave = diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index ba016d2b..a2897126 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -5,14 +5,14 @@ import { type JsonRpcAccount, type PrivateKeyAccount, type PublicClient, + TransactionType, type Transport, + TransportConfig, type WalletClient, createPublicClient, createWalletClient, hexToSignature, keccak256, - TransportConfig, - TransactionType, } from '../../index.js' import type { Hex } from '../../types/misc.js' import { suaveRigil } from '../index.js' @@ -21,12 +21,12 @@ import { serializeConfidentialComputeRequest, } from './serializers.js' import { + PreparedConfidentialRecord, SuaveTxRequestTypes, + SuaveTxType, SuaveTxTypes, type TransactionRequestSuave, TransactionSerializableSuave, - PreparedConfidentialRecord, - SuaveTxType, } from './types.js' /// client types @@ -80,7 +80,9 @@ function getSigningFunction( transport: TTransport, privateKey?: Hex, address?: Hex, -): (txRequest: TransactionSerializableSuave) => Promise> { +): ( + txRequest: TransactionSerializableSuave, +) => Promise> { if (transport.type === 'custom') { if (!address) { throw new Error("'address' is required for custom transports") @@ -101,9 +103,12 @@ function getSigningFunction( throw new Error("'privateKey' is required for non-custom transports") } return async (txRequest: TransactionSerializableSuave) => { - const {r,s,v} = await signConfidentialComputeRecord(txRequest, privateKey) + const { r, s, v } = await signConfidentialComputeRecord( + txRequest, + privateKey, + ) if (!r || !s || !v) throw new Error('failed to sign') - return {r,s,v} + return { r, s, v } } } } @@ -187,8 +192,11 @@ function newSuaveWallet(params: { chain: suaveRigil, }).extend((client) => ({ /** Sign a prepared Confidential Compute Record; like a request, but with `confidentialInputsHash` and `type=0x42` */ - async signEIP712ConfidentialRequest(request: PreparedConfidentialRecord): Promise> { - if (request.isEIP712 === false) throw new Error('cannot sign an EIP712 CCR with isEIP712=false') + async signEIP712ConfidentialRequest( + request: PreparedConfidentialRecord, + ): Promise> { + if (request.isEIP712 === false) + throw new Error('cannot sign an EIP712 CCR with isEIP712=false') const eip712Tx = { ...request, @@ -228,7 +236,9 @@ function newSuaveWallet(params: { params: [signedTx], }) }, - async signTransaction(txRequest: TransactionRequestSuave): Promise { + async signTransaction( + txRequest: TransactionRequestSuave, + ): Promise { if ( txRequest.type === SuaveTxRequestTypes.ConfidentialRequest || txRequest.kettleAddress || @@ -291,10 +301,16 @@ function newSuaveWallet(params: { isEIP712, } - const sig = isEIP712 ? await this.signEIP712ConfidentialRequest(ccRecord) : await (async () => { - const signCcr = getSigningFunction(client.transport, params.privateKey, params.jsonRpcAccount?.address) - return await signCcr(ccRecord) - })() + const sig = isEIP712 + ? await this.signEIP712ConfidentialRequest(ccRecord) + : await (async () => { + const signCcr = getSigningFunction( + client.transport, + params.privateKey, + params.jsonRpcAccount?.address, + ) + return await signCcr(ccRecord) + })() const { r, s, v } = formatSignature(sig) return serializeConfidentialComputeRequest({ From 81e0ccc52fc9257bef17ebd4bce1cb67cd2eb669 Mon Sep 17 00:00:00 2001 From: zeroXbrock Date: Fri, 28 Jun 2024 00:39:11 +0000 Subject: [PATCH 05/10] chore: format --- bun.lockb | Bin 572880 -> 572880 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 0f3fb9c20aaf4cce0010c947783051be483e184c..11c0ce2e33c79b020bc6aa4bbb614e9113972682 100755 GIT binary patch delta 9808 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 delta 9809 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 From 3135ea1b71c6019d24c33ef191e5f3b70b0f917a Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:31:41 -0700 Subject: [PATCH 06/10] fix broken url in example --- examples/suave-web-demo/src/suave.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/suave-web-demo/src/suave.ts b/examples/suave-web-demo/src/suave.ts index 4b6fae73..c00db4b4 100644 --- a/examples/suave-web-demo/src/suave.ts +++ b/examples/suave-web-demo/src/suave.ts @@ -6,7 +6,7 @@ import { http, } from 'viem' import { privateKeyToAccount } from 'viem/accounts' -import { suaveRigil, goerli } from 'viem/chains' +import { suaveRigil, holesky } from 'viem/chains' import { OFAOrder } from '../../suave/bids' import { getSuaveWallet } from 'viem/chains/utils' import BidContractDeployment from '../../suave/deployedAddress.json' @@ -16,18 +16,18 @@ const KETTLE_ADDRESS: Address = '0xb5feafbdd752ad52afb7e1bd2e40432a485bbb7f' const ADMIN_KEY: Hex = '0x91ab9a7e53c220e6210460b65a7a3bb2ca181412a8a7b43ff336b3df1737ce12' // public goerli node, may need to change if it goes down: -const GOERLI_RPC_URL_HTTP: string = 'https://goerli.rigil.suave.flashbots.net' +const L1_RPC_URL_HTTP: string = 'https://holesky.rigil.suave.flashbots.net' const goerliWallet = createWalletClient({ account: privateKeyToAccount( '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', ), - chain: goerli, - transport: http(GOERLI_RPC_URL_HTTP), + chain: holesky, + transport: http(L1_RPC_URL_HTTP), }) const goerliProvider = createPublicClient({ - transport: http(GOERLI_RPC_URL_HTTP), - chain: goerli, + transport: http(L1_RPC_URL_HTTP), + chain: holesky, }) const suaveAdminWallet = getSuaveWallet({ privateKey: ADMIN_KEY, From 128e0f8bbf98ae17d90fbf53f9fbfe2397b32a88 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:45:01 -0700 Subject: [PATCH 07/10] cleanup, use http transport for suaveProvider in example --- examples/suave-web-demo/src/main.ts | 4 ++-- examples/suave/index.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/suave-web-demo/src/main.ts b/examples/suave-web-demo/src/main.ts index fb5a9fbc..2c613205 100644 --- a/examples/suave-web-demo/src/main.ts +++ b/examples/suave-web-demo/src/main.ts @@ -4,7 +4,7 @@ import typescriptLogo from './typescript.svg' import flashbotsLogo from './flashbots_icon.svg' import { setupConnectButton, setupDripFaucetButton, setupSendBidButton } from './suave' import { Logo } from './components' -import { custom, formatEther } from 'viem' +import { custom, formatEther, http } from 'viem' import { getSuaveWallet, getSuaveProvider } from 'viem/chains/utils' import { suaveRigil } from 'viem/chains' @@ -38,7 +38,7 @@ setupConnectButton(document.querySelector('#connect')!, } const suaveWallet = getSuaveWallet({jsonRpcAccount: account, transport: custom(ethereum)}) console.log(suaveWallet) - const suaveProvider = getSuaveProvider(custom(ethereum)) + const suaveProvider = getSuaveProvider(http("http://localhost:8545")) suaveProvider.getBalance({ address: account }).then((balance: any) => { suaveProvider.getChainId().then((chainId: any) => { if (chainId !== suaveRigil.id) { diff --git a/examples/suave/index.ts b/examples/suave/index.ts index cd228fbb..37f09afa 100644 --- a/examples/suave/index.ts +++ b/examples/suave/index.ts @@ -6,7 +6,6 @@ import { OFAOrder } from './bids' import { SuaveProvider, SuaveWallet, getSuaveProvider, getSuaveWallet, parseTransactionSuave } from 'viem/chains/utils' import { HttpTransport } from 'viem' import BidContractDeployment from './deployedAddress.json' -import { parseSignedComputeRequest } from 'viem/chains/suave/parsers' const failEnv = (name: string) => { throw new Error(`missing env var ${name}`) From dcd7691785a5135f1330144b95063d4bf648857c Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 27 Jun 2024 22:08:39 -0700 Subject: [PATCH 08/10] fix broken test, fix bug in parser assertion --- src/chains/suave/parsers.test.ts | 8 +++++--- src/chains/suave/parsers.ts | 3 ++- src/chains/suave/wallet.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/chains/suave/parsers.test.ts b/src/chains/suave/parsers.test.ts index 69b5db94..fdee6ccd 100644 --- a/src/chains/suave/parsers.test.ts +++ b/src/chains/suave/parsers.test.ts @@ -27,6 +27,7 @@ describe('Suave Transaction Parsers', () => { gas: 100n, gasPrice: 100n, nonce: 0, + // isEIP712: true, type: SuaveTxRequestTypes.ConfidentialRequest, kettleAddress: accounts[1].address, confidentialInputs: '0x42424242424242424242424242424242' as Hex, @@ -42,10 +43,11 @@ describe('Suave Transaction Parsers', () => { "data": "${ccRequest.data}", "gas": 100n, "gasPrice": 100n, + "isEIP712": true, "kettleAddress": "${ccRequest.kettleAddress}", "nonce": 0, - "r": "0xaf44e7e1c628554f85a8bba6a6ced571e87f5996f195d5e3c97a8c03d4ee61e1", - "s": "0x779bcc8d321f21118ce7682028fcac78123a1d2fcbc40b18866b54ac343c2cf8", + "r": "0xae3d9c09d56078e22d4b9747c988f71d086bb24ab42dc15ea431b7bd10e67a99", + "s": "0x6976c6aaaddec226a56ccb0001935397c68d16f58945769fd855121b7e542863", "to": "${ccRequest.to}", "type": "0x43", "v": 0n, @@ -58,7 +60,7 @@ describe('Suave Transaction Parsers', () => { test('parseTransactionSuave parses all SUAVE tx types', async () => { const wallet = getWallet() const serializedTx = - '0x43f902aaf90184098412504db4830f4240949a151aa453329f3cdf04d8e4e81585a423f7fc2580b8e4d8f55db90000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c012e8eff6ead85d9d948631a18c41afb60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000009a151aa453329f3cdf04d8e4e81585a423f7fc25000000000000000000000000000000000000000000000000000000000000000094b5feafbdd752ad52afb7e1bd2e40432a485bbb7fa0249c92db3766bc250ffe17682d363e78dbd3aa1fff59a3b5ca242c872910effa8401008c4580a04a0e49a3711af960c5e76d10a21ae318912702b4cfdb37e6baf087edc84feedca02304c28a2a6cb07efa0643e4e2a78bdd2980ccc1d23b359c9cc67543461eb98ab90120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000dc7b22747873223a5b2230786638363538303064383235336163393431633638353738353161333737633866613736343130396435353933383261393235376334393962383230336538383038343032303131386164613037613861313734613333643136353432363938616538353061303965303530333262373865353934616164613061343164313137376136383333636266633630613031633465663334313031626161363665376338393438376365353062343239653138623733663535323064366130656633396630366234386362343862373064225d7d00000000' + '0x43f8acf8998064649470997970c51812dc3a010c7d01b50e0d17dc79c880139470997970c51812dc3a010c7d01b50e0d17dc79c8a0a71e488c022df32f3b11c11282cf8c6f6b1d7c1d8b3bc3dc921cb6c2c5c0aae7018401008c4580a0ae3d9c09d56078e22d4b9747c988f71d086bb24ab42dc15ea431b7bd10e67a99a06976c6aaaddec226a56ccb0001935397c68d16f58945769fd855121b7e5428639042424242424242424242424242424242' const parsedTx = parseTransactionSuave(serializedTx) expect(parsedTx.type).toBe(SuaveTxRequestTypes.ConfidentialRequest) diff --git a/src/chains/suave/parsers.ts b/src/chains/suave/parsers.ts index accd5e99..1fbf9b13 100644 --- a/src/chains/suave/parsers.ts +++ b/src/chains/suave/parsers.ts @@ -137,12 +137,13 @@ export function assertTransactionSuave( confidentialInputsHash, isEIP712, kettleAddress, + type, to, r, s, v, } = transaction - if (isEIP712 === undefined) throw new Error('isEIP712 is required') + if (type === SuaveTxRequestTypes.ConfidentialRequest && isEIP712 === undefined) throw new Error("must encode 'isEIP712' for confidential requests") if (chainId && chainId <= 0) throw new Error('invalid chain ID') if (to && !isAddress(to)) throw new Error('invalid to address') if (!gasPrice) throw new Error('gasPrice is required') diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index a2897126..4fa4280b 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -238,7 +238,7 @@ function newSuaveWallet(params: { }, async signTransaction( txRequest: TransactionRequestSuave, - ): Promise { + ): Promise<`${SuaveTxType|TransactionType}${string}`> { if ( txRequest.type === SuaveTxRequestTypes.ConfidentialRequest || txRequest.kettleAddress || From a94091b0254a4d7de7aca799dc69fe4f411b3065 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 27 Jun 2024 22:08:58 -0700 Subject: [PATCH 09/10] lint --- src/chains/suave/parsers.ts | 6 +++++- src/chains/suave/wallet.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/chains/suave/parsers.ts b/src/chains/suave/parsers.ts index 1fbf9b13..05834e77 100644 --- a/src/chains/suave/parsers.ts +++ b/src/chains/suave/parsers.ts @@ -143,7 +143,11 @@ export function assertTransactionSuave( s, v, } = transaction - if (type === SuaveTxRequestTypes.ConfidentialRequest && isEIP712 === undefined) throw new Error("must encode 'isEIP712' for confidential requests") + if ( + type === SuaveTxRequestTypes.ConfidentialRequest && + isEIP712 === undefined + ) + throw new Error("must encode 'isEIP712' for confidential requests") if (chainId && chainId <= 0) throw new Error('invalid chain ID') if (to && !isAddress(to)) throw new Error('invalid to address') if (!gasPrice) throw new Error('gasPrice is required') diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index 4fa4280b..52c61736 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -238,7 +238,7 @@ function newSuaveWallet(params: { }, async signTransaction( txRequest: TransactionRequestSuave, - ): Promise<`${SuaveTxType|TransactionType}${string}`> { + ): Promise<`${SuaveTxType | TransactionType}${string}`> { if ( txRequest.type === SuaveTxRequestTypes.ConfidentialRequest || txRequest.kettleAddress || From 4c482fc653d2059e86e45ecd8ba43c3bd10ff864 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Fri, 28 Jun 2024 14:45:42 -0700 Subject: [PATCH 10/10] fix bad falsy v check --- src/chains/suave/wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chains/suave/wallet.ts b/src/chains/suave/wallet.ts index 52c61736..4c02bb52 100644 --- a/src/chains/suave/wallet.ts +++ b/src/chains/suave/wallet.ts @@ -107,7 +107,7 @@ function getSigningFunction( txRequest, privateKey, ) - if (!r || !s || !v) throw new Error('failed to sign') + if (!r || !s || v === undefined) throw new Error('failed to sign') return { r, s, v } } }