Skip to main content

Deploy a custom contract

Now, let's write our own simple contract that illustrates how to use confidential inputs. We'll also focus on how to to deploy custom contracts.

The following guides will provide more details about precompiles, the programming model, and the data flows we expect to see in SUAVE.

1. Write the contract

We have a number of example contracts already written in suave/sol/standard_peekers to give you inspiration.

Create a new file in that directory called OnlyConfidential.sol:

pragma solidity ^0.8.8;

import "../libraries/Suave.sol";

contract OnlyConfidential {

event SimResultEvent(
uint64 egp
);

function fetchBidConfidentialBundleData() public returns (bytes memory) {
require(Suave.isConfidential());

bytes memory confidentialInputs = Suave.confidentialInputs();
return abi.decode(confidentialInputs, (bytes));
}


// note: because of confidential execution,
// you will not see your input as input to the function
function helloWorld() external {
// 0. ensure confidential execution
require(Suave.isConfidential());

// 1. fetch bundle data
bytes memory bundleData = this.fetchBidConfidentialBundleData();

// 2. sim bundle and get effective gas price
uint64 effectiveGasPrice = Suave.simulateBundle(bundleData);

emit SimResultEvent(effectiveGasPrice);

// note: this function doesn't return anything
// so this computation result will never land onchain
}
}

This contract uses three new precompiles:

  1. isConfidential to ensure that only the MEVM(s) specified by the user can fetch the confidential data in transactions to this contract.
  2. confidentialInputs to fetch the confidential data that was submitted along with the transaction that the user sent which specified this contract.
  3. simulateBundle for no specific reason here other than to illustrate what you, as a contract creator, might want any MEVM allowed to see confidential data to do once they have fetched it.

2. Deploy locally

There are currently 3 steps required to deploy a custom contract.

  1. Generate the necessary artifacts. This can be done by running:
suave/scripts/contracts.sh build
  1. Extend the suave/e2e/contracts.go by adding to the var block which declares all the contracts:
OnlyConfidentialContract  = newArtifact("OnlyConfidential.sol/OnlyConfidential.json")
Make sure to capitalize OnlyConfidentialContract so that it is exported from this file. :::
  1. Edit the contract deployment code from the previous guide to look like this:
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 (
exNodeEthAddr = common.HexToAddress("b5feafbdd752ad52afb7e1bd2e40432a485bbb7f")
exNodeNetAddr = "http://localhost:8545"
fundedAccount = newPrivKeyFromHex("91ab9a7e53c220e6210460b65a7a3bb2ca181412a8a7b43ff336b3df1737ce12")
)

var (
onlyConfidentialArtifact = e2e.OnlyConfidentialContract
)

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

var onlyConfidentialContract *sdk.Contract
_ = onlyConfidentialContract

txnResult, err := sdk.DeployContract(onlyConfidentialArtifact.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)
onlyConfidentialContract = sdk.GetContract(receipt.ContractAddress, onlyConfidentialArtifact.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}
}

Run:

go run suave/devenv/cmd/deploy.go

And you should see the address of your custom contract printed in your terminal.

3. Deploy to Rigil

Follow the same steps as listed above, but point your deploy script at the Rigil RPC after getting some faucet funds.

Request faucet funds here.

deploy.go should work as is, just adapt this block:

var (
// Target the Rigil RPC
exNodeNetAddr = "https://rpc.rigil.suave.flashbots.net"
// Insert a private key you own with some SUAVE ETH
fundedAccount = newPrivKeyFromHex("<your_priv_key>")
// The public address of a Kettle on Rigil
exNodeEthAddr = common.HexToAddress("03493869959c866713c33669ca118e774a30a0e5")
)