Skip to content

Commit

Permalink
add associated token account resource
Browse files Browse the repository at this point in the history
  • Loading branch information
callensm committed Dec 20, 2021
1 parent 3de4098 commit 10c65ab
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 90 deletions.
32 changes: 32 additions & 0 deletions docs/resources/associated_token_account.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "solana_associated_token_account Resource - terraform-provider-solana"
subcategory: ""
description: |-
Create a new associated token account for a specified wallet address.
---

# solana_associated_token_account (Resource)

Create a new associated token account for a specified wallet address.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- **owner** (String) Public key of the wallet to create the token account for as a base-58 encoded string.
- **token_mint** (String) The token mint for the associated token account as a base-58 encoded string.

### Optional

- **id** (String) The ID of this resource.
- **payer** (String) Public key of the transaction payer as a base-58 encoded string.

### Read-Only

- **public_key** (String) The base-58 encoded string for the new token account's public key.


6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module github.com/callensm/terraform-provider-solana
go 1.16

require (
github.com/gagliardetto/solana-go v1.0.0
github.com/hashicorp/terraform-plugin-docs v0.4.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0
github.com/gagliardetto/solana-go v1.0.2
github.com/hashicorp/terraform-plugin-docs v0.5.1
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
github.com/stretchr/testify v1.7.0
)
128 changes: 74 additions & 54 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/solana/data_source_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func dataSourceAccountRead(d *schema.ResourceData, meta interface{}) error {
if encoding == solana.EncodingJSONParsed {
d.Set("data", string(res.Value.Data.GetRawJSON()))
} else {
d.Set("data", res.Value.Data.GetBytes().String())
d.Set("data", string(res.Value.Data.GetBinary()))
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion internal/solana/data_source_address_signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func dataSourceAddressSignaturesRead(d *schema.ResourceData, meta interface{}) e
r := map[string]interface{}{
"signature": sig.Signature.String(),
"slot": uint64(sig.Slot),
"block_time": int64(sig.BlockTime),
"block_time": int64(sig.BlockTime.Time().Unix()),
}

if sig.Memo != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/solana/data_source_rent_exemption_cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func dataSourceRentExemptionCost() *schema.Resource {
func dataSourceRentExemptionCostRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*providerConfig).rpcClient

dataLength := d.Get("data_length").(int)
dataLength := d.Get("data_length").(uint64)
res, err := client.GetMinimumBalanceForRentExemption(context.Background(), dataLength, rpc.CommitmentRecent)
if err != nil {
return err
Expand Down
13 changes: 1 addition & 12 deletions internal/solana/data_source_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,6 @@ func dataSourceTransactionRead(d *schema.ResourceData, meta interface{}) error {

d.SetId(fmt.Sprintf("transaction:%s_%d", sig.String(), res.Slot))
d.Set("slot", uint64(res.Slot))
d.Set("block_time", int64(res.BlockTime))
d.Set("block_time", int64(res.BlockTime.Time().Unix()))
return nil
}

func validateTransactionDataEncoding(val interface{}, k string) (warnings []string, errs []error) {
for _, encoding := range dataEncodingOptions {
if string(encoding) == val.(string) {
return
}
}

errs = append(errs, fmt.Errorf("transaction data encoding input (%s) is not a valid option", val.(string)))
return
}
17 changes: 3 additions & 14 deletions internal/solana/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package solana

import (
"errors"
"fmt"

"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
Expand Down Expand Up @@ -50,7 +49,8 @@ func New() *schema.Provider {
},

ResourcesMap: map[string]*schema.Resource{
"solana_random_keypair": resourceRandomKeypair(),
"solana_associated_token_account": resourceAssociatedTokenAccount(),
"solana_random_keypair": resourceRandomKeypair(),
},

DataSourcesMap: map[string]*schema.Resource{
Expand Down Expand Up @@ -85,17 +85,6 @@ func initializeProvider(d *schema.ResourceData) (interface{}, error) {
}

return &providerConfig{
rpcClient: rpc.NewClient(endpoint),
rpcClient: rpc.New(endpoint),
}, nil
}

func validateProviderClusterName(val interface{}, k string) (warnings []string, errs []error) {
for _, name := range clusterNameOptions {
if name == val.(string) {
return
}
}

errs = append(errs, fmt.Errorf("you received cluster name (%s) is not a valid option", val.(string)))
return
}
185 changes: 185 additions & 0 deletions internal/solana/resource_associated_token_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package solana

import (
"context"
"fmt"

"github.com/gagliardetto/solana-go"
associatedtokenaccount "github.com/gagliardetto/solana-go/programs/associated-token-account"
token "github.com/gagliardetto/solana-go/programs/token"
"github.com/gagliardetto/solana-go/rpc"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceAssociatedTokenAccount() *schema.Resource {
return &schema.Resource{
Description: "Create a new associated token account for a specified wallet address.",

Create: resourceAssociatedTokenAccountCreate,
Read: resourceAssociatedTokenAccountRead,
Delete: resourceAssociatedTokenAccountDelete,

Schema: map[string]*schema.Schema{
"owner": {
Type: schema.TypeString,
Description: "Public key of the wallet to create the token account for as a base-58 encoded string.",
Required: true,
ForceNew: true,
},
"token_mint": {
Type: schema.TypeString,
Description: "The token mint for the associated token account as a base-58 encoded string.",
Required: true,
ForceNew: true,
},
"payer": {
Type: schema.TypeString,
Description: "Public key of the transaction payer as a base-58 encoded string.",
Optional: true,
ForceNew: true,
},
"public_key": {
Type: schema.TypeString,
Description: "The base-58 encoded string for the new token account's public key.",
Computed: true,
},
},
}
}

func resourceAssociatedTokenAccountCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*providerConfig).rpcClient

owner, err := solana.PublicKeyFromBase58(d.Get("owner").(string))
if err != nil {
return err
}

tokenMint, err := solana.PublicKeyFromBase58(d.Get("token_mint").(string))
if err != nil {
return err
}

payer := owner
if p, ok := d.GetOk("payer"); ok {
pubkey, err := solana.PublicKeyFromBase58(p.(string))
if err != nil {
return err
}

payer = pubkey
}

inst := associatedtokenaccount.NewCreateInstruction(payer, owner, tokenMint)
recentHash, err := client.GetRecentBlockhash(context.Background(), rpc.CommitmentConfirmed)
if err != nil {
return err
}

tx, err := solana.NewTransaction([]solana.Instruction{inst.Build()}, recentHash.Value.Blockhash)
if err != nil {
return err
}

_, err = client.SendTransactionWithOpts(context.Background(), tx, false, rpc.CommitmentConfirmed)
if err != nil {
return err
}

res, err := client.GetTokenAccountsByOwner(
context.Background(),
owner,
&rpc.GetTokenAccountsConfig{
Mint: &tokenMint,
ProgramId: &associatedtokenaccount.ProgramID,
},
&rpc.GetTokenAccountsOpts{
Commitment: rpc.CommitmentConfirmed,
},
)
if err != nil {
return err
} else if len(res.Value) == 0 {
return fmt.Errorf("the new associated token account was not found in the confirmed accounts")
}

tokenAccountPubkey := res.Value[0].Pubkey.String()

d.SetId(fmt.Sprintf("associated_token_account:%s", tokenAccountPubkey))
d.Set("payer", payer.String())
d.Set("public_key", tokenAccountPubkey)
return nil
}

func resourceAssociatedTokenAccountRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*providerConfig).rpcClient

owner, err := solana.PublicKeyFromBase58(d.Get("owner").(string))
if err != nil {
return err
}

tokenMint, err := solana.PublicKeyFromBase58(d.Get("token_mint").(string))
if err != nil {
return err
}

res, err := client.GetTokenAccountsByOwner(
context.Background(),
owner,
&rpc.GetTokenAccountsConfig{
Mint: &tokenMint,
ProgramId: &associatedtokenaccount.ProgramID,
},
&rpc.GetTokenAccountsOpts{
Commitment: rpc.CommitmentConfirmed,
},
)
if err != nil {
return err
} else if len(res.Value) == 0 {
return fmt.Errorf("the associated token account was not found in the confirmed accounts for owner")
}

d.Set("public_key", res.Value[0].Pubkey.String())

return nil
}

func resourceAssociatedTokenAccountDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*providerConfig).rpcClient

tokenAccount, err := solana.PublicKeyFromBase58(d.Get("public_key").(string))
if err != nil {
return err
}

owner, err := solana.PublicKeyFromBase58(d.Get("owner").(string))
if err != nil {
return err
}

payer, err := solana.PublicKeyFromBase58(d.Get("payer").(string))
if err != nil {
return err
}

inst := token.NewCloseAccountInstruction(tokenAccount, payer, owner, nil)

recentHash, err := client.GetRecentBlockhash(context.Background(), rpc.CommitmentConfirmed)
if err != nil {
return err
}

tx, err := solana.NewTransaction([]solana.Instruction{inst.Build()}, recentHash.Value.Blockhash)
if err != nil {
return err
}

_, err = client.SendTransactionWithOpts(context.Background(), tx, false, rpc.CommitmentConfirmed)
if err != nil {
return err
}

return nil
}
8 changes: 4 additions & 4 deletions internal/solana/resource_random_keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ func resourceRandomKeypair() *schema.Resource {
}

func resourceRandomKeypairCreate(d *schema.ResourceData, meta interface{}) error {
pub, priv, err := solana.NewRandomPrivateKey()
key, err := solana.NewRandomPrivateKey()
if err != nil {
return err
}

d.SetId(fmt.Sprintf("random_keypair:%x", sha1.Sum([]byte(pub.String()))))
d.Set("public_key", pub.String())
d.Set("private_key", priv.String())
d.SetId(fmt.Sprintf("random_keypair:%x", sha1.Sum([]byte(key.PublicKey().String()))))
d.Set("public_key", key.PublicKey().String())
d.Set("private_key", key.String())
return nil
}

Expand Down

0 comments on commit 10c65ab

Please sign in to comment.