Skip to content

Commit

Permalink
Sui support
Browse files Browse the repository at this point in the history
  • Loading branch information
skudasov committed Jan 16, 2025
1 parent 27b768e commit 045819a
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 31 deletions.
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- [EVM](framework/components/blockchains/evm.md)
- [Solana](framework/components/blockchains/solana.md)
- [Aptos](framework/components/blockchains/aptos.md)
- [Sui](framework/components/blockchains/sui.md)
- [Optimism Stack]()
- [Arbitrum Stack]()
- [Chainlink](framework/components/chainlink.md)
Expand Down
78 changes: 78 additions & 0 deletions book/src/framework/components/blockchains/sui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Sui Blockchain Client

API is available on [localhost:9000](http://localhost:9000)

## Configuration

```toml
[blockchain_a]
type = "sui"
image = "mysten/sui-tools:mainnet" # if omitted default is mysten/sui-tools:devnet
contracts_dir = "$your_dir"
```

## Usage

```golang
package examples

import (
"context"
"fmt"
"github.com/block-vision/sui-go-sdk/models"
"github.com/block-vision/sui-go-sdk/signer"
"github.com/block-vision/sui-go-sdk/sui"
"github.com/smartcontractkit/chainlink-testing-framework/framework"
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
"github.com/stretchr/testify/require"
"testing"
)

type CfgSui struct {
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
}

func TestSuiSmoke(t *testing.T) {
in, err := framework.Load[CfgSui](t)
require.NoError(t, err)

bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
require.NoError(t, err)

// network is already funded, here are the keys
_ = bc.NetworkSpecificData.SuiAccount.Mnemonic
_ = bc.NetworkSpecificData.SuiAccount.PublicBase64Key
_ = bc.NetworkSpecificData.SuiAccount.SuiAddress

// execute any additional commands, to deploy contracts or set up
_, err = framework.ExecContainer(bc.ContainerName, []string{"ls", "-lah"})
require.NoError(t, err)

t.Run("test something", func(t *testing.T) {
// use internal URL to connect Chainlink nodes
_ = bc.Nodes[0].DockerInternalHTTPUrl
// use host URL to interact
_ = bc.Nodes[0].HostHTTPUrl

cli := sui.NewSuiClient(bc.Nodes[0].HostHTTPUrl)

signerAccount, err := signer.NewSignertWithMnemonic(bc.NetworkSpecificData.SuiAccount.Mnemonic)
require.NoError(t, err)
rsp, err := cli.SuiXGetAllBalance(context.Background(), models.SuiXGetAllBalanceRequest{
Owner: signerAccount.Address,
})
require.NoError(t, err)
fmt.Printf("My funds: %v\n", rsp)
})
}
```

## Test Private Keys

Since Sui doesn't have official local development chain we are using real node and generating mnemonic at start then funding that account through internal faucet, see
```
// network is already funded, here are the keys
_ = bc.NetworkSpecificData.SuiAccount.Mnemonic
_ = bc.NetworkSpecificData.SuiAccount.PublicBase64Key
_ = bc.NetworkSpecificData.SuiAccount.SuiAddress
```
1 change: 1 addition & 0 deletions framework/.changeset/v0.4.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add Sui network support
18 changes: 9 additions & 9 deletions framework/components/blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ type Input struct {

// Output is a blockchain network output, ChainID and one or more nodes that forms the network
type Output struct {
UseCache bool `toml:"use_cache"`
Family string `toml:"family"`
ContainerName string `toml:"container_name"`
GeneratedData *GeneratedData `toml:"generated_data"`
Container testcontainers.Container `toml:"-"`
ChainID string `toml:"chain_id"`
Nodes []*Node `toml:"nodes"`
UseCache bool `toml:"use_cache"`
Family string `toml:"family"`
ContainerName string `toml:"container_name"`
NetworkSpecificData *NetworkSpecificData `toml:"network_specific_data"`
Container testcontainers.Container `toml:"-"`
ChainID string `toml:"chain_id"`
Nodes []*Node `toml:"nodes"`
}

type GeneratedData struct {
Mnemonic string `toml:"mnemonic"`
type NetworkSpecificData struct {
SuiAccount *SuiWalletInfo
}

// Node represents blockchain node output, URLs required for connection locally and inside docker network
Expand Down
46 changes: 29 additions & 17 deletions framework/components/blockchain/sui.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ import (
"time"
)

type SuiKeyInfo struct {
const (
DefaultFaucetPort = "9123/tcp"
DefaultFaucetPortNum = "9123"
DefaultSuiNodePort = "9000"
)

// SuiWalletInfo info about Sui account/wallet
type SuiWalletInfo struct {
Alias *string `json:"alias"` // Alias key name, usually "null"
Flag int `json:"flag"` // Flag is an integer
KeyScheme string `json:"keyScheme"` // Key scheme is a string
Expand All @@ -25,8 +32,11 @@ type SuiKeyInfo struct {
SuiAddress string `json:"suiAddress"` // Sui address is a 0x prefixed hex string
}

// funds provided key using local faucet
// we can't use the best client available - block-vision/sui-go-sdk for that, since some versions have old API and it is hardcoded
// https://github.com/block-vision/sui-go-sdk/blob/main/sui/faucet_api.go#L16
func fundAccount(url string, address string) error {
r := resty.New().SetBaseURL(url).EnableTrace().SetDebug(true)
r := resty.New().SetBaseURL(url)
b := &models.FaucetRequest{
FixedAmountRequest: &models.FaucetFixedAmountRequest{
Recipient: address,
Expand All @@ -40,18 +50,21 @@ func fundAccount(url string, address string) error {
return nil
}

func generateKeyData(containerName string, keyCipherType string) (*SuiKeyInfo, error) {
// generateKeyData generates a wallet and returns all the data
func generateKeyData(containerName string, keyCipherType string) (*SuiWalletInfo, error) {
cmdStr := []string{"sui", "keytool", "generate", keyCipherType, "--json"}
keyOut, err := framework.ExecContainer(containerName, cmdStr)
if err != nil {
return nil, err
}
// formatted JSON with, no plain --json version, remove special symbols
cleanKey := strings.ReplaceAll(keyOut, "\x00", "")
cleanKey = strings.ReplaceAll(cleanKey, "\x01", "")
cleanKey = strings.ReplaceAll(cleanKey, "\x02", "")
cleanKey = strings.ReplaceAll(cleanKey, "\n", "")
var key *SuiKeyInfo
if err := json.Unmarshal([]byte("{"+cleanKey[2:]), &key); err != nil {
cleanKey = "{" + cleanKey[2:]
var key *SuiWalletInfo
if err := json.Unmarshal([]byte(cleanKey), &key); err != nil {
return nil, err
}
framework.L.Info().Interface("Key", key).Msg("Test key")
Expand All @@ -63,9 +76,9 @@ func defaultSui(in *Input) {
in.Image = "mysten/sui-tools:devnet"
}
if in.Port != "" {
framework.L.Warn().Msg("'port' field is set but only default port can be used: 9000")
framework.L.Warn().Msgf("'port' field is set but only default port can be used: %s", DefaultSuiNodePort)
}
in.Port = "9000"
in.Port = DefaultSuiNodePort
}

func newSui(in *Input) (*Output, error) {
Expand All @@ -82,15 +95,15 @@ func newSui(in *Input) (*Output, error) {

req := testcontainers.ContainerRequest{
Image: in.Image,
ExposedPorts: []string{in.Port, "9123/tcp"},
ExposedPorts: []string{in.Port, DefaultFaucetPort},
Name: containerName,
Labels: framework.DefaultTCLabels(),
Networks: []string{framework.DefaultNetworkName},
NetworkAliases: map[string][]string{
framework.DefaultNetworkName: {containerName},
},
HostConfigModifier: func(h *container.HostConfig) {
h.PortBindings = framework.MapTheSamePort(bindPort, "9123/tcp")
h.PortBindings = framework.MapTheSamePort(bindPort, DefaultFaucetPort)
},
ImagePlatform: "linux/amd64",
Env: map[string]string{
Expand All @@ -109,7 +122,7 @@ func newSui(in *Input) (*Output, error) {
},
},
// we need faucet for funding
WaitingFor: wait.ForListeningPort("9123/tcp").WithStartupTimeout(10 * time.Second).WithPollInterval(200 * time.Millisecond),
WaitingFor: wait.ForListeningPort(DefaultFaucetPort).WithStartupTimeout(10 * time.Second).WithPollInterval(200 * time.Millisecond),
}

c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
Expand All @@ -123,19 +136,18 @@ func newSui(in *Input) (*Output, error) {
if err != nil {
return nil, err
}
keyData, err := generateKeyData(containerName, "ed25519")
suiAccount, err := generateKeyData(containerName, "ed25519")
if err != nil {
return nil, err
}
err = fundAccount(fmt.Sprintf("http://%s:%s", "127.0.0.1", "9123"), keyData.SuiAddress)
if err != nil {
if err := fundAccount(fmt.Sprintf("http://%s:%s", "127.0.0.1", DefaultFaucetPortNum), suiAccount.SuiAddress); err != nil {
return nil, err
}
return &Output{
UseCache: true,
Family: "sui",
ContainerName: containerName,
GeneratedData: &GeneratedData{Mnemonic: keyData.Mnemonic},
UseCache: true,
Family: "sui",
ContainerName: containerName,
NetworkSpecificData: &NetworkSpecificData{SuiAccount: suiAccount},
Nodes: []*Node{
{
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, in.Port),
Expand Down
1 change: 1 addition & 0 deletions framework/examples/myproject/smoke_sui.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[blockchain_a]
type = "sui"
image = "mysten/sui-tools:mainnet"
13 changes: 9 additions & 4 deletions framework/examples/myproject/smoke_sui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ func TestSuiSmoke(t *testing.T) {
bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
require.NoError(t, err)

// execute any additional commands, to deploy contracts or set up
// network is already funded, here are the keys
_ = bc.GeneratedData.Mnemonic
_ = bc.NetworkSpecificData.SuiAccount.Mnemonic
_ = bc.NetworkSpecificData.SuiAccount.PublicBase64Key
_ = bc.NetworkSpecificData.SuiAccount.SuiAddress

// execute any additional commands, to deploy contracts or set up
_, err = framework.ExecContainer(bc.ContainerName, []string{"ls", "-lah"})
require.NoError(t, err)

Expand All @@ -35,14 +37,17 @@ func TestSuiSmoke(t *testing.T) {
_ = bc.Nodes[0].DockerInternalHTTPUrl
// use host URL to interact
_ = bc.Nodes[0].HostHTTPUrl
cli := sui.NewSuiClient("http://localhost:9000")

signerAccount, err := signer.NewSignertWithMnemonic(bc.GeneratedData.Mnemonic)
cli := sui.NewSuiClient(bc.Nodes[0].HostHTTPUrl)

signerAccount, err := signer.NewSignertWithMnemonic(bc.NetworkSpecificData.SuiAccount.Mnemonic)
require.NoError(t, err)
rsp, err := cli.SuiXGetAllBalance(context.Background(), models.SuiXGetAllBalanceRequest{
Owner: signerAccount.Address,
})
require.NoError(t, err)
fmt.Printf("My funds: %v\n", rsp)
fmt.Printf("url1: %s\n", bc.Nodes[0].HostHTTPUrl)
fmt.Printf("url2: %s\n", bc.Nodes[0].DockerInternalHTTPUrl)
})
}
4 changes: 4 additions & 0 deletions framework/examples/myproject_cll/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.33.0 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/block-vision/sui-go-sdk v1.0.6 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
Expand Down Expand Up @@ -100,6 +101,9 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/testcontainers/testcontainers-go v0.35.0 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
Expand Down
8 changes: 8 additions & 0 deletions framework/examples/myproject_cll/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/block-vision/sui-go-sdk v1.0.6 h1:FysCc4TJC8v4BEBbCjJPDR4iR5eKqJT1dxGwsT67etg=
github.com/block-vision/sui-go-sdk v1.0.6/go.mod h1:FyK1vGE8lWm9QA1fdQpf1agfXQSMbPT8AV1BICgx6d8=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
Expand Down Expand Up @@ -282,6 +284,12 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
Expand Down
2 changes: 1 addition & 1 deletion framework/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ replace github.com/smartcontractkit/chainlink-testing-framework/seth => ../seth
require (
github.com/aws/aws-sdk-go-v2/config v1.27.39
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3
github.com/block-vision/sui-go-sdk v1.0.6
github.com/charmbracelet/huh v0.6.0
github.com/charmbracelet/huh/spinner v0.0.0-20241028115900-20a4d21717a8
github.com/davecgh/go-spew v1.1.1
Expand Down Expand Up @@ -48,7 +49,6 @@ require (
github.com/aws/smithy-go v1.21.0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/block-vision/sui-go-sdk v1.0.6 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/bytedance/sonic v1.12.3 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
Expand Down

0 comments on commit 045819a

Please sign in to comment.