Skip to content

Commit

Permalink
[client] Add some basic testing and Network
Browse files Browse the repository at this point in the history
- Adds basic network configurations for known networks
- Adds a full e2e test of transaction submission and checking
- Merges RestClient and Faucet client like the TS SDK
  • Loading branch information
gregnazario committed May 1, 2024
1 parent 83fd6a3 commit fce2886
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 0 deletions.
125 changes: 125 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,45 @@ import (
// For Content-Type header
const APTOS_SIGNED_BCS = "application/x.aptos.signed_transaction+bcs"

const (
localnet = "localnet"
devnet = "devnet"
testnet = "testnet"
mainnet = "mainnet"
)

const (
localnet_api = "http://localhost:8080/v1"
devnet_api = "https://api.devnet.aptoslabs.com/v1"
testnet_api = "https://api.testnet.aptoslabs.com/v1"
mainnet_api = "https://api.mainnet.aptoslabs.com/v1"
)

const (
localnet_faucet = "http://localhost:8081/v1"
devnet_faucet = "https://faucet.devnet.aptoslabs.com/"
testnet_faucet = "https://faucet.testnet.aptoslabs.com/"
)

const (
localnet_chain_id = 4
testnet_chain_id = 2
mainnet_chain_id = 1
)

type NetworkConfig struct {
network *string
api *string
faucet *string
indexer *string
}

type Client struct {
restClient RestClient
faucetClient FaucetClient
// TODO: Add indexer client
}

// TODO: rename 'NodeClient' (vs IndexerClient) ?
// what looks best for `import aptos "github.com/aptoslabs/aptos-go-sdk"` then aptos.NewClient() ?
type RestClient struct {
Expand All @@ -29,6 +68,92 @@ type RestClient struct {
baseUrl url.URL
}

func NewNetworkClient(config NetworkConfig) (client *Client, err error) {
var apiUrl *url.URL = nil

switch {
case config.api == nil && config.network == nil:
err = errors.New("aptos api url or network is required")
return
case config.api != nil:
apiUrl, err = url.Parse(*config.api)
if err != nil {
return
}
case *config.network == localnet:
apiUrl, err = url.Parse(localnet_api)
if err != nil {
return
}
case *config.network == devnet:
apiUrl, err = url.Parse(devnet_api)
if err != nil {
return
}
case *config.network == testnet:
apiUrl, err = url.Parse(testnet_api)
if err != nil {
return
}
case *config.network == mainnet:
apiUrl, err = url.Parse(mainnet_api)
if err != nil {
return
}
default:
err = errors.New("network name is unknown, please put localnet, devnet, testnet, or mainnet")
return
}
var faucetUrl *url.URL = nil

switch {
case config.faucet == nil && config.network == nil:
err = errors.New("aptos faucet url or network is required")
return
case config.faucet != nil:
faucetUrl, err = url.Parse(*config.faucet)
if err != nil {
return
}
case *config.network == localnet:
faucetUrl, err = url.Parse(localnet_faucet)
if err != nil {
return
}
case *config.network == devnet:
faucetUrl, err = url.Parse(devnet_faucet)
if err != nil {
return
}
case *config.network == testnet:
faucetUrl, err = url.Parse(testnet_faucet)
if err != nil {
return
}
case *config.network == mainnet:
faucetUrl = nil
default:
err = errors.New("network name is unknown, please put localnet, devnet, testnet, or mainnet")
return
}

// TODO: add indexer

restClient := new(RestClient)
restClient.baseUrl = *apiUrl
restClient.client.Timeout = 60 * time.Second // TODO: Make configurable
faucetClient := &FaucetClient{
restClient,
faucetUrl,
}
client = &Client{
*restClient,
*faucetClient,
}
return
}

// TODO: Deprecate
func NewClient(baseUrl string) (rc *RestClient, err error) {
rc = new(RestClient)
tu, err := url.Parse(baseUrl)
Expand Down
62 changes: 62 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package aptos

import (
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)

func Test_Flow(t *testing.T) {
// Create a client
network := devnet
config := NetworkConfig{
network: &network,
}
client, err := NewNetworkClient(config)
assert.NoError(t, err)

// Verify chain id retrieval works
chainId, err := client.restClient.GetChainId()
assert.NoError(t, err)
assert.Less(t, uint8(4), chainId)

// Create an account
account, err := NewAccount()
assert.NoError(t, err)

// Fund the account with 1 APT
client.faucetClient.Fund(account.Address, 100_000_000)

// Send money to 0x1
// Build transaction
signed_txn, err := TransferTransaction(&client.restClient, account, Account0x1, 100)
assert.NoError(t, err)

// Send transaction
// TODO: verify response
result, err := client.restClient.SubmitTransaction(signed_txn)
assert.NoError(t, err)

hash := result["hash"].(string)

// TODO Wait on transaction
err = client.restClient.WaitForTransactions([]string{hash})
assert.NoError(t, err)

// Read transaction by hash
txn, err := client.restClient.TransactionByHash(hash)
assert.NoError(t, err)

// Read transaction by version
versionString := txn["version"].(string)

// Convert string version to uint64
version, err := strconv.ParseUint(versionString, 10, 64)
assert.NoError(t, err)

// Load the transaction again
txnByVersion, err := client.restClient.TransactionByVersion(version)

// Assert that both are the same
assert.Equal(t, txn, txnByVersion)
}
34 changes: 34 additions & 0 deletions faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,40 @@ func truthy(x any) bool {
}
}

type FaucetClient struct {
restClient *RestClient
url *url.URL
}

func (faucetClient *FaucetClient) Fund(address AccountAddress, amount uint64) error {
mintUrl := faucetClient.url
mintUrl.Path = path.Join(mintUrl.Path, "mint")
params := url.Values{}
params.Set("amount", strconv.FormatUint(amount, 10))
params.Set("address", address.String())
mintUrl.RawQuery = params.Encode()
response, err := http.Post(mintUrl.String(), "text/plain", nil)
if err != nil {
return err
}
if response.StatusCode >= 400 {
return NewHttpError(response)
}
dec := json.NewDecoder(response.Body)
var txnHashes []string
err = dec.Decode(&txnHashes)
if err != nil {
return fmt.Errorf("response json decode error, %w", err)
}
if faucetClient.restClient == nil {
slog.Debug("FundAccount no txns to wait for")
// no Aptos client to wait on txn completion
return nil
}
slog.Debug("FundAccount wait for txns", "ntxn", len(txnHashes))
return faucetClient.restClient.WaitForTransactions(txnHashes)
}

// Ask the faucet to send some money to a test account
func FundAccount(rc *RestClient, faucetUrl string, address AccountAddress, amount uint64) error {
au, err := url.Parse(faucetUrl)
Expand Down

0 comments on commit fce2886

Please sign in to comment.