Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DeployAccount Method to account/account.go for Issue #381 #445

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 60 additions & 8 deletions account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ func (account *Account) BlockNumber(ctx context.Context) (uint64, error) {
// - blockID: The rpc.BlockID object representing the block.
// Returns:
// - uint64: the number of transactions in the block
// - error: an error, if any
// - error: an error, if any
func (account *Account) BlockTransactionCount(ctx context.Context, blockID rpc.BlockID) (uint64, error) {
return account.provider.BlockTransactionCount(ctx, blockID)
}
Expand Down Expand Up @@ -530,12 +530,12 @@ func (account *Account) ChainID(ctx context.Context) (string, error) {
//
// Parameters:
// - ctx: The context.Context
// - blockID: The rpc.BlockID
// - blockID: The rpc.BlockID
// - classHash: The `*felt.Felt`
// Returns:
// - *rpc.ClassOutput: The rpc.ClassOutput (the class output could be a DeprecatedContractClass
// - *rpc.ClassOutput: The rpc.ClassOutput (the class output could be a DeprecatedContractClass
// or just a Contract class depending on the contract version)
// - error: An error if any occurred.
// - error: An error if any occurred.
func (account *Account) Class(ctx context.Context, blockID rpc.BlockID, classHash *felt.Felt) (rpc.ClassOutput, error) {
return account.provider.Class(ctx, blockID, classHash)
}
Expand All @@ -546,9 +546,9 @@ func (account *Account) Class(ctx context.Context, blockID rpc.BlockID, classHas
// - blockID: The rpc.BlockID object representing the block ID.
// - contractAddress: The felt.Felt object representing the contract address.
// Returns:
// - *rpc.ClassOutput: The rpc.ClassOutput object (the class output could be a DeprecatedContractClass
// - *rpc.ClassOutput: The rpc.ClassOutput object (the class output could be a DeprecatedContractClass
// or just a Contract class depending on the contract version)
// - error: An error if any occurred.
// - error: An error if any occurred.
func (account *Account) ClassAt(ctx context.Context, blockID rpc.BlockID, contractAddress *felt.Felt) (rpc.ClassOutput, error) {
return account.provider.ClassAt(ctx, blockID, contractAddress)
}
Expand Down Expand Up @@ -618,7 +618,7 @@ func (account *Account) Nonce(ctx context.Context, blockID rpc.BlockID, contract
}

// SimulateTransactions simulates transactions using the provided context
// Parameters:
// Parameters:
// - ctx: The context.Context object
// - blockID: The rpc.BlockID object for the block referencing the state or call the transactions are on
// - txns: The slice of rpc.Transaction objects representing the transactions to simulate
Expand Down Expand Up @@ -707,7 +707,7 @@ func (account *Account) TransactionReceipt(ctx context.Context, transactionHash
// Parameters:
// - ctx: The context.Context object for the request.
// - transactionHash: The transaction hash for which the transaction trace is to be retrieved.
// Returns:
// Returns:
// - rpc.TxnTrace: The rpc.TxnTrace object representing the transaction trace, and an error if any.
func (account *Account) TraceTransaction(ctx context.Context, transactionHash *felt.Felt) (rpc.TxnTrace, error) {
return account.provider.TraceTransaction(ctx, transactionHash)
Expand Down Expand Up @@ -824,3 +824,55 @@ func FmtCalldataCairo2(fnCalls []rpc.FunctionCall) []*felt.Felt {

return execCallData
}

type DeployOptions struct {
ClassHash *felt.Felt // ClassHash is the unique identifier of the account class in the network.
MaxFee *felt.Felt // MaxFee represents the maximum fee the user is willing to pay for the deployment transaction.
DeployWaitTime time.Duration // DeployWaitTime specifies the duration to wait for the deployment transaction to be processed.
ConstructorCalldata []*felt.Felt // ConstructorCalldata contains the calldata to be passed to the constructor of the account contract upon deployment.
}

func (account *Account) CreateAndExecuteAddDeployAccount(options DeployOptions) (*rpc.AddDeployAccountTransactionResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for rpcv05 and lower. However, rpcv06 introduces a new deployAccount transaction version (v3), meaning users won't be able to use this method to submit v3 transactions. It would be great if we could make this compatible with both transaction versions (eg by accepting the transaction as an argument, tx rpc.DeployAccountType)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure will fix it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by "this method" you mean the whole method "CreateAndExecuteAddDeployAccount" or the way you deploy accounts like in the function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateAndExecuteAddDeployAccount method.


pub, err := utils.HexToFelt(account.publicKey)
if err != nil {
return nil, err
}

// Create transaction data
tx := rpc.DeployAccountTxn{
Nonce: &felt.Zero,
MaxFee: options.MaxFee,
Type: rpc.TransactionType_DeployAccount,
Version: rpc.TransactionV1,
Signature: []*felt.Felt{},
ClassHash: options.ClassHash,
ContractAddressSalt: pub,
ConstructorCalldata: options.ConstructorCalldata,
}

precomputedAddress, err := account.PrecomputeAddress(&felt.Zero, pub, options.ClassHash, tx.ConstructorCalldata)
if err != nil {
return nil, err
}

err = account.SignDeployAccountTransaction(context.Background(), &tx, precomputedAddress)
if err != nil {
return nil, err
}

resp, err := account.AddDeployAccountTransaction(context.Background(), rpc.BroadcastDeployAccountTxn{
DeployAccountTxn: tx,
})

if err != nil {
return nil, err
}

_, err = account.WaitForTransactionReceipt(context.Background(), resp.TransactionHash, options.DeployWaitTime)
if err != nil {
return nil, err
}

return resp, nil
}
97 changes: 79 additions & 18 deletions account/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ var (
// Parameters:
// - m: is the test main
// Returns:
// none
//
// none
func TestMain(m *testing.M) {
flag.StringVar(&testEnv, "env", "mock", "set the test environment")
flag.Parse()
Expand All @@ -60,11 +61,13 @@ func TestMain(m *testing.M) {
// of the transaction hash. Each test case consists of the expected hash, a flag
// indicating whether the KeyStore should be set, account address, public key,
// private key, chain ID, function call, and transaction details.
//
//
// Parameters:
// - t: The testing.T object for running the test
//
// Returns:
// none
//
// none
func TestTransactionHashInvoke(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
Expand Down Expand Up @@ -184,11 +187,12 @@ func TestTransactionHashInvoke(t *testing.T) {
//
// It tests the FmtCallData function by providing different test sets
// and comparing the output with the expected call data.
//
//
// Parameters:
// - t: The testing.T instance for running the test
// Return:
// none
//
// none
func TestFmtCallData(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
Expand Down Expand Up @@ -253,7 +257,8 @@ func TestFmtCallData(t *testing.T) {
// Parameters:
// - t: The testing.T instance for running the test
// Return:
// none
//
// none
func TestChainIdMOCK(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
Expand Down Expand Up @@ -296,7 +301,8 @@ func TestChainIdMOCK(t *testing.T) {
// Parameters:
// - t: The testing.T instance for running the test
// Return:
// none
//
// none
func TestChainId(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
Expand Down Expand Up @@ -344,7 +350,8 @@ func TestChainId(t *testing.T) {
// Parameters:
// - t: The testing.T instance for running the test
// Returns:
// none
//
// none
func TestSignMOCK(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
Expand Down Expand Up @@ -404,7 +411,8 @@ func TestSignMOCK(t *testing.T) {
// Parameters:
// - t: The testing.T instance for running the test
// Returns:
// none
//
// none
func TestAddInvoke(t *testing.T) {

type testSetType struct {
Expand Down Expand Up @@ -582,9 +590,11 @@ func TestAddInvoke(t *testing.T) {
// response is not nil.
//
// Parameters:
// - t: is the testing framework
// - t: is the testing framework
//
// Returns:
// none
//
// none
func TestAddDeployAccountDevnet(t *testing.T) {
if testEnv != "devnet" {
t.Skip("Skipping test as it requires a devnet environment")
Expand Down Expand Up @@ -640,9 +650,11 @@ func TestAddDeployAccountDevnet(t *testing.T) {
// Finally, it verifies that the calculated hash matches the expected hash.
//
// Parameters:
// - t: is the testing framework
// - t: is the testing framework
//
// Returns:
// none
//
// none
func TestTransactionHashDeployAccountTestnet(t *testing.T) {

if testEnv != "testnet" {
Expand Down Expand Up @@ -709,7 +721,8 @@ func TestTransactionHashDeployAccountTestnet(t *testing.T) {
// Parameters:
// - t: reference to the testing.T object
// Returns:
// none
//
// none
func TestTransactionHashDeclare(t *testing.T) {
// https://goerli.voyager.online/tx/0x4e0519272438a3ae0d0fca776136e2bb6fcd5d3b2af47e53575c5874ccfce92
if testEnv != "testnet" {
Expand Down Expand Up @@ -751,7 +764,8 @@ func TestTransactionHashDeclare(t *testing.T) {
// Parameters:
// - t: The testing.T object for test assertions and logging
// Returns:
// none
//
// none
func TestWaitForTransactionReceiptMOCK(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
Expand Down Expand Up @@ -833,7 +847,8 @@ func TestWaitForTransactionReceiptMOCK(t *testing.T) {
// Parameters:
// - t: The testing.T instance for running the test
// Returns:
// none
//
// none
func TestWaitForTransactionReceipt(t *testing.T) {
if testEnv != "devnet" {
t.Skip("Skipping test as it requires a devnet environment")
Expand Down Expand Up @@ -882,9 +897,11 @@ func TestWaitForTransactionReceipt(t *testing.T) {
// It asserts that the expected hash and error values are returned for each test set.
//
// Parameters:
// - t: The testing.T instance for running the test
// - t: The testing.T instance for running the test
//
// Returns:
// none
//
// none
func TestAddDeclareTxn(t *testing.T) {
// https://goerli.voyager.online/tx/0x76af2faec46130ffad1ab2f615ad16b30afcf49cfbd09f655a26e545b03a21d
if testEnv != "testnet" {
Expand Down Expand Up @@ -955,6 +972,49 @@ func TestAddDeclareTxn(t *testing.T) {
}
}

func TestCreateAndExecuteAddDeployAccount(t *testing.T) {
if testEnv != "devnet" {
t.Skip("Skipping test as it requires a devnet environment")
}
client, err := rpc.NewClient(base + "/rpc")
require.NoError(t, err, "Error in rpc.NewClient")
provider := rpc.NewProvider(client)

devnet, acnts, err := newDevnet(t, base)
require.NoError(t, err, "Error setting up Devnet")
fakeUser := acnts[0]
fakeUserAddr := utils.TestHexToFelt(t, fakeUser.Address)
fakeUserPub := utils.TestHexToFelt(t, fakeUser.PublicKey)

// Set up ks
ks := account.NewMemKeystore()
fakePrivKeyBI, ok := new(big.Int).SetString(fakeUser.PrivateKey, 0)
require.True(t, ok)
ks.Put(fakeUser.PublicKey, fakePrivKeyBI)

acnt, err := account.NewAccount(provider, fakeUserAddr, fakeUser.PublicKey, ks)
require.NoError(t, err)

classHash := utils.TestHexToFelt(t, "0x7b3e05f48f0c69e4a65ce5e076a66271a527aff2c34ce1083ec6e1526997a69") // preDeployed classhash
require.NoError(t, err)

precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, fakeUserPub, classHash, []*felt.Felt{fakeUserPub})

_, err = devnet.Mint(precomputedAddress, new(big.Int).SetUint64(10000000000000000000))

require.NoError(t, err)

deployOptions := account.DeployOptions{
ClassHash: classHash,
MaxFee: new(felt.Felt).SetUint64(4724395326064),
DeployWaitTime: 2 * time.Second,
ConstructorCalldata: []*felt.Felt{fakeUserPub},
}
resp, err := acnt.CreateAndExecuteAddDeployAccount(deployOptions)
require.NoError(t, err, "DeployAccount gave an Error")
require.NotNil(t, resp, "DeployAccount resp not nil")
}

// newDevnet creates a new devnet with the given URL.
//
// Parameters:
Expand All @@ -964,6 +1024,7 @@ func TestAddDeclareTxn(t *testing.T) {
// - *devnet.DevNet: a pointer to a devnet object
// - []devnet.TestAccount: a slice of test accounts
// - error: an error, if any

func newDevnet(t *testing.T, url string) (*devnet.DevNet, []devnet.TestAccount, error) {
devnet := devnet.NewDevNet(url)
acnts, err := devnet.Accounts()
Expand Down
2 changes: 2 additions & 0 deletions examples/createAndDeployAccount/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# use this variable to change the RPC base URL
#INTEGRATION_BASE=http_insert_end_point
12 changes: 12 additions & 0 deletions examples/createAndDeployAccount/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
This example uses a pre-existing contract on the goerli network to deploy a new account contract. To successfully run this example, you will need: 1) a goerli endpoint, and 2) to fund the precomputed address.

Steps:
1. Rename the ".env.template" file to ".env.testnet"
2. Uncomment, and assign your testnet endpoint to the "INTEGRATION_BASE" variable
3. Execute `go mod tidy` (make sure you are in the "deployAccount" folder)
4. Execute `go run main.go`
5. Fund the precomputed address using a starknet faucet, eg https://faucet.goerli.starknet.io/
6. Press any key, then enter

At this point your account should be deployed on testnet, and you can use a block explorer like [Voyager](https://voyager.online/) to view your transaction using the transaction hash.

38 changes: 38 additions & 0 deletions examples/createAndDeployAccount/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module account

go 1.21

require (
github.com/NethermindEth/juno v0.3.1
github.com/NethermindEth/starknet.go v0.2.1-0.20220620163912-1db2ca279608
github.com/ethereum/go-ethereum v1.10.26
github.com/joho/godotenv v1.4.0
)

replace github.com/NethermindEth/starknet.go => ../../

require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/bits-and-blooms/bitset v1.7.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.11.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/test-go/testify v1.1.4 // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.2.0 // indirect
golang.org/x/sys v0.3.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
6 changes: 6 additions & 0 deletions examples/createAndDeployAccount/go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go 1.21

use (
.
../..
)
Loading