Skip to content

Commit 38ed076

Browse files
authored
Merge branch 'main' into TT-1907-update-AWSSecretsManager
2 parents dcbc216 + 4380c2a commit 38ed076

File tree

16 files changed

+352
-7
lines changed

16 files changed

+352
-7
lines changed

.github/workflows/framework-golden-tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ jobs:
2222
config: smoke.toml
2323
count: 1
2424
timeout: 10m
25+
- name: TestSuiSmoke
26+
config: smoke_sui.toml
27+
count: 1
28+
timeout: 10m
2529
- name: TestAptosSmoke
2630
config: smoke_aptos.toml
2731
count: 1

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
- [EVM](framework/components/blockchains/evm.md)
3939
- [Solana](framework/components/blockchains/solana.md)
4040
- [Aptos](framework/components/blockchains/aptos.md)
41+
- [Sui](framework/components/blockchains/sui.md)
4142
- [Optimism Stack]()
4243
- [Arbitrum Stack]()
4344
- [Chainlink](framework/components/chainlink.md)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Sui Blockchain Client
2+
3+
API is available on [localhost:9000](http://localhost:9000)
4+
5+
## Configuration
6+
7+
```toml
8+
[blockchain_a]
9+
type = "sui"
10+
image = "mysten/sui-tools:mainnet" # if omitted default is mysten/sui-tools:devnet
11+
contracts_dir = "$your_dir"
12+
```
13+
14+
## Usage
15+
16+
```golang
17+
package examples
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"github.com/block-vision/sui-go-sdk/models"
23+
"github.com/block-vision/sui-go-sdk/signer"
24+
"github.com/block-vision/sui-go-sdk/sui"
25+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
26+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
27+
"github.com/stretchr/testify/require"
28+
"testing"
29+
)
30+
31+
type CfgSui struct {
32+
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
33+
}
34+
35+
func TestSuiSmoke(t *testing.T) {
36+
in, err := framework.Load[CfgSui](t)
37+
require.NoError(t, err)
38+
39+
bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
40+
require.NoError(t, err)
41+
42+
// network is already funded, here are the keys
43+
_ = bc.NetworkSpecificData.SuiAccount.Mnemonic
44+
_ = bc.NetworkSpecificData.SuiAccount.PublicBase64Key
45+
_ = bc.NetworkSpecificData.SuiAccount.SuiAddress
46+
47+
// execute any additional commands, to deploy contracts or set up
48+
_, err = framework.ExecContainer(bc.ContainerName, []string{"ls", "-lah"})
49+
require.NoError(t, err)
50+
51+
t.Run("test something", func(t *testing.T) {
52+
// use internal URL to connect Chainlink nodes
53+
_ = bc.Nodes[0].DockerInternalHTTPUrl
54+
// use host URL to interact
55+
_ = bc.Nodes[0].HostHTTPUrl
56+
57+
cli := sui.NewSuiClient(bc.Nodes[0].HostHTTPUrl)
58+
59+
signerAccount, err := signer.NewSignertWithMnemonic(bc.NetworkSpecificData.SuiAccount.Mnemonic)
60+
require.NoError(t, err)
61+
rsp, err := cli.SuiXGetAllBalance(context.Background(), models.SuiXGetAllBalanceRequest{
62+
Owner: signerAccount.Address,
63+
})
64+
require.NoError(t, err)
65+
fmt.Printf("My funds: %v\n", rsp)
66+
})
67+
}
68+
```
69+
70+
## Test Private Keys
71+
72+
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
73+
```
74+
// network is already funded, here are the keys
75+
_ = bc.NetworkSpecificData.SuiAccount.Mnemonic
76+
_ = bc.NetworkSpecificData.SuiAccount.PublicBase64Key
77+
_ = bc.NetworkSpecificData.SuiAccount.SuiAddress
78+
```

framework/.changeset/v0.4.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add Sui network support

framework/components/blockchain/blockchain.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
// Input is a blockchain network configuration params
99
type Input struct {
1010
// Common EVM fields
11-
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana aptos" envconfig:"net_type"`
11+
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana aptos sui" envconfig:"net_type"`
1212
Image string `toml:"image"`
1313
PullImage bool `toml:"pull_image"`
1414
Port string `toml:"port"`
@@ -30,12 +30,17 @@ type Input struct {
3030

3131
// Output is a blockchain network output, ChainID and one or more nodes that forms the network
3232
type Output struct {
33-
UseCache bool `toml:"use_cache"`
34-
Family string `toml:"family"`
35-
ContainerName string `toml:"container_name"`
36-
Container testcontainers.Container `toml:"-"`
37-
ChainID string `toml:"chain_id"`
38-
Nodes []*Node `toml:"nodes"`
33+
UseCache bool `toml:"use_cache"`
34+
Family string `toml:"family"`
35+
ContainerName string `toml:"container_name"`
36+
NetworkSpecificData *NetworkSpecificData `toml:"network_specific_data"`
37+
Container testcontainers.Container `toml:"-"`
38+
ChainID string `toml:"chain_id"`
39+
Nodes []*Node `toml:"nodes"`
40+
}
41+
42+
type NetworkSpecificData struct {
43+
SuiAccount *SuiWalletInfo
3944
}
4045

4146
// Node represents blockchain node output, URLs required for connection locally and inside docker network
@@ -64,6 +69,8 @@ func NewBlockchainNetwork(in *Input) (*Output, error) {
6469
out, err = newSolana(in)
6570
case "aptos":
6671
out, err = newAptos(in)
72+
case "sui":
73+
out, err = newSui(in)
6774
default:
6875
return nil, fmt.Errorf("blockchain type is not supported or empty, must be 'anvil' or 'geth'")
6976
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package blockchain
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/block-vision/sui-go-sdk/models"
8+
"github.com/docker/docker/api/types/container"
9+
"github.com/go-resty/resty/v2"
10+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
11+
"github.com/testcontainers/testcontainers-go"
12+
"github.com/testcontainers/testcontainers-go/wait"
13+
"path/filepath"
14+
"strings"
15+
"time"
16+
)
17+
18+
const (
19+
DefaultFaucetPort = "9123/tcp"
20+
DefaultFaucetPortNum = "9123"
21+
DefaultSuiNodePort = "9000"
22+
)
23+
24+
// SuiWalletInfo info about Sui account/wallet
25+
type SuiWalletInfo struct {
26+
Alias *string `json:"alias"` // Alias key name, usually "null"
27+
Flag int `json:"flag"` // Flag is an integer
28+
KeyScheme string `json:"keyScheme"` // Key scheme is a string
29+
Mnemonic string `json:"mnemonic"` // Mnemonic is a string
30+
PeerId string `json:"peerId"` // Peer ID is a string
31+
PublicBase64Key string `json:"publicBase64Key"` // Public key in Base64 format
32+
SuiAddress string `json:"suiAddress"` // Sui address is a 0x prefixed hex string
33+
}
34+
35+
// funds provided key using local faucet
36+
// 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
37+
// https://github.com/block-vision/sui-go-sdk/blob/main/sui/faucet_api.go#L16
38+
func fundAccount(url string, address string) error {
39+
r := resty.New().SetBaseURL(url)
40+
b := &models.FaucetRequest{
41+
FixedAmountRequest: &models.FaucetFixedAmountRequest{
42+
Recipient: address,
43+
},
44+
}
45+
resp, err := r.R().SetBody(b).SetHeader("Content-Type", "application/json").Post("/gas")
46+
if err != nil {
47+
return err
48+
}
49+
framework.L.Info().Any("Resp", resp).Msg("Address is funded!")
50+
return nil
51+
}
52+
53+
// generateKeyData generates a wallet and returns all the data
54+
func generateKeyData(containerName string, keyCipherType string) (*SuiWalletInfo, error) {
55+
cmdStr := []string{"sui", "keytool", "generate", keyCipherType, "--json"}
56+
keyOut, err := framework.ExecContainer(containerName, cmdStr)
57+
if err != nil {
58+
return nil, err
59+
}
60+
// formatted JSON with, no plain --json version, remove special symbols
61+
cleanKey := strings.ReplaceAll(keyOut, "\x00", "")
62+
cleanKey = strings.ReplaceAll(cleanKey, "\x01", "")
63+
cleanKey = strings.ReplaceAll(cleanKey, "\x02", "")
64+
cleanKey = strings.ReplaceAll(cleanKey, "\n", "")
65+
cleanKey = "{" + cleanKey[2:]
66+
var key *SuiWalletInfo
67+
if err := json.Unmarshal([]byte(cleanKey), &key); err != nil {
68+
return nil, err
69+
}
70+
framework.L.Info().Interface("Key", key).Msg("Test key")
71+
return key, nil
72+
}
73+
74+
func defaultSui(in *Input) {
75+
if in.Image == "" {
76+
in.Image = "mysten/sui-tools:devnet"
77+
}
78+
if in.Port != "" {
79+
framework.L.Warn().Msgf("'port' field is set but only default port can be used: %s", DefaultSuiNodePort)
80+
}
81+
in.Port = DefaultSuiNodePort
82+
}
83+
84+
func newSui(in *Input) (*Output, error) {
85+
defaultSui(in)
86+
ctx := context.Background()
87+
containerName := framework.DefaultTCName("blockchain-node")
88+
89+
absPath, err := filepath.Abs(in.ContractsDir)
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
bindPort := fmt.Sprintf("%s/tcp", in.Port)
95+
96+
req := testcontainers.ContainerRequest{
97+
Image: in.Image,
98+
ExposedPorts: []string{in.Port, DefaultFaucetPort},
99+
Name: containerName,
100+
Labels: framework.DefaultTCLabels(),
101+
Networks: []string{framework.DefaultNetworkName},
102+
NetworkAliases: map[string][]string{
103+
framework.DefaultNetworkName: {containerName},
104+
},
105+
HostConfigModifier: func(h *container.HostConfig) {
106+
h.PortBindings = framework.MapTheSamePort(bindPort, DefaultFaucetPort)
107+
},
108+
ImagePlatform: "linux/amd64",
109+
Env: map[string]string{
110+
"RUST_LOG": "off,sui_node=info",
111+
},
112+
Cmd: []string{
113+
"sui",
114+
"start",
115+
"--force-regenesis",
116+
"--with-faucet",
117+
},
118+
Files: []testcontainers.ContainerFile{
119+
{
120+
HostFilePath: absPath,
121+
ContainerFilePath: "/",
122+
},
123+
},
124+
// we need faucet for funding
125+
WaitingFor: wait.ForListeningPort(DefaultFaucetPort).WithStartupTimeout(10 * time.Second).WithPollInterval(200 * time.Millisecond),
126+
}
127+
128+
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
129+
ContainerRequest: req,
130+
Started: true,
131+
})
132+
if err != nil {
133+
return nil, err
134+
}
135+
host, err := c.Host(ctx)
136+
if err != nil {
137+
return nil, err
138+
}
139+
suiAccount, err := generateKeyData(containerName, "ed25519")
140+
if err != nil {
141+
return nil, err
142+
}
143+
if err := fundAccount(fmt.Sprintf("http://%s:%s", "127.0.0.1", DefaultFaucetPortNum), suiAccount.SuiAddress); err != nil {
144+
return nil, err
145+
}
146+
return &Output{
147+
UseCache: true,
148+
Family: "sui",
149+
ContainerName: containerName,
150+
NetworkSpecificData: &NetworkSpecificData{SuiAccount: suiAccount},
151+
Nodes: []*Node{
152+
{
153+
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, in.Port),
154+
DockerInternalHTTPUrl: fmt.Sprintf("http://%s:%s", containerName, in.Port),
155+
},
156+
},
157+
}, nil
158+
}

framework/examples/myproject/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ replace (
1010
)
1111

1212
require (
13+
github.com/block-vision/sui-go-sdk v1.0.6
1314
github.com/blocto/solana-go-sdk v1.30.0
1415
github.com/ethereum/go-ethereum v1.14.11
1516
github.com/go-resty/resty/v2 v2.15.3
@@ -221,9 +222,13 @@ require (
221222
github.com/spf13/pflag v1.0.5 // indirect
222223
github.com/stretchr/objx v0.5.2 // indirect
223224
github.com/supranational/blst v0.3.13 // indirect
225+
github.com/tidwall/gjson v1.14.4 // indirect
226+
github.com/tidwall/match v1.1.1 // indirect
227+
github.com/tidwall/pretty v1.2.0 // indirect
224228
github.com/tklauser/go-sysconf v0.3.12 // indirect
225229
github.com/tklauser/numcpus v0.6.1 // indirect
226230
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
231+
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
227232
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
228233
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
229234
github.com/ugorji/go/codec v1.2.12 // indirect

framework/examples/myproject/go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
157157
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
158158
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
159159
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
160+
github.com/block-vision/sui-go-sdk v1.0.6 h1:FysCc4TJC8v4BEBbCjJPDR4iR5eKqJT1dxGwsT67etg=
161+
github.com/block-vision/sui-go-sdk v1.0.6/go.mod h1:FyK1vGE8lWm9QA1fdQpf1agfXQSMbPT8AV1BICgx6d8=
160162
github.com/blocto/solana-go-sdk v1.30.0 h1:GEh4GDjYk1lMhV/hqJDCyuDeCuc5dianbN33yxL88NU=
161163
github.com/blocto/solana-go-sdk v1.30.0/go.mod h1:Xoyhhb3hrGpEQ5rJps5a3OgMwDpmEhrd9bgzFKkkwMs=
162164
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
@@ -899,7 +901,13 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70
899901
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
900902
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
901903
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
904+
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
905+
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
906+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
907+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
902908
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
909+
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
910+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
903911
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
904912
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
905913
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[blockchain_a]
2+
type = "sui"
3+
image = "mysten/sui-tools:mainnet"

0 commit comments

Comments
 (0)