Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

default request args #51

Merged
merged 17 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading