Deploy a custom contract
This sub-section of guides will walk you through creating and deploying increasingly complex smart contracts.
We'll begin with a simple contract that illustrates how to use confidential inputs.
This focus of this first guide 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 test.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 view {
// 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. This will change!
- Use Remix or your other favourite tool to generate the abi and bytecode, then paste this into the
suave/artifacts
folder. - Extend the
suave/e2e/contracts.go
file with the name and location of your contract, ABI, and bytecode. - Edit the contract deployment code from the previous guide.
Using the contract above as an example, create a new folder called test.sol
in the suave/artifacts
directory. Create a file in you new folder called onlyConfidential.json
and structure it like this:
{
"abi": ["<copy_from_remix>"],
"deployedBytecode": "<copy_from_remix>",
"bytecode": "<same_as_deployedBytecode>"
}
Extend suave/e2e/contracts.go
by adding to the var
block which declares all the contracts:
OnlyConfidentialContract = newArtifact("test.sol/onlyConfidential.json")
Make sure to capitalize
OnlyConfidentialContract
so that it is exported from this file.
Edit suave/devenv/cmd/deploy.go
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")
)