Skip to content

Commit

Permalink
Interact With SUAVE (#35)
Browse files Browse the repository at this point in the history
* Send Requests Section

* typescript SDK + text fixes

---------

Co-authored-by: Andy Tudhope <[email protected]>
  • Loading branch information
dmarzzz and andytudhope authored Nov 6, 2023
1 parent 3afa360 commit a8f1958
Show file tree
Hide file tree
Showing 8 changed files with 805 additions and 224 deletions.
89 changes: 89 additions & 0 deletions docs/how-to/interact-with-suave/deploy-a-contract.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: Deploy Simple Contract
description: Deploy a Precompiled Contract
---

We've written a number of example smart contracts to help get you started thinking about what's possible. For this script using the golang SDK, we'll stick to deploying one of these examples to keep things simple.

Inside [suave-geth](https://github.com/flashbots/suave-geth) create a new file in `suave/devenv/cmd` called `deploy.go`:

```go
package main

import (
"crypto/ecdsa"
"fmt"

_ "embed"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/suave/e2e"
"github.com/ethereum/go-ethereum/suave/sdk"
)

var (
// This is the address we used when starting the MEVM
exNodeEthAddr = common.HexToAddress("b5feafbdd752ad52afb7e1bd2e40432a485bbb7f")
exNodeNetAddr = "http://localhost:8545"
// This account is funded in both devenv networks
// address: 0xBE69d72ca5f88aCba033a063dF5DBe43a4148De0
fundedAccount = newPrivKeyFromHex(
"91ab9a7e53c220e6210460b65a7a3bb2ca181412a8a7b43ff336b3df1737ce12"
)
)

var (
mevShareArtifact = e2e.MevShareBidContract
)

func main() {
rpcClient, _ := rpc.Dial(exNodeNetAddr)
mevmClt := sdk.NewClient(rpcClient, fundedAccount.priv, exNodeEthAddr)

var mevShareContract *sdk.Contract
_ = mevShareContract

txnResult, err := sdk.DeployContract(mevShareArtifact.Code, mevmClt)
if err != nil {
fmt.Errorf("Failed to deploy contract: %v", err)
}
receipt, err := txnResult.Wait()
if err != nil {
fmt.Errorf("Failed to wait for transaction result: %v", err)
}
if receipt.Status == 0 {
fmt.Errorf("Failed to deploy contract: %v", err)
}

fmt.Printf("- Example contract deployed: %s\n", receipt.ContractAddress)
mevShareContract = sdk.GetContract(receipt.ContractAddress, mevShareArtifact.Abi, mevmClt)
}

// Helpers, not unique to SUAVE

type privKey struct {
priv *ecdsa.PrivateKey
}

func newPrivKeyFromHex(hex string) *privKey {
key, err := crypto.HexToECDSA(hex)
if err != nil {
panic(fmt.Sprintf("failed to parse private key: %v", err))
}
return &privKey{priv: key}
}
```

If you now run:

```bash
go run suave/devenv/cmd/deploy.go
```

You should see the address of your new example contract printed in the terminal.

The important parts to note when deploying contracts are the call to [`e2e`](https://github.com/flashbots/suave-geth/blob/main/suave/e2e/contracts.go), which helps generate ABIs and bytecode for contracts, and the `sdk.DeplyContract` and `sdk.GetContract`.

If you're able to generate the necessary ABIs and bytecode, you should be able to deploy any contract you like using the above pattern.
118 changes: 118 additions & 0 deletions docs/how-to/interact-with-suave/deploy-and-test-example-suapp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
title: Deploy and Test Example SUAPP
description: Deploy a preocmpiled SUAPP and Interact with it
---

SUAVE-geth has an example version of MEV-share along with a script that will:
- deploy example MEV-share
- sign an submit a transaction to be back run
- grab the extracted hint from onchain
- sign a back run transaction and submit it

## Running the Script

If you followed the [previous how-to](/how-to/run-suave), you will have SUAVE running locally, either in Docker or via the binaries themselves.

In either case, you can cause a series of transactions to occur by running:

```bash
go run suave/devenv/cmd/main.go
```

This script is focused on sending transactions to a local dev enviornment, but you can repurpose it easily for remote by changing `exNodeNetAddr`.

## Step-by-Step Breakdown

Below we will break down what this script is doing.

### Step 1: Create and Fund Test Accounts

```go
testAddr1 = generatePrivKey()
testAddr2 = generatePrivKey()

if err := fundAccount(mevmClt, testAddr1.Address(), fundBalance); err != nil {
return err
}
fmt.Printf("- Funded test account: %s (%s)\n", testAddr1.Address().Hex(), fundBalance.String())
```

- Generate two new private keys representing test accounts.
- Fund these test accounts with a specified amount of Ether to simulate transactions.

### Step 2: Deploy mev-share Contract

```go
// Code snippet related to the step
txnResult, err := sdk.DeployContract(mevShareArtifact.Code, mevmClt)
```

- Deploy the `mev-share` smart contract to the blockchain using the bytecode and a client instance.
- Confirm the deployment by checking the transaction receipt status.

### Step 3: Send Bid

```go
refundPercent := 10
bundle := &types.SBundle{
Txs: types.Transactions{ethTxn1},
RevertingHashes: []common.Hash{},
RefundPercent: &refundPercent,
}
bundleBytes, _ := json.Marshal(bundle)

targetBlock := uint64(1)
allowedPeekers := []common.Address{mevShareContract.Address()}

confidentialDataBytes, _ := bundleBidContract.Abi.Methods["fetchBidConfidentialBundleData"].Outputs.Pack(bundleBytes)

txnResult, err := mevShareContract.SendTransaction("newBid", []interface{}{targetBlock + 1, allowedPeekers, []common.Address{}}, confidentialDataBytes)
if err != nil {
return err
}
```

- Craft a bid using the `mev-share` contract function `newBid`.
- Serialize the bundle of transactions to include in the bid.
- Send the bid to the blockchain and validate its inclusion.

### Step 4: Send Backrun

```go
backRunBundle := &types.SBundle{
Txs: types.Transactions{ethTxnBackrun},
RevertingHashes: []common.Hash{},
}
backRunBundleBytes, _ := json.Marshal(backRunBundle)

confidentialDataMatchBytes, _ := bundleBidContract.Abi.Methods["fetchBidConfidentialBundleData"].Outputs.Pack(backRunBundleBytes)

// backrun inputs
targetBlock := uint64(1)
allowedPeekers := []common.Address{mevShareContract.Address()}

txnResult, err := mevShareContract.SendTransaction("newMatch", []interface{}{targetBlock + 1, allowedPeekers, []common.Address{}, bidId}, confidentialDataMatchBytes)
if err != nil {
return err
}
```

- Create a backrun transaction bundle that will be sent after the initial bid.
- Serialize the backrun bundle and use the `mev-share` contract's `newMatch` function to submit it.
- Verify the transaction receipt to ensure the backrun was successful.

#### Result
If all has been succesfully run you should see the following in your terminal:
```bash
suave-geth$ go run suave/devenv/cmd/main.go
Step 0: Create and fund test accounts
- Funded test account: 0x66d5a8D6B34329c0639071275b3d78D29e11EbC6 (100000000)
Step 1: Deploy mev-share contract
- Mev share contract deployed: 0x8f21Fdd6B4f4CacD33151777A46c122797c8BF17
Step 2: Send bid
- Bid sent at txn: 0xb49debcdead2b306d6ab6282b88fdad7c8d6a33d87df34b79f56d141eae7c08a
- Bid id: 30bbc65298f24e67aaf5c95bf5f0686c
Step 3: Send backrun
- Backrun sent at txn: 0xcf7880e61e94aaab48c60655c321716ecab6edab752586448b0412e93a969889
- Backrun bid id: db98b83d02694fc2b13c042ad22c233
```
112 changes: 112 additions & 0 deletions docs/how-to/interact-with-suave/fund-local-account.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
title: Fund Local Account
description: Fund Local Accounts for Local Development
---

This script will go through funding a local account using the golang SDK.

Inside [suave-geth](https://github.com/flashbots/suave-geth) create a new file in `suave/devenv/cmd` called `transactions.go`:

```go
package main

import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"os"

_ "embed"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/suave/sdk"
)

var (
// This is the address we used when starting the MEVM
exNodeEthAddr = common.HexToAddress("b5feafbdd752ad52afb7e1bd2e40432a485bbb7f")
exNodeNetAddr = "http://localhost:8545"
// This account is funded in both devenv networks
// address: 0xBE69d72ca5f88aCba033a063dF5DBe43a4148De0
fundedAccount = newPrivKeyFromHex(
"91ab9a7e53c220e6210460b65a7a3bb2ca181412a8a7b43ff336b3df1737ce12"
)
)

func main() {
rpcClient, _ := rpc.Dial(exNodeNetAddr)
// Use the SDK to create a new client by specifying the Eth Address of the MEVM
mevmClt := sdk.NewClient(rpcClient, fundedAccount.priv, exNodeEthAddr)

testAddr1 := generatePrivKey()

fundBalance := big.NewInt(100000000)
if err := fundAccount(mevmClt, testAddr1.Address(), fundBalance); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
return
}
fmt.Printf("Funded test account: %s (%s)\n", testAddr1.Address().Hex(), fundBalance.String())
}

func fundAccount(clt *sdk.Client, to common.Address, value *big.Int) error {
txn := &types.LegacyTx{
Value: value,
To: &to,
}
result, err := clt.SendTransaction(txn)
if err != nil {
return err
}
_, err = result.Wait()
if err != nil {
return err
}
// check balance
balance, err := clt.RPC().BalanceAt(context.Background(), to, nil)
if err != nil {
return err
}
if balance.Cmp(value) != 0 {
return fmt.Errorf("failed to fund account")
}
return nil
}

// General types and methods we need for the above to work as we want it to,
// nothing SUAVE specific
type privKey struct {
priv *ecdsa.PrivateKey
}

func (p *privKey) Address() common.Address {
return crypto.PubkeyToAddress(p.priv.PublicKey)
}

func newPrivKeyFromHex(hex string) *privKey {
key, err := crypto.HexToECDSA(hex)
if err != nil {
panic(fmt.Sprintf("failed to parse private key: %v", err))
}
return &privKey{priv: key}
}

func generatePrivKey() *privKey {
key, err := crypto.GenerateKey()
if err != nil {
panic(fmt.Sprintf("failed to generate private key: %v", err))
}
return &privKey{priv: key}
}
```

If you run the following from your terminal, you should now have one funded account:

```bash
go run suave/devenv/cmd/transactions.go
```

The important parts to note are the `SendTransaction` method, and the way the balance of a given account is fetched via the `RPC()` method, both available in the SDK. Using this pattern, you should be able to send most of the transactions you wish to, as well as fetch information about other accounts or transactions as is necessary.
Loading

0 comments on commit a8f1958

Please sign in to comment.