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:
isConfidential
to ensure that only the MEVM(s) specified by the user can fetch the confidential data in transactions to this contract.confidentialInputs
to fetch the confidential data that was submitted along with the transaction that the user sent which specified this contract.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.
- Generate the necessary artifacts. This can be done by running:
suave/scripts/contracts.sh build
- Extend the
suave/e2e/contracts.go
by adding to thevar
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.
- 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")
)