From 00f3530454b7e39d4cf260d8a443baeb34c69fb0 Mon Sep 17 00:00:00 2001 From: Guilherme Lopes Date: Sun, 22 Oct 2023 22:45:41 +0100 Subject: [PATCH 1/4] created Account.DeployAccount method --- account/account.go | 49 ++++++++++++++++++++++++++++++++++ examples/deployAccount/go.mod | 4 ++- examples/deployAccount/main.go | 49 +++++++++++++++------------------- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/account/account.go b/account/account.go index 592caec6..552bd197 100644 --- a/account/account.go +++ b/account/account.go @@ -480,3 +480,52 @@ func FmtCalldataCairo2(fnCalls []rpc.FunctionCall) []*felt.Felt { return execCallData } + +type DeployOptions struct { + ClassHash *felt.Felt + MaxFee *felt.Felt + DeploytWaitTime time.Duration +} + +func (account *Account) DeployAccount(options DeployOptions) (*rpc.AddDeployAccountTransactionResponse, error) { + + if options.ClassHash == nil { + + } + + 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: []*felt.Felt{pub}, + } + + 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(), tx) + if err != nil { + return nil, err + } + + account.WaitForTransactionReceipt(context.Background(), resp.TransactionHash, options.DeploytWaitTime) + + return resp, nil +} diff --git a/examples/deployAccount/go.mod b/examples/deployAccount/go.mod index c031a165..b67b9b54 100644 --- a/examples/deployAccount/go.mod +++ b/examples/deployAccount/go.mod @@ -14,14 +14,15 @@ 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/google/go-querystring v1.1.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 @@ -33,4 +34,5 @@ require ( 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 ) diff --git a/examples/deployAccount/main.go b/examples/deployAccount/main.go index c14afdb4..105a03bc 100644 --- a/examples/deployAccount/main.go +++ b/examples/deployAccount/main.go @@ -1,9 +1,11 @@ package main import ( + "bufio" "context" "fmt" "os" + "time" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/starknet.go/account" @@ -48,39 +50,30 @@ func main() { panic(err) } - // Create transaction data - tx := rpc.DeployAccountTxn{ - Nonce: &felt.Zero, // Contract accounts start with nonce zero. - MaxFee: new(felt.Felt).SetUint64(4724395326064), - Type: rpc.TransactionType_DeployAccount, - Version: rpc.TransactionV1, - Signature: []*felt.Felt{}, - ClassHash: classHash, - ContractAddressSalt: pub, - ConstructorCalldata: []*felt.Felt{pub}, - } - - precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, pub, classHash, tx.ConstructorCalldata) - fmt.Println("precomputedAddress:", precomputedAddress) + precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, pub, classHash, []*felt.Felt{pub}) - // At this point you need to add funds to precomputed address to use it. - var input string + fmt.Printf("\nIn order to deploy your account (address %s), you need to fund the acccount (using a faucet), and then press `enter` to continue : \n", precomputedAddress.String()) - fmt.Println("The `precomputedAddress` account needs to have enough ETH to perform a transaction.") - fmt.Println("Use the starknet faucet to send ETH to your `precomputedAddress`") - fmt.Println("When your account has been funded by the faucet, press any key, then `enter` to continue : ") - fmt.Scan(&input) - - // Sign the transaction - err = acnt.SignDeployAccountTransaction(context.Background(), &tx, precomputedAddress) + reader := bufio.NewReader(os.Stdin) + _, err = reader.ReadString('\n') if err != nil { - panic(err) + fmt.Printf("Error: %v\n", err) + return } - // Send transaction to the network - resp, err := acnt.AddDeployAccountTransaction(context.Background(), tx) + fmt.Println("Waiting for deployment") + deployOptions := account.DeployOptions{ + ClassHash: classHash, + MaxFee: new(felt.Felt).SetUint64(4724395326064), + DeploytWaitTime: 2 * time.Second, + } + + // Deploy the account + resp, err := acnt.DeployAccount(deployOptions) + if err != nil { - panic(fmt.Sprint("Error returned from AddDeployAccountTransaction: ", err)) + panic(fmt.Sprint("Error returned from DeployAccount: ", err)) } - fmt.Println("AddDeployAccountTransaction response:", resp) + fmt.Println("Deployed with response response:", resp) + } From 3f606aa836bbd3c17c1f72f0898147e46d2dd092 Mon Sep 17 00:00:00 2001 From: Guilherme Lopes Date: Tue, 7 Nov 2023 14:12:33 +0000 Subject: [PATCH 2/4] added test for account.DeployAccount --- account/account.go | 13 ++++------ account/account_test.go | 43 ++++++++++++++++++++++++++++++++ examples/deployAccount/README.md | 1 - 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/account/account.go b/account/account.go index 552bd197..beff0d95 100644 --- a/account/account.go +++ b/account/account.go @@ -482,17 +482,14 @@ func FmtCalldataCairo2(fnCalls []rpc.FunctionCall) []*felt.Felt { } type DeployOptions struct { - ClassHash *felt.Felt - MaxFee *felt.Felt - DeploytWaitTime time.Duration + ClassHash *felt.Felt + MaxFee *felt.Felt + DeploytWaitTime time.Duration + ConstructorCalldata []*felt.Felt } func (account *Account) DeployAccount(options DeployOptions) (*rpc.AddDeployAccountTransactionResponse, error) { - if options.ClassHash == nil { - - } - pub, err := utils.HexToFelt(account.publicKey) if err != nil { return nil, err @@ -507,7 +504,7 @@ func (account *Account) DeployAccount(options DeployOptions) (*rpc.AddDeployAcco Signature: []*felt.Felt{}, ClassHash: options.ClassHash, ContractAddressSalt: pub, - ConstructorCalldata: []*felt.Felt{pub}, + ConstructorCalldata: options.ConstructorCalldata, } precomputedAddress, err := account.PrecomputeAddress(&felt.Zero, pub, options.ClassHash, tx.ConstructorCalldata) diff --git a/account/account_test.go b/account/account_test.go index 17925390..e05e7488 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -791,6 +791,49 @@ func TestAddDeclareTxn(t *testing.T) { } } +func TestDeployAccountDevnet(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(0), + DeploytWaitTime: 2 * time.Second, + ConstructorCalldata: []*felt.Felt{}, + } + resp, err := acnt.DeployAccount(deployOptions) + require.NoError(t, err, "DeployAccount gave an Error") + require.NotNil(t, resp, "DeployAccount resp not nil") +} + func newDevnet(t *testing.T, url string) (*devnet.DevNet, []devnet.TestAccount, error) { devnet := devnet.NewDevNet(url) acnts, err := devnet.Accounts() diff --git a/examples/deployAccount/README.md b/examples/deployAccount/README.md index 67408e41..047ba153 100644 --- a/examples/deployAccount/README.md +++ b/examples/deployAccount/README.md @@ -1,4 +1,3 @@ - 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: From 5524d06cfb3222a23bf1647e42d9a0c7206e49cb Mon Sep 17 00:00:00 2001 From: Guilherme Lopes Date: Mon, 20 Nov 2023 16:29:59 +0000 Subject: [PATCH 3/4] Fixed typo and changed DeployAccount name --- account/account.go | 15 +++++++++------ account/account_test.go | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/account/account.go b/account/account.go index beff0d95..62838c76 100644 --- a/account/account.go +++ b/account/account.go @@ -482,13 +482,13 @@ func FmtCalldataCairo2(fnCalls []rpc.FunctionCall) []*felt.Felt { } type DeployOptions struct { - ClassHash *felt.Felt - MaxFee *felt.Felt - DeploytWaitTime time.Duration - ConstructorCalldata []*felt.Felt + 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) DeployAccount(options DeployOptions) (*rpc.AddDeployAccountTransactionResponse, error) { +func (account *Account) CreateAndExecuteAddDeployAccount(options DeployOptions) (*rpc.AddDeployAccountTransactionResponse, error) { pub, err := utils.HexToFelt(account.publicKey) if err != nil { @@ -522,7 +522,10 @@ func (account *Account) DeployAccount(options DeployOptions) (*rpc.AddDeployAcco return nil, err } - account.WaitForTransactionReceipt(context.Background(), resp.TransactionHash, options.DeploytWaitTime) + _, err = account.WaitForTransactionReceipt(context.Background(), resp.TransactionHash, options.DeployWaitTime) + if err != nil { + return nil, err + } return resp, nil } diff --git a/account/account_test.go b/account/account_test.go index e05e7488..b2eeff11 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -826,10 +826,10 @@ func TestDeployAccountDevnet(t *testing.T) { deployOptions := account.DeployOptions{ ClassHash: classHash, MaxFee: new(felt.Felt).SetUint64(0), - DeploytWaitTime: 2 * time.Second, + DeployWaitTime: 2 * time.Second, ConstructorCalldata: []*felt.Felt{}, } - resp, err := acnt.DeployAccount(deployOptions) + resp, err := acnt.CreateAndExecuteAddDeployAccount(deployOptions) require.NoError(t, err, "DeployAccount gave an Error") require.NotNil(t, resp, "DeployAccount resp not nil") } From 26a0d836375af22475e7271a1eaa6e91b76d53c7 Mon Sep 17 00:00:00 2001 From: Guilherme Lopes Date: Mon, 20 Nov 2023 18:59:02 +0000 Subject: [PATCH 4/4] - Updated the TestDeployAccountDevnet func name to TestCreateAndExecuteAddDeployAccount - Created a separate example for CreateAndExecuteAddDeployAccount - Merged with upstream and updated CreateAndExecuteAddDeployAccount (Fixed issues mentioned in NethermindEth/starknet.go/pull/445) --- account/account.go | 21 +++-- account/account_test.go | 60 +++++++----- examples/createAndDeployAccount/.env.template | 2 + examples/createAndDeployAccount/README.md | 12 +++ examples/createAndDeployAccount/go.mod | 38 ++++++++ examples/createAndDeployAccount/go.work | 6 ++ examples/createAndDeployAccount/main.go | 93 +++++++++++++++++++ examples/deployAccount/main.go | 56 ++++++----- 8 files changed, 234 insertions(+), 54 deletions(-) create mode 100644 examples/createAndDeployAccount/.env.template create mode 100644 examples/createAndDeployAccount/README.md create mode 100644 examples/createAndDeployAccount/go.mod create mode 100644 examples/createAndDeployAccount/go.work create mode 100644 examples/createAndDeployAccount/main.go diff --git a/account/account.go b/account/account.go index d4025a57..71f1d4f7 100644 --- a/account/account.go +++ b/account/account.go @@ -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) } @@ -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) } @@ -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) } @@ -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 @@ -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) @@ -861,7 +861,10 @@ func (account *Account) CreateAndExecuteAddDeployAccount(options DeployOptions) return nil, err } - resp, err := account.AddDeployAccountTransaction(context.Background(), tx) + resp, err := account.AddDeployAccountTransaction(context.Background(), rpc.BroadcastDeployAccountTxn{ + DeployAccountTxn: tx, + }) + if err != nil { return nil, err } diff --git a/account/account_test.go b/account/account_test.go index 795dc6ef..9129409e 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -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() @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 { @@ -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") @@ -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" { @@ -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" { @@ -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) @@ -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") @@ -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" { @@ -955,7 +972,7 @@ func TestAddDeclareTxn(t *testing.T) { } } -func TestDeployAccountDevnet(t *testing.T) { +func TestCreateAndExecuteAddDeployAccount(t *testing.T) { if testEnv != "devnet" { t.Skip("Skipping test as it requires a devnet environment") } @@ -989,16 +1006,15 @@ func TestDeployAccountDevnet(t *testing.T) { deployOptions := account.DeployOptions{ ClassHash: classHash, - MaxFee: new(felt.Felt).SetUint64(0), + MaxFee: new(felt.Felt).SetUint64(4724395326064), DeployWaitTime: 2 * time.Second, - ConstructorCalldata: []*felt.Felt{}, + 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: diff --git a/examples/createAndDeployAccount/.env.template b/examples/createAndDeployAccount/.env.template new file mode 100644 index 00000000..70f7e092 --- /dev/null +++ b/examples/createAndDeployAccount/.env.template @@ -0,0 +1,2 @@ +# use this variable to change the RPC base URL +#INTEGRATION_BASE=http_insert_end_point diff --git a/examples/createAndDeployAccount/README.md b/examples/createAndDeployAccount/README.md new file mode 100644 index 00000000..047ba153 --- /dev/null +++ b/examples/createAndDeployAccount/README.md @@ -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. + diff --git a/examples/createAndDeployAccount/go.mod b/examples/createAndDeployAccount/go.mod new file mode 100644 index 00000000..d2d1fc18 --- /dev/null +++ b/examples/createAndDeployAccount/go.mod @@ -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 +) diff --git a/examples/createAndDeployAccount/go.work b/examples/createAndDeployAccount/go.work new file mode 100644 index 00000000..01071242 --- /dev/null +++ b/examples/createAndDeployAccount/go.work @@ -0,0 +1,6 @@ +go 1.21 + +use ( + . + ../.. +) diff --git a/examples/createAndDeployAccount/main.go b/examples/createAndDeployAccount/main.go new file mode 100644 index 00000000..b255a21c --- /dev/null +++ b/examples/createAndDeployAccount/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "bufio" + "context" + "fmt" + "os" + "time" + + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/starknet.go/account" + "github.com/NethermindEth/starknet.go/rpc" + "github.com/NethermindEth/starknet.go/utils" + ethrpc "github.com/ethereum/go-ethereum/rpc" + "github.com/joho/godotenv" +) + +var ( + network string = "testnet" + predeployedClassHash = "0x2794ce20e5f2ff0d40e632cb53845b9f4e526ebd8471983f7dbd355b721d5a" + accountAddress = "0xdeadbeef" +) + +// main, similar to the deployAccount example, initializes the client, generates random keys, and sets up a new account for StarkNet deployment. +// +// It starts by loading environment variables and connecting to the Ethereum RPC. Random keys are generated for account creation. The account address is converted to a felt type, and the account is initialized with these parameters. +// +// The function precomputes an address, asking the user to fund it. After user confirmation, it uses CreateAndExecuteAddDeployAccount for deployment, streamlining the process compared to the deployAccount example. +// +// Parameters: +// +// none +// +// Returns: +// +// none +func main() { + // Initialise the client. + godotenv.Load(fmt.Sprintf(".env.%s", network)) + base := os.Getenv("INTEGRATION_BASE") + c, err := ethrpc.DialContext(context.Background(), base) + if err != nil { + panic("You need to specify the testnet url in .env.testnet") + } + clientv02 := rpc.NewProvider(c) + + // Get random keys for test purposes + ks, pub, _ := account.GetRandomKeys() + + accountAddressFelt, err := new(felt.Felt).SetString(accountAddress) + if err != nil { + panic("Error casting accountAddress to felt") + } + + // Set up account + acnt, err := account.NewAccount(clientv02, accountAddressFelt, pub.String(), ks) + if err != nil { + panic(err) + } + + classHash, err := utils.HexToFelt(predeployedClassHash) + if err != nil { + panic(err) + } + + precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, pub, classHash, []*felt.Felt{pub}) + + fmt.Printf("\nIn order to deploy your account (address %s), you need to fund the acccount (using a faucet), and then press `enter` to continue : \n", precomputedAddress.String()) + + reader := bufio.NewReader(os.Stdin) + _, err = reader.ReadString('\n') + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + fmt.Println("Waiting for deployment") + deployOptions := account.DeployOptions{ + ClassHash: classHash, + MaxFee: new(felt.Felt).SetUint64(4724395326064), + DeployWaitTime: 2 * time.Second, + ConstructorCalldata: []*felt.Felt{pub}, + } + + // Deploy the account + resp, err := acnt.CreateAndExecuteAddDeployAccount(deployOptions) + + if err != nil { + panic(fmt.Sprint("Error returned from DeployAccount: ", err)) + } + fmt.Println("Deployed with response response:", resp) + +} diff --git a/examples/deployAccount/main.go b/examples/deployAccount/main.go index 6eb7c4bc..474d78bb 100644 --- a/examples/deployAccount/main.go +++ b/examples/deployAccount/main.go @@ -1,11 +1,9 @@ package main import ( - "bufio" "context" "fmt" "os" - "time" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/starknet.go/account" @@ -29,9 +27,12 @@ var ( // and finally sends the transaction to the network. // // Parameters: -// none +// +// none +// // Returns: -// none +// +// none func main() { // Initialise the client. godotenv.Load(fmt.Sprintf(".env.%s", network)) @@ -61,30 +62,39 @@ func main() { panic(err) } - precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, pub, classHash, []*felt.Felt{pub}) - - fmt.Printf("\nIn order to deploy your account (address %s), you need to fund the acccount (using a faucet), and then press `enter` to continue : \n", precomputedAddress.String()) - - reader := bufio.NewReader(os.Stdin) - _, err = reader.ReadString('\n') - if err != nil { - fmt.Printf("Error: %v\n", err) - return + // Create transaction data + tx := rpc.DeployAccountTxn{ + Nonce: &felt.Zero, // Contract accounts start with nonce zero. + MaxFee: new(felt.Felt).SetUint64(4724395326064), + Type: rpc.TransactionType_DeployAccount, + Version: rpc.TransactionV1, + Signature: []*felt.Felt{}, + ClassHash: classHash, + ContractAddressSalt: pub, + ConstructorCalldata: []*felt.Felt{pub}, } - fmt.Println("Waiting for deployment") - deployOptions := account.DeployOptions{ - ClassHash: classHash, - MaxFee: new(felt.Felt).SetUint64(4724395326064), - DeploytWaitTime: 2 * time.Second, - } + precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, pub, classHash, tx.ConstructorCalldata) + fmt.Println("precomputedAddress:", precomputedAddress) + + // At this point you need to add funds to precomputed address to use it. + var input string - // Deploy the account - resp, err := acnt.DeployAccount(deployOptions) + fmt.Println("The `precomputedAddress` account needs to have enough ETH to perform a transaction.") + fmt.Println("Use the starknet faucet to send ETH to your `precomputedAddress`") + fmt.Println("When your account has been funded by the faucet, press any key, then `enter` to continue : ") + fmt.Scan(&input) + // Sign the transaction + err = acnt.SignDeployAccountTransaction(context.Background(), &tx, precomputedAddress) if err != nil { - panic(fmt.Sprint("Error returned from DeployAccount: ", err)) + panic(err) } - fmt.Println("Deployed with response response:", resp) + // Send transaction to the network + resp, err := acnt.AddDeployAccountTransaction(context.Background(), rpc.BroadcastDeployAccountTxn{DeployAccountTxn: tx}) + if err != nil { + panic(fmt.Sprint("Error returned from AddDeployAccountTransaction: ", err)) + } + fmt.Println("AddDeployAccountTransaction response:", resp) }