Skip to content

Commit

Permalink
eip712 signature for CCR (#43)
Browse files Browse the repository at this point in the history
* hacky impl: eip712 signature for CCR

* use verifyingAddress in eip712 domain

* full support for EIP712 CCRs alongside legacy CCR & standard eth txs

* fix broken url in example

* cleanup, use http transport for suaveProvider in example

* fix broken test, fix bug in parser assertion

* fix bad falsy v check

---------

Co-authored-by: zeroXbrock <zeroXbrock@users.noreply.github.com>
  • Loading branch information
zeroXbrock and zeroXbrock authored Jun 29, 2024
1 parent 12ff5ab commit 69f6c35
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 65 deletions.
4 changes: 2 additions & 2 deletions examples/suave-web-demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -38,7 +38,7 @@ setupConnectButton(document.querySelector<HTMLButtonElement>('#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) {
Expand Down
12 changes: 6 additions & 6 deletions examples/suave-web-demo/src/suave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion examples/suave/bids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down
7 changes: 3 additions & 4 deletions examples/suave/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ 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'

const failEnv = (name: string) => {
throw new Error(`missing env var ${name}`)
Expand Down Expand Up @@ -126,12 +125,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
Expand Down
8 changes: 5 additions & 3 deletions src/chains/suave/parsers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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)

Expand Down
18 changes: 15 additions & 3 deletions src/chains/suave/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ const safeHexToNumber = (hex: Hex) => {
return hexToNumber(hex)
}

export const parseSignedComputeRequest = (signedComputeRequest: Hex) => {
export const parseSignedComputeRequest = (
signedComputeRequest: Hex,
): Partial<TransactionSerializableSuave> => {
const serializedType = signedComputeRequest.slice(0, 4)
if (serializedType !== SuaveTxRequestTypes.ConfidentialRequest) {
throw new InvalidSerializedTransactionTypeError({
Expand All @@ -43,14 +45,15 @@ export const parseSignedComputeRequest = (signedComputeRequest: Hex) => {
data,
kettleAddress,
confidentialInputsHash,
isEIP712,
chainId,
v,
r,
s,
],
confidentialInputs,
] = txArray
if (txArray.length !== 2 || txArray[0].length !== 12) {
if (txArray.length !== 2 || txArray[0].length !== 13) {
throw new InvalidSerializedTransactionError({
attributes: {
nonce,
Expand All @@ -59,6 +62,7 @@ export const parseSignedComputeRequest = (signedComputeRequest: Hex) => {
gas,
kettleAddress,
confidentialInputsHash,
isEIP712,
value,
gasPrice,
chainId,
Expand All @@ -78,6 +82,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),
Expand All @@ -100,7 +105,7 @@ export type ParseTransactionSuaveReturnType<TType extends SuaveTxType> =

/** Parse a serialized transaction into a SUAVE Transaction object. */
export function parseTransactionSuave(
serializedTransaction: TransactionSerializedSuave,
serializedTransaction: TransactionSerializedSuave | Hex,
): ParseTransactionSuaveReturnType<SuaveTxType> {
const serializedType = serializedTransaction.slice(0, 4)
const parsedTx =
Expand Down Expand Up @@ -130,12 +135,19 @@ export function assertTransactionSuave(
maxFeePerGas,
confidentialInputs,
confidentialInputsHash,
isEIP712,
kettleAddress,
type,
to,
r,
s,
v,
} = transaction
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')
Expand Down
5 changes: 4 additions & 1 deletion src/chains/suave/serializers.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -166,6 +166,9 @@ export const serializeConfidentialComputeRequest = (
transaction.kettleAddress,
transaction.confidentialInputsHash,

// envelope
boolToHex(transaction.isEIP712 ?? true),

numberToHex(transaction.chainId),
toHex(transaction.v),
transaction.r,
Expand Down
31 changes: 14 additions & 17 deletions src/chains/suave/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type SuaveTxRequestType =

type ConfidentialOverrides = {
kettleAddress?: Address
isEIP712?: boolean
}

type ConfidentialComputeRequestOverrides = ConfidentialOverrides & {
Expand Down Expand Up @@ -136,22 +137,8 @@ export type ConfidentialComputeRecord<
TQuantity = bigint,
TIndex = number,
> = Omit<
Omit<
Omit<
Omit<
TransactionBase<
TQuantity,
TIndex,
SuaveTxTypes.ConfidentialRecord,
TPending
>,
'blockHash'
>,
'transactionIndex'
>,
'blockNumber'
>,
'from'
TransactionBase<TQuantity, TIndex, SuaveTxTypes.ConfidentialRecord, TPending>,
'blockHash' | 'transactionIndex' | 'blockNumber' | 'from'
> &
ConfidentialComputeRecordOverrides

Expand All @@ -167,6 +154,17 @@ export type TransactionRequestSuave<
from?: Address
}

export type PreparedConfidentialRecord = Omit<
ConfidentialComputeRecord,
'input' | 'typeHex' | 'hash' | 'r' | 's' | 'v'
> & {
data: Hex
to: Address
gasPrice: bigint
kettleAddress: Address
confidentialInputsHash: Hash
}

export type RpcTransactionRequestSuave<TType = SuaveTxType> =
TransactionRequestSuave<Hex, Hex> & {
type: TType
Expand Down Expand Up @@ -195,7 +193,6 @@ export type TransactionSerializableSuave<
> = TransactionSerializableEIP2930<TQuantity, TIndex> &
ConfidentialComputeRecordOverrides &
ConfidentialComputeRequestOverrides & {
signedComputeRecord?: Hex
type: TType
}

Expand Down
Loading

0 comments on commit 69f6c35

Please sign in to comment.