Skip to content

Commit

Permalink
default request args (#51)
Browse files Browse the repository at this point in the history
* use verifyingAddress in eip712 domain

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

* fix broken url in example

* fix broken test, fix bug in parser assertion


---------

Co-authored-by: zeroXbrock <[email protected]>
  • Loading branch information
zeroXbrock and zeroXbrock authored Jul 1, 2024
1 parent 69f6c35 commit 4005170
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 43 deletions.
12 changes: 8 additions & 4 deletions examples/suave-web-demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import './style.css'
import viteLogo from '/vite.svg'
import typescriptLogo from './typescript.svg'
import flashbotsLogo from './flashbots_icon.svg'
import { setupConnectButton, setupDripFaucetButton, setupSendBidButton } from './suave'
import { SUAVE_RPC_URL_HTTP, setupConnectButton, setupDripFaucetButton, setupSendBidButton } from './suave'
import { Logo } from './components'
import { custom, formatEther, http } from 'viem'
import { getSuaveWallet, getSuaveProvider } from 'viem/chains/utils'
Expand Down Expand Up @@ -36,9 +36,13 @@ setupConnectButton(document.querySelector<HTMLButtonElement>('#connect')!,
console.error(err)
alert(err.message)
}
const suaveWallet = getSuaveWallet({jsonRpcAccount: account, transport: custom(ethereum)})
const suaveWallet = getSuaveWallet({
jsonRpcAccount: account,
transport: custom(ethereum),
customRpc: SUAVE_RPC_URL_HTTP,
})
console.log(suaveWallet)
const suaveProvider = getSuaveProvider(http("http://localhost:8545"))
const suaveProvider = getSuaveProvider(http(SUAVE_RPC_URL_HTTP))
suaveProvider.getBalance({ address: account }).then((balance: any) => {
suaveProvider.getChainId().then((chainId: any) => {
if (chainId !== suaveRigil.id) {
Expand All @@ -58,7 +62,7 @@ setupConnectButton(document.querySelector<HTMLButtonElement>('#connect')!,
alert(err)
return
}
const suaveProvider = getSuaveProvider(custom(ethereum))
const suaveProvider = getSuaveProvider(http(SUAVE_RPC_URL_HTTP))
suaveProvider.getTransactionReceipt({hash: txHash}).then((receipt: any) => {
console.log("receipt", receipt)
document.querySelector<HTMLDivElement>('#status-content')!.innerHTML = `
Expand Down
19 changes: 9 additions & 10 deletions examples/suave-web-demo/src/suave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ import BidContractDeployment from '../../suave/deployedAddress.json'
const KETTLE_ADDRESS: Address = '0xb5feafbdd752ad52afb7e1bd2e40432a485bbb7f'
const ADMIN_KEY: Hex =
'0x91ab9a7e53c220e6210460b65a7a3bb2ca181412a8a7b43ff336b3df1737ce12'
// public goerli node, may need to change if it goes down:
const L1_RPC_URL_HTTP: string = 'https://holesky.rigil.suave.flashbots.net'
export const L1_RPC_URL_HTTP: string = 'https://holesky.rigil.suave.flashbots.net'
export const SUAVE_RPC_URL_HTTP: string = 'http://localhost:8545'

const goerliWallet = createWalletClient({
const l1Wallet = createWalletClient({
account: privateKeyToAccount(
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
),
chain: holesky,
transport: http(L1_RPC_URL_HTTP),
})
const goerliProvider = createPublicClient({
transport: http(L1_RPC_URL_HTTP),
const l1Provider = createPublicClient({
chain: holesky,
transport: http(L1_RPC_URL_HTTP),
})
const suaveAdminWallet = getSuaveWallet({
privateKey: ADMIN_KEY,
transport: http('http://localhost:8545'),
transport: http(SUAVE_RPC_URL_HTTP),
})

/** Sets up "connect to wallet" button and holds wallet instance. */
Expand Down Expand Up @@ -73,27 +73,26 @@ export function setupSendBidButton(
// create sample transaction; won't land onchain, but will pass payload validation
const sampleTx = {
type: 'eip1559' as const,
chainId: 5,
chainId: 17000,
nonce: 0,
maxBaseFeePerGas: 0x3b9aca00n,
maxPriorityFeePerGas: 0x5208n,
to: '0x0000000000000000000000000000000000000000' as Address,
value: 0n,
data: '0xf00ba7' as Hex,
}
const signedTx = await goerliWallet.signTransaction(sampleTx)
const signedTx = await l1Wallet.signTransaction(sampleTx)
console.log('signed goerli tx', signedTx)

// create bid & send ccr
const decryptionCondition = 1n + (await goerliProvider.getBlockNumber())
const decryptionCondition = 1n + (await l1Provider.getBlockNumber())
console.log("decryptionCondition", decryptionCondition)
const bid = new OFAOrder(
decryptionCondition,
signedTx,
KETTLE_ADDRESS,
BidContractDeployment.address as Address,
)
console.log("bid", bid)
const ccr = bid.toConfidentialRequest()
console.log("ccr", ccr)
let txHash: Hex
Expand Down
3 changes: 0 additions & 3 deletions examples/suave/bids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ export class OFAOrder {
return {
to: this.OFAContract,
data: this.newOrderCalldata(),
type: '0x43',
gas: 500000n,
gasPrice: 1000000000n,
isEIP712,
kettleAddress: this.kettle,
confidentialInputs: this.confidentialInputsBytes(),
Expand Down
90 changes: 64 additions & 26 deletions src/chains/suave/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
hexToSignature,
keccak256,
} from '../../index.js'
import type { Hex } from '../../types/misc.js'
import type { Hash, Hex } from '../../types/misc.js'
import { suaveRigil } from '../index.js'
import {
serializeConfidentialComputeRecord,
Expand Down Expand Up @@ -114,6 +114,11 @@ function getSigningFunction<TTransport extends TransportConfig>(
}

/** Get a SUAVE-enabled viem wallet.
*
* @param params.transport - the transport to use for RPC requests. Defaults to public testnet.
* @param params.jsonRpcAccount - the address to use for EIP-1193 requests (browser wallets). Required for `custom` transports.
* @param params.privateKey - the private key to use for signing transactions. Required for *non*-`custom` transports.
* @param params.customRpc - the RPC URL to use for SUAVE calls (nonce, gas estimates, etc) when using a `custom` transport. Defaults to transport URL.
*
* @example
* import { http } from 'viem'
Expand All @@ -133,6 +138,8 @@ function getSigningFunction<TTransport extends TransportConfig>(
* const wallet = getSuaveWallet({
* transport: custom(window.ethereum),
* jsonRpcAccount: account,
* // ensures reliable RPC requests (nonce, gas estimates, etc) as user switches RPCs in their wallet
* customRpc: 'http://localhost:8545',
* })
* }
* main()
Expand All @@ -141,6 +148,7 @@ export function getSuaveWallet<TTransport extends Transport>(params: {
transport?: TTransport
jsonRpcAccount?: Hex
privateKey?: Hex
customRpc?: string
}): SuaveWallet<TTransport> {
return newSuaveWallet({
transport: params.transport ?? http(suaveRigil.rpcUrls.public.http[0]),
Expand All @@ -149,29 +157,18 @@ export function getSuaveWallet<TTransport extends Transport>(params: {
address: params.jsonRpcAccount,
type: 'json-rpc',
},
customRpc: params.customRpc,
})
}

async function prepareTx(
client: ReturnType<typeof newSuaveWallet>,
txRequest: TransactionRequestSuave,
) {
const preparedTx = await client.prepareTransactionRequest(txRequest)
const payload: TransactionRequestSuave = {
...txRequest,
from: txRequest.from ?? preparedTx.from,
nonce: txRequest.nonce ?? preparedTx.nonce,
gas: txRequest.gas ?? preparedTx.gas,
gasPrice: txRequest.gasPrice ?? preparedTx.gasPrice,
chainId: txRequest.chainId ?? suaveRigil.id,
}
return payload
}

/** Get a SUAVE-enabled viem wallet. */
function newSuaveWallet<TTransport extends Transport>(params: {
transport: TTransport
/** must set this for custom transports only. */
jsonRpcAccount?: JsonRpcAccount
/** should set this for custom transports. */
customRpc?: string
/** must set this for non-custom transports only. */
privateKey?: Hex
}): SuaveWallet<TTransport> {
if (!params.jsonRpcAccount && !params.privateKey) {
Expand All @@ -185,12 +182,48 @@ function newSuaveWallet<TTransport extends Transport>(params: {
const privateKeyAccount = params.privateKey
? privateKeyToAccount(params.privateKey)
: undefined
const account = params.jsonRpcAccount ?? privateKeyAccount
const account = params.jsonRpcAccount || privateKeyAccount

return createWalletClient({
account,
transport: params.transport,
chain: suaveRigil,
}).extend((client) => ({
/** If `customRpc` is provided, this is used for some RPC requests instead of provided (custom) `transport`.
* `transport` is still used for things that require the wallet's account (signing, etc).
*/
customProvider: getSuaveProvider(
params.customRpc ? http(params.customRpc) : params.transport,
),

/** Prepare any omitted fields in request. */
async prepareTxRequest(
txRequest: TransactionRequestSuave,
): Promise<TransactionRequestSuave> {
const gas =
txRequest.gas ??
(() => {
// TODO: replace this with a working call to eth_estimateGas
console.warn('no gas provided, using default 30000000')
return 30000000n
})()
const preparedTx = await this.customProvider.prepareTransactionRequest({
account: client.account,
...txRequest,
gas,
})
const gasPrice =
preparedTx.gasPrice ?? (await this.customProvider.getGasPrice())
return {
...txRequest,
from: txRequest.from ?? preparedTx.from,
nonce: txRequest.nonce ?? preparedTx.nonce,
gas: txRequest.gas ?? preparedTx.gas,
gasPrice: txRequest.gasPrice ?? gasPrice,
chainId: txRequest.chainId ?? suaveRigil.id,
}
},

/** Sign a prepared Confidential Compute Record; like a request, but with `confidentialInputsHash` and `type=0x42` */
async signEIP712ConfidentialRequest(
request: PreparedConfidentialRecord,
Expand Down Expand Up @@ -228,14 +261,19 @@ function newSuaveWallet<TTransport extends Transport>(params: {
})
return hexToSignature(rawSig)
},
async sendTransaction(txRequest: TransactionRequestSuave) {
const payload = await prepareTx(client, txRequest)

/** Sign and Send an unsigned request. */
async sendTransaction(txRequest: TransactionRequestSuave): Promise<Hash> {
// signTransaction also invokes prepareTxRequest, but only for CCRs. this is still needed for standard txs.
const payload = await this.prepareTxRequest(txRequest)
const signedTx = await this.signTransaction(payload)
return client.request({
return this.customProvider.request({
method: 'eth_sendRawTransaction',
params: [signedTx],
params: [signedTx as Hex],
})
},

/** Sign a transaction request. */
async signTransaction(
txRequest: TransactionRequestSuave,
): Promise<`${SuaveTxType | TransactionType}${string}`> {
Expand All @@ -259,9 +297,9 @@ function newSuaveWallet<TTransport extends Transport>(params: {
}

const confidentialInputs = txRequest.confidentialInputs ?? '0x'
// 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
// get nonce, gas price, etc.
const ctxParams = this.prepareTxRequest(txRequest)
// 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
Expand All @@ -287,7 +325,7 @@ function newSuaveWallet<TTransport extends Transport>(params: {
}

const ccRecord: PreparedConfidentialRecord = {
// ...txRequest,
...txRequest,
nonce,
type: SuaveTxTypes.ConfidentialRecord,
chainId,
Expand Down

0 comments on commit 4005170

Please sign in to comment.