diff --git a/examples/deployAccountUDC/.env.template b/examples/deployAccountUDC/.env.template deleted file mode 100644 index a055aebc..00000000 --- a/examples/deployAccountUDC/.env.template +++ /dev/null @@ -1,2 +0,0 @@ -# use this variable to change the RPC base URL -# INTEGRATION_BASE=http_insert_end_point diff --git a/examples/deployAccountUDC/README.md b/examples/deployAccountUDC/README.md deleted file mode 100644 index fe771252..00000000 --- a/examples/deployAccountUDC/README.md +++ /dev/null @@ -1,9 +0,0 @@ -Note: To run this example, you need a testnet endpoint. - -Steps to run this example on testnet: - -1. rename ".env.template" to ".env.testnet" -2. uncomment, and set INTEGRATION_BASE to the testnet url -3. make sure you are in the "deployAccountUDC" directory -4. execute `go mod tidy` -5. execute `go run main.go` diff --git a/examples/deployAccountUDC/go.mod b/examples/deployAccountUDC/go.mod deleted file mode 100644 index 4c38b905..00000000 --- a/examples/deployAccountUDC/go.mod +++ /dev/null @@ -1,33 +0,0 @@ -module account - -go 1.21 - -require ( - github.com/NethermindEth/juno v0.3.1 - github.com/NethermindEth/starknet.go v0.4.6-0.20231005024141-742a82479868 - github.com/joho/godotenv v1.4.0 -) - -require ( - github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // 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/ethereum/go-ethereum v1.10.26 // 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/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 -) diff --git a/examples/deployAccountUDC/go.work b/examples/deployAccountUDC/go.work deleted file mode 100644 index 01071242..00000000 --- a/examples/deployAccountUDC/go.work +++ /dev/null @@ -1,6 +0,0 @@ -go 1.21 - -use ( - . - ../.. -) diff --git a/examples/deployAccountUDC/main.go b/examples/deployAccountUDC/main.go deleted file mode 100644 index 6c9b3629..00000000 --- a/examples/deployAccountUDC/main.go +++ /dev/null @@ -1,153 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "math/big" - "math/rand" - "os" - - "github.com/NethermindEth/juno/core/felt" - "github.com/NethermindEth/starknet.go/account" - "github.com/NethermindEth/starknet.go/rpc" - "github.com/NethermindEth/starknet.go/utils" - "github.com/joho/godotenv" -) - -// NOTE : Please add in your keys only for testing purposes, in case of a leak you would potentially lose your funds. -var ( - name string = "testnet" // env."name" - account_addr string = "0x06f36e8a0fc06518125bbb1c63553e8a7d8597d437f9d56d891b8c7d3c977716" // Replace it with your account address - account_cairo_version = 0 // Replace with the cairo version of your account - privateKey string = "0x0687bf84896ee63f52d69e6de1b41492abeadc0dc3cb7bd351d0a52116915937" // Replace it with your account private key - public_key string = "0x58b0824ee8480133cad03533c8930eda6888b3c5170db2f6e4f51b519141963" // Replace it with your account public key - someContract string = "0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf" // UDC contract address - contractMethod string = "deployContract" // UDC method to deploy account (from pre-declared contract) -) - -// Example succesful transaction created from this example on Goerli -// https://goerli.voyager.online/tx/0x9576bad061e1790ea1785cb3a950a5724390ea3d0bbb65fc09cc300d801b22 - -func main() { - fmt.Println("Starting deployAccountUDC example") - - // Loading the env - godotenv.Load(fmt.Sprintf(".env.%s", name)) - url := os.Getenv("INTEGRATION_BASE") //please modify the .env.testnet and replace the INTEGRATION_BASE with a starknet goerli RPC. - - // Initialize connection to RPC provider - clientv02, err := rpc.NewProvider(url) - if err != nil { - log.Fatal(fmt.Sprintf("Error dialing the RPC provider: %s", err)) - } - - // Here we are converting the account address to felt - account_address, err := utils.HexToFelt(account_addr) - if err != nil { - panic(err.Error()) - } - // Initialize the account memkeyStore (set public and private keys) - ks := account.NewMemKeystore() - fakePrivKeyBI, ok := new(big.Int).SetString(privateKey, 0) - if !ok { - panic(err.Error()) - } - ks.Put(public_key, fakePrivKeyBI) - - fmt.Println("Established connection with the client") - - // Set maxFee - maxfee, err := utils.HexToFelt("0x9184e72a000") - if err != nil { - panic(err.Error()) - } - - // Initialize the account - accnt, err := account.NewAccount(clientv02, account_address, public_key, ks, account_cairo_version) - if err != nil { - panic(err.Error()) - } - - // Get the accounts nonce - nonce, err := accnt.Nonce(context.Background(), rpc.BlockID{Tag: "latest"}, accnt.AccountAddress) - if err != nil { - panic(err) - } - - // Build the InvokeTx struct - InvokeTx := rpc.InvokeTxnV1{ - MaxFee: maxfee, - Version: rpc.TransactionV1, - Nonce: nonce, - Type: rpc.TransactionType_Invoke, - SenderAddress: accnt.AccountAddress, - } - - // Convert the contractAddress from hex to felt - contractAddress, err := utils.HexToFelt(someContract) - if err != nil { - panic(err.Error()) - } - - // Build the functionCall struct, where : - FnCall := rpc.FunctionCall{ - ContractAddress: contractAddress, //contractAddress is the contract that we want to call - EntryPointSelector: utils.GetSelectorFromNameFelt(contractMethod), //this is the function that we want to call - Calldata: getUDCCalldata(), - } - - // Building the Calldata with the help of FmtCalldata where we pass in the FnCall struct along with the Cairo version - InvokeTx.Calldata, err = accnt.FmtCalldata([]rpc.FunctionCall{FnCall}) - if err != nil { - panic(err.Error()) - } - - // Sign the transaction - err = accnt.SignInvokeTransaction(context.Background(), &InvokeTx) - if err != nil { - panic(err.Error()) - } - - // After the signing we finally call the AddInvokeTransaction in order to invoke the contract function - resp, err := accnt.AddInvokeTransaction(context.Background(), InvokeTx) - if err != nil { - panic(err) - } - // This returns us with the transaction hash - fmt.Println("Transaction hash response : ", resp.TransactionHash) - -} - -// getUDCCalldata is a simple helper to set the call data required by the UDCs deployContract function. Update as needed. -func getUDCCalldata() []*felt.Felt { - - classHash, err := new(felt.Felt).SetString("0x32f352d58c0a96d594de0ab19c24b9e6ed1e6310f805e61369ff156310827a") - if err != nil { - panic(err.Error()) - } - - randomInt := rand.Uint64() - salt := new(felt.Felt).SetUint64(randomInt) // to prevent address clashes - - unique, err := new(felt.Felt).SetString("0x0") - if err != nil { - panic(err.Error()) - } - - calldataLen, err := new(felt.Felt).SetString("0x5") - if err != nil { - panic(err.Error()) - } - - calldata, err := utils.HexArrToFelt([]string{ - "0x477261696c7320455243343034", - "0x475241494c53", - "0x2710", - "0x00", - "0x07820b89733f802708f8eb768b59615f986205adc6eb6917c38b7771f7801caa"}) - if err != nil { - panic(err.Error()) - } - return append([]*felt.Felt{classHash, salt, unique, calldataLen}, calldata...) -} diff --git a/examples/deployContractUDC/README.md b/examples/deployContractUDC/README.md new file mode 100644 index 00000000..eb785837 --- /dev/null +++ b/examples/deployContractUDC/README.md @@ -0,0 +1,24 @@ +Note: To run this example, you need a testnet endpoint. + +Steps to run this example on testnet: + +1. rename ".env.template" to ".env.testnet" +2. uncomment, and set INTEGRATION_BASE to the testnet url +3. make sure you are in the "deployAccountUDC" directory +4. execute `go mod tidy` +5. execute `go run main.go` + + + +This example uses a pre-existing class on the Sepolia network to deploy a new account contract. To successfully run this example, you will need: 1) a Sepolia endpoint, and 2) some Sepolia ETH to fund the precomputed address. + +Steps: +1. Rename the ".env.template" file located at the root of the "examples" folder to ".env" +1. Uncomment, and assign your testnet endpoint to the `RPC_PROVIDER_URL` variable in the ".env" file +1. Make sure you are in the "deployAccountUDC" directory +1. Execute `go run main.go` +1. Fund the precomputed address using a starknet faucet, eg https://starknet-faucet.vercel.app/ +1. 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://sepolia.voyager.online/) to view your transaction using the transaction hash. + diff --git a/examples/deployContractUDC/main.go b/examples/deployContractUDC/main.go new file mode 100644 index 00000000..b6123c0e --- /dev/null +++ b/examples/deployContractUDC/main.go @@ -0,0 +1,171 @@ +package main + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + "math/rand" + "strconv" + "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" + + setup "github.com/NethermindEth/starknet.go/examples/internal" +) + +// More info: https://docs.starknet.io/architecture-and-concepts/accounts/universal-deployer/ +var ( + someContractHash string = "0x046ded64ae2dead6448e247234bab192a9c483644395b66f2155f2614e5804b0" // The contract hash to be deployed (in this example, it's an ERC20 contract) + UDCAddress string = "0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf" // UDC contract address + contractMethod string = "deployContract" // UDC method to deploy a contract (from pre-declared contracts) +) + +// Example succesful transaction created from this example on Goerli +// https://goerli.voyager.online/tx/0x9576bad061e1790ea1785cb3a950a5724390ea3d0bbb65fc09cc300d801b22 + +func main() { + fmt.Println("Starting deployContractUDC example") + + // Load variables from '.env' file + rpcProviderUrl := setup.GetRpcProviderUrl() + account_addr := setup.GetAccountAddress() + account_cairo_version := setup.GetAccountCairoVersion() + privateKey := setup.GetPrivateKey() + public_key := setup.GetPublicKey() + + // Initialize connection to RPC provider + client, err := rpc.NewProvider(rpcProviderUrl) + if err != nil { + panic(fmt.Sprintf("Error dialing the RPC provider: %s", err)) + } + + // Here we are converting the account address to felt + account_address, err := utils.HexToFelt(account_addr) + if err != nil { + panic(err) + } + + // Initialize the account memkeyStore (set public and private keys) + ks := account.NewMemKeystore() + privKeyBI, ok := new(big.Int).SetString(privateKey, 0) + if !ok { + panic("Fail to convert privKey to bitInt") + } + ks.Put(public_key, privKeyBI) + + fmt.Println("Established connection with the client") + + // Set maxFee + maxfee, err := utils.HexToFelt("0x9184e72a000") + if err != nil { + panic(err) + } + + // Initialize the account + accnt, err := account.NewAccount(client, account_address, public_key, ks, account_cairo_version) + if err != nil { + panic(err) + } + + // Get the accounts nonce + nonce, err := accnt.Nonce(context.Background(), rpc.BlockID{Tag: "latest"}, accnt.AccountAddress) + if err != nil { + setup.PanicRPC(err) + } + + // Build the InvokeTx struct + InvokeTx := rpc.InvokeTxnV1{ + MaxFee: maxfee, + Version: rpc.TransactionV1, + Nonce: nonce, + Type: rpc.TransactionType_Invoke, + SenderAddress: accnt.AccountAddress, + } + + // Convert the contractAddress from hex to felt + contractAddress, err := utils.HexToFelt(UDCAddress) + if err != nil { + panic(err) + } + + // Build the functionCall struct, where : + FnCall := rpc.FunctionCall{ + ContractAddress: contractAddress, //contractAddress is the contract that we want to call + EntryPointSelector: utils.GetSelectorFromNameFelt(contractMethod), //this is the function that we want to call + Calldata: getUDCCalldata(account_addr), //change this function content to your use case + } + + // Building the Calldata with the help of FmtCalldata where we pass in the FnCall struct along with the Cairo version + InvokeTx.Calldata, err = accnt.FmtCalldata([]rpc.FunctionCall{FnCall}) + if err != nil { + panic(err) + } + + // Sign the transaction + err = accnt.SignInvokeTransaction(context.Background(), &InvokeTx) + if err != nil { + panic(err) + } + + // After the signing we finally call the AddInvokeTransaction in order to invoke the contract function + resp, err := accnt.AddInvokeTransaction(context.Background(), InvokeTx) + if err != nil { + setup.PanicRPC(err) + } + + time.Sleep(time.Second * 3) // Waiting 3 seconds + + //Getting the transaction status + txStatus, err := client.GetTransactionStatus(context.Background(), resp.TransactionHash) + if err != nil { + setup.PanicRPC(err) + } + + // This returns us with the transaction hash and status + fmt.Println("Transaction hash response : ", resp.TransactionHash) + fmt.Println("Transaction execution status : ", txStatus.ExecutionStatus) + fmt.Println("Transaction status : ", txStatus.FinalityStatus) +} + +// getUDCCalldata is a simple helper to set the call data required by the UDCs deployContract function. Update as needed. +func getUDCCalldata(data ...string) []*felt.Felt { + + classHash, err := new(felt.Felt).SetString(someContractHash) + if err != nil { + panic(err) + } + + randomInt := rand.Uint64() + salt := new(felt.Felt).SetUint64(randomInt) // to prevent address clashes + + unique, err := new(felt.Felt).SetString("0x0") // see https://docs.starknet.io/architecture-and-concepts/accounts/universal-deployer/#deployment_types + if err != nil { + panic(err) + } + + // As we are using an ERC20 token in this example, the calldata needs to have the ERC20 constructor required parameters. + // You must adjust these fields to match the constructor's parameters of your desired contract. + // https://docs.openzeppelin.com/contracts-cairo/0.8.1/api/erc20#ERC20-constructor-section + calldata, err := utils.HexArrToFelt([]string{ + hex.EncodeToString([]byte("MyERC20Token")), //name + hex.EncodeToString([]byte("MET")), //symbol + strconv.FormatInt(200000000000000000, 16), //fixed_supply (u128 low). See https://book.cairo-lang.org/ch02-02-data-types.html#integer-types + strconv.FormatInt(0, 16), //fixed_supply (u128 high) + data[0], //recipient + }) + if err != nil { + panic(err) + } + + length := int64(len(calldata)) + calldataLen, err := new(felt.Felt).SetString(strconv.FormatInt(length, 16)) + if err != nil { + panic(err) + } + + return append([]*felt.Felt{classHash, salt, unique, calldataLen}, calldata...) +}