diff --git a/clijs/src/commands/smart-account/new.ts b/clijs/src/commands/smart-account/new.ts index 160483624..be4913110 100644 --- a/clijs/src/commands/smart-account/new.ts +++ b/clijs/src/commands/smart-account/new.ts @@ -81,6 +81,8 @@ export default class SmartAccountNew extends BaseCommand { throw new Error("Faucet client is not initialized"); } + logger.debug("Deploying new smart-account"); + const pubkey = signer.getPublicKey(); const smartAccount = new SmartAccountV1({ pubkey: pubkey, @@ -91,20 +93,8 @@ export default class SmartAccountNew extends BaseCommand { }); const smartAccountAddress = smartAccount.address; - logger.debug(`Withdrawing ${flags.amount} to ${smartAccountAddress}`); - - const faucets = await this.faucetClient.getAllFaucets(); - await this.faucetClient.topUpAndWaitUntilCompletion( - { - amount: flags.amount, - smartAccountAddress: smartAccountAddress, - faucetAddress: faucets.NIL, - }, - this.rpcClient, - ); - - const tx = await smartAccount.selfDeploy(true); - this.info(`Successfully deployed smart account with tx hash: ${tx.hash}`); + const address = await smartAccount.selfDeploy(this.faucetClient); + this.info(`Successfully deployed smart account at: ${address}`); if (this.quiet) { this.log(smartAccountAddress); diff --git a/clijs/test/setup.ts b/clijs/test/setup.ts index 0a7100ca4..1437bb6d0 100644 --- a/clijs/test/setup.ts +++ b/clijs/test/setup.ts @@ -10,7 +10,6 @@ import { LocalECDSAKeySigner, SmartAccountV1, generateRandomPrivateKey, - waitTillCompleted, } from "@nilfoundation/niljs"; import { PublicClient } from "@nilfoundation/niljs"; import type { Errors } from "@oclif/core"; @@ -126,19 +125,7 @@ export const CliTest = test.extend({ signer: signer, }); - const faucets = await faucetClient.getAllFaucets(); - await faucetClient.topUpAndWaitUntilCompletion( - { - faucetAddress: faucets.NIL, - smartAccountAddress: smartAccount.address, - amount: 1_000_000_000_000_000_000n, - }, - rpcClient, - ); - - const deployTx = await smartAccount.selfDeploy(true); - - await waitTillCompleted(rpcClient, deployTx.hash); + await smartAccount.selfDeploy(faucetClient); await use(smartAccount); }, }); diff --git a/docs/nil/nilcli/deploying-smart-contract.mdx b/docs/nil/nilcli/deploying-smart-contract.mdx index c2e100fd2..3e5eb8470 100644 --- a/docs/nil/nilcli/deploying-smart-contract.mdx +++ b/docs/nil/nilcli/deploying-smart-contract.mdx @@ -25,36 +25,3 @@ To deploy the manufactuer with the given public key and the address of the retai ``` This will make sure that `Manufacturer.sol` and `Retailer.sol` are on different shards. - -## External deployment - -First, calulate the address of the retailer contract and send funds to this address: - -```bash file=../../tests/working-with-smart-contracts-cli.test.mjs start=startExternalRetailerAddressCommand end=endExternalRetailerAddressCommand -``` - -```bash file=../../tests/working-with-smart-contracts-cli.test.mjs start=startSendTokensToRetailerForExternalDeployment end=endSendTokensToRetailerForExternalDeployment -``` - -Then, deploy the retailer contract while providing the same `SALT`: - -```bash file=../../tests/working-with-smart-contracts-cli.test.mjs start=startRetailerExternalDeploymentCommand end=endRetailerExternalDeploymentCommand -``` - -Retrieve the public key assigned to the smart account: - -```bash file=../../tests/commands.mjs start=startInfo end=endInfo -``` - -Calculate the address of the manufacturer contract and send funds to it: - -```bash file=../../tests/working-with-smart-contracts-cli.test.mjs start=startExternalManufacturerAddressCommand end=endExternalManufacturerAddressCommand -``` - -```bash file=../../tests/working-with-smart-contracts-cli.test.mjs start=startSendTokensToManufacturerForExternalDeploymentCommand end=endSendTokensToManufacturerForExternalDeploymentCommand -``` - -Deploy the manufacturer contract: - -```bash file=../../tests/working-with-smart-contracts-cli.test.mjs start=startManufacturerExternalDeploymentCommand end=endManufacturerExternalDeploymentCommand -``` \ No newline at end of file diff --git a/docs/tests/working-with-smart-contracts-cli.test.mjs b/docs/tests/working-with-smart-contracts-cli.test.mjs index eb5b6fa46..04215a305 100644 --- a/docs/tests/working-with-smart-contracts-cli.test.mjs +++ b/docs/tests/working-with-smart-contracts-cli.test.mjs @@ -93,54 +93,4 @@ describe.sequential("CLI deployment tests", async () => { expect(stdout).toMatch(/new-product/); }, ); - - test.sequential("external deployment of Retailer and Manufacturer is successful", async () => { - const SALT = BigInt(Math.floor(Math.random() * 10000)); - //startExternalRetailerAddressCommand - const RETAILER_ADDRESS_COMMAND = `${NIL_GLOBAL} contract address ./tests/Retailer/Retailer.bin --shard-id 1 --salt ${SALT} ${CONFIG_FLAG}`; - //endExternalRetailerAddressCommand - - let { stdout, stderr } = await exec(RETAILER_ADDRESS_COMMAND); - expect(stdout).toMatch(ADDRESS_PATTERN); - let addressMatches = stdout.match(ADDRESS_PATTERN); - RETAILER_ADDRESS = addressMatches[0]; - - const AMOUNT = 10000000; - - //startSendTokensToRetailerForExternalDeploymentCommand - const RETAILER_SEND_TOKENS_COMMAND_EXTERNAL = `${NIL_GLOBAL} smart-account send-tokens ${RETAILER_ADDRESS} ${AMOUNT} ${CONFIG_FLAG}`; - //endSendTokensToRetailerForExternalDeploymentCommand - - await exec(RETAILER_SEND_TOKENS_COMMAND_EXTERNAL); - - //startRetailerExternalDeploymentCommand - const RETAILER_EXTERNAL_DEPLOYMENT_COMMAND = `${NIL_GLOBAL} contract deploy ./tests/Retailer/Retailer.bin --shard-id 1 --salt ${SALT} ${CONFIG_FLAG}`; - //endRetailerExternalDeploymentCommand - - ({ stdout, stderr } = await exec(RETAILER_EXTERNAL_DEPLOYMENT_COMMAND)); - expect(stdout).toBeDefined; - expect(stdout).toMatch(CONTRACT_ADDRESS_PATTERN); - - //startExternalManufacturerAddressCommand - const MANUFACTURER_ADDRESS_COMMAND = `${NIL_GLOBAL} contract address ./tests/Manufacturer/Manufacturer.bin ${PUBKEY} ${RETAILER_ADDRESS} --shard-id 2 --salt ${SALT} ${CONFIG_FLAG} --abi ./tests/Manufacturer/Manufacturer.abi`; - //endExternalManufacturerAddressCommand - - ({ stdout, stderr } = await exec(MANUFACTURER_ADDRESS_COMMAND)); - addressMatches = stdout.match(ADDRESS_PATTERN); - MANUFACTURER_ADDRESS = addressMatches[0]; - - //startSendTokensToManufacturerForExternalDeploymentCommand - const MANUFACTURER_SEND_TOKENS_COMMAND_EXTERNAL = `${NIL_GLOBAL} smart-account send-tokens ${MANUFACTURER_ADDRESS} ${AMOUNT} ${CONFIG_FLAG}`; - //endSendTokensToManufacturerForExternalDeploymentCommand - - await exec(MANUFACTURER_SEND_TOKENS_COMMAND_EXTERNAL); - - //startManufacturerExternalDeploymentCommand - const MANUFACTURER_EXTERNAL_DEPLOYMENT_COMMAND = `${NIL_GLOBAL} contract deploy ./tests/Manufacturer/Manufacturer.bin ${PUBKEY} ${RETAILER_ADDRESS} --salt ${SALT} --shard-id 2 --abi ./tests/Manufacturer/Manufacturer.abi ${CONFIG_FLAG}`; - //endManufacturerExternalDeploymentCommand - - ({ stdout, stderr } = await exec(MANUFACTURER_EXTERNAL_DEPLOYMENT_COMMAND)); - expect(stdout).toBeDefined; - expect(stdout).toMatch(CONTRACT_ADDRESS_PATTERN); - }); }); diff --git a/explorer_frontend/src/features/account-connector/init.ts b/explorer_frontend/src/features/account-connector/init.ts index 50f277b90..891f2a3ff 100644 --- a/explorer_frontend/src/features/account-connector/init.ts +++ b/explorer_frontend/src/features/account-connector/init.ts @@ -116,7 +116,7 @@ createSmartAccountFx.use(async ({ privateKey, rpcUrl }) => { const code = await client.getCode(smartAccount.address); if (code.length === 0) { - await smartAccount.selfDeploy(true); + await smartAccount.selfDeploy(faucetClient); } setInitializingSmartAccountState("Adding some tokens..."); diff --git a/hardhat-plugin/src/core/wallet.ts b/hardhat-plugin/src/core/wallet.ts index 36e3335ab..bb86f085f 100644 --- a/hardhat-plugin/src/core/wallet.ts +++ b/hardhat-plugin/src/core/wallet.ts @@ -3,7 +3,6 @@ import { LocalECDSAKeySigner, type PublicClient, SmartAccountV1, - convertEthToWei, generateRandomPrivateKey, topUp, } from "@nilfoundation/niljs"; @@ -25,23 +24,10 @@ export async function deployWallet( signer, }); - if (faucetClient) { - const faucets = await faucetClient.getAllFaucets(); - await faucetClient.topUpAndWaitUntilCompletion( - { - amount: convertEthToWei(1), - smartAccountAddress: address, - faucetAddress: faucets.NIL, - }, - client, - ); - console.log("Faucet depositing to smart account", smartAccount.address); - } - const deployed = await smartAccount.checkDeploymentStatus(); - if (!deployed) { + if (faucetClient && !deployed) { console.log("Deploying smartAccount", smartAccount.address); - await smartAccount.selfDeploy(true); + await smartAccount.selfDeploy(faucetClient); } return smartAccount; } @@ -51,6 +37,7 @@ export async function createSmartAccount( config: CreateSmartAccountConfig, ): Promise { const client = hre.nil.getPublicClient(); + const faucetClient = hre.nil.getFaucetClient(); const pk = generateRandomPrivateKey(); const signer = new LocalECDSAKeySigner({ privateKey: pk, @@ -67,7 +54,7 @@ export async function createSmartAccount( await topUpSmartAccount(smartAccount.address, (hre.network.config as HttpNetworkConfig).url); } - await smartAccount.selfDeploy(true); + await smartAccount.selfDeploy(faucetClient); console.log("SmartAccount PK:", pk); return smartAccount; } diff --git a/hardhat-plugin/src/index.ts b/hardhat-plugin/src/index.ts index 36de67bc2..0bb41a95a 100644 --- a/hardhat-plugin/src/index.ts +++ b/hardhat-plugin/src/index.ts @@ -1,6 +1,7 @@ import { extendEnvironment } from "hardhat/config"; import "./tasks/wallet"; import { + FaucetClient, HttpTransport, LocalECDSAKeySigner, PublicClient, @@ -22,6 +23,9 @@ extendEnvironment((hre) => { const publicClient = new PublicClient({ transport: nilProvider, }); + const faucetClient = new FaucetClient({ + transport: nilProvider, + }); const pk = <`0x${string}`>`0x${process.env.PRIVATE_KEY}`; const signer = new LocalECDSAKeySigner({ @@ -30,6 +34,9 @@ extendEnvironment((hre) => { hre.nil = { provider: publicClient, + getFaucetClient: () => { + return faucetClient; + }, getPublicClient: () => { return publicClient; }, diff --git a/hardhat-plugin/src/types.ts b/hardhat-plugin/src/types.ts index 3ae799542..7d72f3ffa 100644 --- a/hardhat-plugin/src/types.ts +++ b/hardhat-plugin/src/types.ts @@ -1,6 +1,7 @@ import type { CommonReadContractMethods, CommonWriteContractMethods, + FaucetClient, IAddress, ISigner, PublicClient, @@ -54,6 +55,7 @@ export declare function createSmartAccount( export type NilHelper = { provider: PublicClient; getPublicClient: () => PublicClient; + getFaucetClient: () => FaucetClient; getSmartAccount: () => Promise; getContractAt: typeof getContractAt; deployContract: typeof deployContract; diff --git a/nil/cmd/nil/internal/contract/contract.go b/nil/cmd/nil/internal/contract/contract.go index 21ae333dd..0bf4a7343 100644 --- a/nil/cmd/nil/internal/contract/contract.go +++ b/nil/cmd/nil/internal/contract/contract.go @@ -29,7 +29,6 @@ func GetCommand(cfg *common.Config) *cobra.Command { GetTokensCommand(cfg), GetCodeCommand(cfg), GetCallReadonlyCommand(cfg), - GetDeployCommand(cfg), GetSendExternalTransactionCommand(cfg), GetEstimateFeeCommand(cfg), GetTopUpCommand(cfg), diff --git a/nil/cmd/nil/internal/contract/deploy.go b/nil/cmd/nil/internal/contract/deploy.go deleted file mode 100644 index 274f91a06..000000000 --- a/nil/cmd/nil/internal/contract/deploy.go +++ /dev/null @@ -1,108 +0,0 @@ -package contract - -import ( - "fmt" - - "github.com/NilFoundation/nil/nil/cmd/nil/common" - libcommon "github.com/NilFoundation/nil/nil/common" - "github.com/NilFoundation/nil/nil/internal/types" - "github.com/NilFoundation/nil/nil/services/cliservice" - "github.com/spf13/cobra" -) - -func GetDeployCommand(cfg *common.Config) *cobra.Command { - params := &contractParams{ - Params: &common.Params{}, - } - - cmd := &cobra.Command{ - Use: "deploy [path to file] [args...]", - Short: "Deploy a smart contract", - Long: "Deploy a smart contract with the specified hex-bytecode from stdin or from a file", - Args: cobra.MinimumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return runDeploy(cmd, args, cfg, params) - }, - SilenceUsage: true, - } - - setDeployFlags(cmd, params) - - return cmd -} - -func setDeployFlags(cmd *cobra.Command, params *contractParams) { - cmd.Flags().Var( - types.NewShardId(¶ms.shardId, types.BaseShardId), - shardIdFlag, - "Specify the shard ID to interact with", - ) - - params.salt = *types.NewUint256(0) - cmd.Flags().Var( - ¶ms.salt, - saltFlag, - "The salt for deployment transaction", - ) - - cmd.Flags().StringVar( - ¶ms.AbiPath, - abiFlag, - "", - "The path to the ABI file", - ) - - cmd.Flags().BoolVar( - ¶ms.noWait, - noWaitFlag, - false, - "Define whether the command should wait for the receipt", - ) - - cmd.Flags().Var( - ¶ms.Fee.FeeCredit, - feeCreditFlag, - "The deployment fee credit. If set to 0, it will be estimated automatically", - ) -} - -func runDeploy(cmd *cobra.Command, cmdArgs []string, cfg *common.Config, params *contractParams) error { - service := cliservice.NewService(cmd.Context(), common.GetRpcClient(), cfg.PrivateKey, nil) - - var filename string - var args []string - if len(cmdArgs) > 0 { - filename = cmdArgs[0] - args = cmdArgs[1:] - } - - bytecode, err := common.ReadBytecode(filename, params.AbiPath, args) - if err != nil { - return err - } - - payload := types.BuildDeployPayload(bytecode, libcommon.Hash(params.salt.Bytes32())) - - txnHash, addr, err := service.DeployContractExternal(params.shardId, payload, params.Fee) - if err != nil { - return err - } - - if !params.noWait { - if _, err := service.WaitForReceipt(txnHash); err != nil { - return err - } - } - - if !common.Quiet { - fmt.Print("Transaction hash: ") - } - fmt.Println(txnHash) - - if !common.Quiet { - fmt.Print("Contract address: ") - } - fmt.Println(addr) - - return nil -} diff --git a/nil/cmd/nil/internal/smartaccount/new.go b/nil/cmd/nil/internal/smartaccount/new.go index 965dfc77d..7f104be9f 100644 --- a/nil/cmd/nil/internal/smartaccount/new.go +++ b/nil/cmd/nil/internal/smartaccount/new.go @@ -74,8 +74,8 @@ func runNew(cmd *cobra.Command, _ []string, cfg *common.Config, params *smartAcc } srv := cliservice.NewService(cmd.Context(), common.GetRpcClient(), cfg.PrivateKey, faucet) check.PanicIfNotf(cfg.PrivateKey != nil, "A private key is not set in the config file") - smartAccountAddress, err := srv.CreateSmartAccount(params.shardId, ¶ms.salt, amount, - types.NewFeePackFromFeeCredit(params.Fee.FeeCredit), &cfg.PrivateKey.PublicKey) + + smartAccountAddress, err := srv.DeploySmartAccount(params.shardId, cfg.PrivateKey, params.salt, amount) if err != nil { return err } diff --git a/nil/cmd/nil_block_generator/internal/commands/client.go b/nil/cmd/nil_block_generator/internal/commands/client.go index 12e0d48ac..00d69c633 100644 --- a/nil/cmd/nil_block_generator/internal/commands/client.go +++ b/nil/cmd/nil_block_generator/internal/commands/client.go @@ -115,8 +115,6 @@ func CreateNewSmartAccount(rpcEndpoint string, logger logging.Logger) (string, s salt := types.NewUint256(0) amount := types.NewValueFromUint64(2_000_000_000_000_000) - fee := types.NewFeePackFromFeeCredit(types.NewValueFromUint64(200_000_000_000_000)) - srv, err := CreateCliService(rpcEndpoint, hexKey, logger) if err != nil { return "", "", err @@ -125,7 +123,8 @@ func CreateNewSmartAccount(rpcEndpoint string, logger logging.Logger) (string, s if err != nil { return "", "", err } - smartAccount, err := srv.CreateSmartAccount(types.BaseShardId, salt, amount, fee, &privateKey.PublicKey) + + smartAccount, err := srv.DeploySmartAccount(types.BaseShardId, privateKey, *salt, amount) if err != nil { return "", "", err } diff --git a/nil/internal/collate/proposer.go b/nil/internal/collate/proposer.go index c008156db..2bf2e1eb3 100644 --- a/nil/internal/collate/proposer.go +++ b/nil/internal/collate/proposer.go @@ -306,8 +306,11 @@ func (p *proposer) handleTransactionsFromPool() error { if res := execution.ValidateExternalTransaction(p.executionState, txn); res.FatalError != nil { return false, res.FatalError } else if res.Failed() { - p.logger.Info().Stringer(logging.FieldTransactionHash, txnHash). - Err(res.Error).Msg("External txn validation failed. Saved failure receipt. Dropping...") + p.logger.Info(). + Stringer(logging.FieldTransactionHash, txnHash). + Stringer(logging.FieldTransactionTo, mt.To). + Err(res.Error). + Msg("External txn validation failed. Saved failure receipt. Dropping...") execution.AddFailureReceipt(txnHash, txn.To, res) unverified = append(unverified, txnHash) diff --git a/nil/internal/execution/transactions.go b/nil/internal/execution/transactions.go index 07b67c795..4f872cbb7 100644 --- a/nil/internal/execution/transactions.go +++ b/nil/internal/execution/transactions.go @@ -110,7 +110,7 @@ func (a accountPayer) AddBalance(amount types.Value) error { } func (a accountPayer) String() string { - return fmt.Sprintf("account %v", a.transaction.From.Hex()) + return fmt.Sprintf("account %x", a.transaction.From) } func buyGas(payer Payer, transaction *types.Transaction) error { diff --git a/nil/services/cliservice/contract.go b/nil/services/cliservice/contract.go index 36dc38512..4166b91b7 100644 --- a/nil/services/cliservice/contract.go +++ b/nil/services/cliservice/contract.go @@ -145,25 +145,6 @@ func (s *Service) DeployContractViaSmartAccount( return txHash, contractAddr, nil } -// DeployContractExternal deploys a new smart contract with the given bytecode via external transaction -func (s *Service) DeployContractExternal( - shardId types.ShardId, - payload types.DeployPayload, - fee types.FeePack, -) (common.Hash, types.Address, error) { - txHash, contractAddr, err := s.client.DeployExternal(s.ctx, shardId, payload, fee) - if err != nil { - s.logger.Error().Err(err).Msg("Failed to send new transaction") - return common.EmptyHash, types.EmptyAddress, err - } - s.logger.Info().Msgf("Contract address: 0x%x", contractAddr) - s.logger.Info(). - Stringer(logging.FieldShardId, shardId). - Stringer(logging.FieldTransactionHash, txHash). - Send() - return txHash, contractAddr, nil -} - // CallContract performs read-only call to the contract func (s *Service) CallContract( contract types.Address, fee types.FeePack, calldata []byte, overrides *jsonrpc.StateOverrides, diff --git a/nil/services/cliservice/faucet.go b/nil/services/cliservice/faucet.go index 409f4e009..2e1ff9e85 100644 --- a/nil/services/cliservice/faucet.go +++ b/nil/services/cliservice/faucet.go @@ -9,7 +9,6 @@ import ( "time" "github.com/NilFoundation/nil/nil/common" - "github.com/NilFoundation/nil/nil/common/check" "github.com/NilFoundation/nil/nil/common/logging" "github.com/NilFoundation/nil/nil/internal/contracts" "github.com/NilFoundation/nil/nil/internal/types" @@ -159,46 +158,22 @@ func (s *Service) TopUpViaFaucet(faucetAddress, contractAddressTo types.Address, return fmt.Errorf("failed to top up contract %s: %s", contractAddressTo, r.ErrorMessage) } -func (s *Service) CreateSmartAccount( +func (s *Service) Deploy( shardId types.ShardId, - salt *types.Uint256, + code []byte, + salt types.Uint256, balance types.Value, - fee types.FeePack, - pubKey *ecdsa.PublicKey, ) (types.Address, error) { - smartAccountCode := contracts.PrepareDefaultSmartAccountForOwnerCode(crypto.FromECDSAPub(pubKey)) - smartAccountAddress := s.ContractAddress(shardId, *salt, smartAccountCode) - - code, err := s.client.GetCode(s.ctx, smartAccountAddress, "latest") - if err != nil { - return types.EmptyAddress, err - } - if len(code) > 0 { - return smartAccountAddress, fmt.Errorf("%w: %s", ErrSmartAccountExists, smartAccountAddress) - } - - // NOTE: we deploy smart account code with ext transaction - // in current implementation this costs 629_160 - err = s.TopUpViaFaucet(types.FaucetAddress, smartAccountAddress, balance) - if err != nil { - return types.EmptyAddress, err - } + return s.faucetClient.Deploy(s.ctx, shardId, code, salt, balance) +} - deployPayload := types.BuildDeployPayload(smartAccountCode, common.Hash(salt.Bytes32())) - txnHash, addr, err := s.DeployContractExternal(shardId, deployPayload, fee) - if err != nil { - return types.EmptyAddress, err - } - check.PanicIfNotf(addr == smartAccountAddress, "contract was deployed to unexpected address") - res, err := s.WaitForReceipt(txnHash) - if err != nil { - return types.EmptyAddress, errors.New("error during waiting for receipt") - } - if !res.IsComplete() { - return types.EmptyAddress, errors.New("deploy transaction processing failed") - } - if !res.AllSuccess() { - return types.EmptyAddress, fmt.Errorf("deploy transaction processing failed: %s", res.ErrorMessage) - } - return addr, nil +func (s *Service) DeploySmartAccount( + shardId types.ShardId, + key *ecdsa.PrivateKey, + salt types.Uint256, + balance types.Value, +) (types.Address, error) { + pubkey := crypto.FromECDSAPub(&key.PublicKey) + smartAccountCode := contracts.PrepareDefaultSmartAccountForOwnerCode(pubkey) + return s.Deploy(shardId, smartAccountCode, salt, balance) } diff --git a/nil/services/faucet/client.go b/nil/services/faucet/client.go index 2df607d60..2923e10e7 100644 --- a/nil/services/faucet/client.go +++ b/nil/services/faucet/client.go @@ -11,6 +11,7 @@ import ( "github.com/NilFoundation/nil/nil/common" "github.com/NilFoundation/nil/nil/common/version" "github.com/NilFoundation/nil/nil/internal/types" + "github.com/ethereum/go-ethereum/common/hexutil" ) type Client struct { @@ -79,6 +80,24 @@ func (c *Client) TopUpViaFaucet( return hash, nil } +func (c *Client) Deploy( + ctx context.Context, + shardId types.ShardId, + code hexutil.Bytes, + salt types.Uint256, + amount types.Value, +) (types.Address, error) { + response, err := c.sendRequest(ctx, "faucet_deploy", []any{shardId, code, salt, amount}) + if err != nil { + return types.EmptyAddress, err + } + var addr types.Address + if err := json.Unmarshal(response, &addr); err != nil { + return types.EmptyAddress, err + } + return addr, nil +} + func (c *Client) GetFaucets(ctx context.Context) (map[string]types.Address, error) { faucets := make(map[string]types.Address) response, err := c.sendRequest(ctx, "faucet_getFaucets", []any{}) diff --git a/nil/services/faucet/jsonrpc.go b/nil/services/faucet/jsonrpc.go index cdb47c2c0..91335e7ea 100644 --- a/nil/services/faucet/jsonrpc.go +++ b/nil/services/faucet/jsonrpc.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "math/big" "sync" + "time" "github.com/NilFoundation/nil/nil/client" "github.com/NilFoundation/nil/nil/client/rpc" @@ -13,11 +15,15 @@ import ( "github.com/NilFoundation/nil/nil/internal/types" "github.com/NilFoundation/nil/nil/services/rpc/jsonrpc" "github.com/NilFoundation/nil/nil/services/rpc/transport" + "github.com/ethereum/go-ethereum/common/hexutil" ) type API interface { TopUpViaFaucet( ctx context.Context, faucetAddress, contractAddressTo types.Address, amount types.Value) (common.Hash, error) + Deploy( + ctx context.Context, shardId types.ShardId, pubKey hexutil.Bytes, salt types.Uint256, amount types.Value, + ) (types.Address, error) GetFaucets() map[string]types.Address } @@ -62,11 +68,11 @@ func (c *APIImpl) getOrFetchSeqno(ctx context.Context, faucetAddress types.Addre return seqno, nil } -func (c *APIImpl) TopUpViaFaucet( +func (c *APIImpl) sendRawTransactionWithRetry( ctx context.Context, faucetAddress types.Address, - contractAddressTo types.Address, - amount types.Value, + calldata []byte, + gas types.Gas, ) (common.Hash, error) { c.mu.Lock() defer c.mu.Unlock() @@ -76,20 +82,12 @@ func (c *APIImpl) TopUpViaFaucet( return common.EmptyHash, err } - contractName := contracts.NameFaucet - if faucetAddress != types.FaucetAddress { - contractName = contracts.NameFaucetToken - } - callData, err := contracts.NewCallData(contractName, "withdrawTo", contractAddressTo, amount.ToBig()) - if err != nil { - return common.EmptyHash, err - } extTxn := &types.ExternalTransaction{ To: faucetAddress, - Data: callData, + Data: calldata, Seqno: seqno, Kind: types.ExecutionTransactionKind, - FeePack: types.NewFeePackFromGas(100_000), + FeePack: types.NewFeePackFromGas(gas), } data, err := extTxn.MarshalNil() @@ -125,9 +123,65 @@ func (c *APIImpl) TopUpViaFaucet( c.seqnos[faucetAddress] = seqno + 1 + var receipt *jsonrpc.RPCReceipt + for { + var err error + receipt, err = c.client.GetInTransactionReceipt(ctx, hash) + if err != nil { + return common.EmptyHash, fmt.Errorf("failed to get receipt for transaction %s: %w", hash, err) + } + if receipt.IsComplete() { + break + } + select { + case <-ctx.Done(): + return common.EmptyHash, ctx.Err() + case <-time.After(100 * time.Millisecond): + } + } + + if receipt == nil || !receipt.AllSuccess() { + return common.EmptyHash, fmt.Errorf("transaction %s for failed", hash) + } + return hash, nil } +func (c *APIImpl) TopUpViaFaucet( + ctx context.Context, + faucetAddress types.Address, + contractAddressTo types.Address, + amount types.Value, +) (common.Hash, error) { + contractName := contracts.NameFaucet + if faucetAddress != types.FaucetAddress { + contractName = contracts.NameFaucetToken + } + callData, err := contracts.NewCallData(contractName, "withdrawTo", contractAddressTo, amount.ToBig()) + if err != nil { + return common.EmptyHash, err + } + return c.sendRawTransactionWithRetry(ctx, faucetAddress, callData, 100_000) +} + +func (c *APIImpl) Deploy( + ctx context.Context, shardId types.ShardId, code hexutil.Bytes, salt types.Uint256, amount types.Value, +) (types.Address, error) { + contractName := contracts.NameFaucet + callData, err := contracts.NewCallData(contractName, "deploy", + big.NewInt(int64(shardId)), []byte(code), salt.Bytes32(), amount.ToBig()) + if err != nil { + return types.EmptyAddress, err + } + + if _, err := c.sendRawTransactionWithRetry(ctx, types.FaucetAddress, callData, 5_000_000); err != nil { + return types.EmptyAddress, err + } + + dp := types.BuildDeployPayload(types.Code(code), salt.Bytes32()) + return types.CreateAddress(shardId, dp), nil +} + func (c *APIImpl) GetFaucets() map[string]types.Address { return types.GetTokens() } diff --git a/nil/services/nil_load_generator/contracts/utils.go b/nil/services/nil_load_generator/contracts/utils.go index b095fa5c5..9def0bedd 100644 --- a/nil/services/nil_load_generator/contracts/utils.go +++ b/nil/services/nil_load_generator/contracts/utils.go @@ -33,8 +33,7 @@ func NewSmartAccount(service *cliservice.Service, shardId types.ShardId) (SmartA } salt := types.NewUint256(0) - smartAccountAdr, err := service.CreateSmartAccount( - shardId, salt, types.GasToValue(1_000_000_000), types.FeePack{}, &pk.PublicKey) + smartAccountAdr, err := service.DeploySmartAccount(shardId, pk, *salt, types.GasToValue(1_000_000_000)) if err != nil { if !strings.Contains(err.Error(), "smart account already exists") { return SmartAccount{}, err diff --git a/nil/services/relayer/internal/l2/init.go b/nil/services/relayer/internal/l2/init.go index 882162b69..600374915 100644 --- a/nil/services/relayer/internal/l2/init.go +++ b/nil/services/relayer/internal/l2/init.go @@ -91,7 +91,6 @@ func initDebugL2SmartAccount( debugDeployer := cliservice.NewService(ctx, l2Client, key, faucetClient) amount := types.NewValueFromUint64(2_000_000_000_000_000) - fee := types.NewFeePackFromFeeCredit(types.NewValueFromUint64(200_000_000_000_000)) salt := types.NewUint256(0) if len(config.SmartAccountSalt) > 0 { @@ -103,13 +102,7 @@ func initDebugL2SmartAccount( logger.Warn().Msg("smart account salt is not set, using default") } - addr, err := debugDeployer.CreateSmartAccount( - types.BaseShardId, - salt, - amount, - fee, - &key.PublicKey, - ) + addr, err := debugDeployer.DeploySmartAccount(types.BaseShardId, key, *salt, amount) if errors.Is(err, cliservice.ErrSmartAccountExists) { if config.SmartAccountAddress != "" { check.PanicIfNotf( diff --git a/nil/tests/cli/cli_test.go b/nil/tests/cli/cli_test.go index 7351c4931..3ded0463f 100644 --- a/nil/tests/cli/cli_test.go +++ b/nil/tests/cli/cli_test.go @@ -214,8 +214,8 @@ func (s *SuiteCliService) testNewSmartAccountOnShard(shardId types.ShardId) { crypto.FromECDSAPub(&ownerPrivateKey.PublicKey)) code := types.BuildDeployPayload(smartAccountCode, common.EmptyHash) expectedAddress := types.CreateAddress(shardId, code) - smartAccountAddres, err := s.cli.CreateSmartAccount(shardId, types.NewUint256(0), types.GasToValue(10_000_000), - types.FeePack{}, &ownerPrivateKey.PublicKey) + smartAccountAddres, err := s.cli.DeploySmartAccount(shardId, ownerPrivateKey, *types.NewUint256(0), + types.GasToValue(10_000_000)) s.Require().NoError(err) s.Require().Equal(expectedAddress, smartAccountAddres) } @@ -363,7 +363,7 @@ faucet_endpoint = {{ .FaucetUrl }} s.Run("Check seqno", func() { res := s.RunCli("-c", cfgPath, "smart-account", "seqno") - s.Contains(res, "Smart account seqno: 1") + s.Contains(res, "Smart account seqno: 0") }) var addr string @@ -392,7 +392,7 @@ faucet_endpoint = {{ .FaucetUrl }} s.Run("Check seqno", func() { res := s.RunCli("-c", cfgPath, "smart-account", "seqno") - s.Contains(res, "Smart account seqno: 2") + s.Contains(res, "Smart account seqno: 1") res = s.RunCli("-c", cfgPath, "contract", "seqno", addr) s.Contains(res, "Contract seqno: 0") diff --git a/nil/tests/faucet/faucet_test.go b/nil/tests/faucet/faucet_test.go index acc9e4f9f..94881cf07 100644 --- a/nil/tests/faucet/faucet_test.go +++ b/nil/tests/faucet/faucet_test.go @@ -40,14 +40,17 @@ func (s *SuiteFaucet) TearDownSuite() { s.Cancel() } -func (s *SuiteFaucet) createSmartAccountViaFaucet(ownerPrivateKey *ecdsa.PrivateKey, value int64) types.Address { +func (s *SuiteFaucet) createSmartAccountViaFaucet( + shardId types.ShardId, ownerPrivateKey *ecdsa.PrivateKey, value int64, +) types.Address { s.T().Helper() ownerPublicKey := crypto.FromECDSAPub(&ownerPrivateKey.PublicKey) + smartAccountCode := contracts.PrepareDefaultSmartAccountForOwnerCode(ownerPublicKey) salt := uint256.NewInt(123).Bytes32() callData, err := contracts.NewCallData( - contracts.NameFaucet, "createSmartAccount", ownerPublicKey, salt, big.NewInt(value)) + contracts.NameFaucet, "deploy", big.NewInt(int64(shardId)), []byte(smartAccountCode), salt, big.NewInt(value)) s.Require().NoError(err) resHash, err := s.DefaultClient.SendExternalTransaction( @@ -57,9 +60,8 @@ func (s *SuiteFaucet) createSmartAccountViaFaucet(ownerPrivateKey *ecdsa.Private res := s.WaitIncludedInMain(resHash) s.Require().True(res.AllSuccess()) - // Checking whether the address generated by CREATE2 matches the expected one - smartAccountCode := contracts.PrepareDefaultSmartAccountForOwnerCode(ownerPublicKey) - smartAccountAddr := types.CreateAddressForCreate2(types.FaucetAddress, smartAccountCode, salt) + dp := types.BuildDeployPayload(smartAccountCode, salt) + smartAccountAddr := types.CreateAddress(shardId, dp) s.Require().Equal(common.LeftPadBytes(smartAccountAddr.Bytes(), 32), []byte(res.Logs[0].Data[:])) return smartAccountAddr } @@ -69,7 +71,7 @@ func (s *SuiteFaucet) TestCreateSmartAccountViaFaucet() { s.Require().NoError(err) var value int64 = 100000 - smartAccountAddress := s.createSmartAccountViaFaucet(userPrivateKey, value) + smartAccountAddress := s.createSmartAccountViaFaucet(types.BaseShardId, userPrivateKey, value) blockNumber := transport.LatestBlockNumber balance, err := s.DefaultClient.GetBalance(s.Context, smartAccountAddress, diff --git a/niljs/examples/deploySmartAccount.ts b/niljs/examples/deploySmartAccount.ts index f4a462ba7..2b0f4b163 100644 --- a/niljs/examples/deploySmartAccount.ts +++ b/niljs/examples/deploySmartAccount.ts @@ -1,11 +1,11 @@ import { + FaucetClient, HttpTransport, LocalECDSAKeySigner, PublicClient, SmartAccountV1, bytesToHex, generateRandomPrivateKey, - topUp, } from "../src/index.js"; import { FAUCET_ENDPOINT, RPC_ENDPOINT } from "./helpers.js"; @@ -16,6 +16,12 @@ const client = new PublicClient({ shardId: 1, }); +const faucetClinet = new FaucetClient({ + transport: new HttpTransport({ + endpoint: FAUCET_ENDPOINT, + }), +}); + const signer = new LocalECDSAKeySigner({ privateKey: generateRandomPrivateKey(), }); @@ -33,13 +39,7 @@ const smartAccountAddress = smartAccount.address; console.log("smartAccountAddress", smartAccountAddress); -await topUp({ - address: smartAccountAddress, - faucetEndpoint: FAUCET_ENDPOINT, - rpcEndpoint: RPC_ENDPOINT, -}); - -await smartAccount.selfDeploy(true); +await smartAccount.selfDeploy(faucetClinet); const code = await client.getCode(smartAccountAddress, "latest"); diff --git a/niljs/examples/externalContractDeployment.ts b/niljs/examples/externalContractDeployment.ts deleted file mode 100644 index 2cc81d236..000000000 --- a/niljs/examples/externalContractDeployment.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - HttpTransport, - PublicClient, - SmartAccountV1, - bytesToHex, - externalDeploymentTransaction, - topUp, -} from "../src/index.js"; -import { FAUCET_ENDPOINT, RPC_ENDPOINT, generatePublicKey } from "./helpers.js"; - -const client = new PublicClient({ - transport: new HttpTransport({ - endpoint: RPC_ENDPOINT, - }), - shardId: 1, -}); - -const pubKey = generatePublicKey(); - -const chainId = await client.chainId(); -const gasPrice = await client.getGasPrice(1); - -const deploymentTransaction = externalDeploymentTransaction( - { - salt: 100n, - shard: 1, - bytecode: SmartAccountV1.code, - abi: SmartAccountV1.abi, - args: [pubKey], - feeCredit: 1_000_000n * gasPrice, - }, - chainId, -); -const addr = bytesToHex(deploymentTransaction.to); - -console.log("smartAccountAddress", addr); - -await topUp({ - address: addr, - faucetEndpoint: FAUCET_ENDPOINT, - rpcEndpoint: RPC_ENDPOINT, -}); - -await deploymentTransaction.send(client); - -while (true) { - const code = await client.getCode(addr, "latest"); - if (code.length > 0) { - console.log("code", bytesToHex(code)); - break; - } - await new Promise((resolve) => setTimeout(resolve, 1000)); -} - -console.log("Smart account deployed successfully"); diff --git a/niljs/src/clients/FaucetClient.ts b/niljs/src/clients/FaucetClient.ts index 8c027a17c..2af43c60d 100644 --- a/niljs/src/clients/FaucetClient.ts +++ b/niljs/src/clients/FaucetClient.ts @@ -14,6 +14,16 @@ export type TopUpParams = { amount: bigint; }; +/** + * The parameters for the deploy request. + */ +export type FaucetDeployParams = { + shardId: number; + code: Hex | Uint8Array; + salt: bigint; + amount: bigint; +}; + /** * FaucetClient is a client that interacts with the faucet api. * It is used to get information about the faucet and to top up the account with custom tokens. @@ -60,6 +70,22 @@ class FaucetClient extends BaseClient { }); } + /** + * Deploys a contract to the specified shard with specified salt and amount of base tokens. + * @param param - The parameters for the deploy request. + * @param param.shardId - Destination shard ID. + * @param param.code - Deployment code of the contract. + * @param param.salt - Salt for the contract address. + * @param param.amount - Initial contract balance. + * @returns The contract address. + */ + public async deploy({ shardId, code, salt, amount }: FaucetDeployParams) { + return await this.request({ + method: "faucet_deploy", + params: [shardId, toHex(code), salt.toString(), amount.toString()], + }); + } + /** * Topups the smart account with the specified amount of token which can be issued by the faucet. * This function waits until the top up transaction is completed. diff --git a/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.test.ts b/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.test.ts index ab362f0be..7208af5b3 100644 --- a/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.test.ts +++ b/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.test.ts @@ -1,3 +1,4 @@ +import { FaucetClient } from "../../clients/FaucetClient.js"; import { PublicClient } from "../../clients/PublicClient.js"; import { LocalECDSAKeySigner } from "../../signers/LocalECDSAKeySigner.js"; import { generateRandomPrivateKey } from "../../signers/privateKey.js"; @@ -18,6 +19,10 @@ const client = new PublicClient({ shardId: 1, }); +const faucetClient = new FaucetClient({ + transport: new MockTransport(fn), +}); + test("Smart account creation test with salt and no salt", async ({ expect }) => { describe("empty smart account creation", () => { expect(() => new SmartAccountV1({} as SmartAccountV1Config)).toThrowError(); @@ -94,9 +99,8 @@ test("Smart account self deploy test", async ({ expect }) => { }); await expect(async () => { - const tx = await smartAccount.selfDeploy(true); - expect(tx).toBeDefined(); - expect(tx.hash).toBeDefined(); + const address = await smartAccount.selfDeploy(faucetClient); + expect(address).toBeDefined(); }).rejects.toThrowError(); }); diff --git a/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.ts b/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.ts index cba2e4467..17431138d 100644 --- a/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.ts +++ b/niljs/src/smart-accounts/SmartAccountV1/SmartAccountV1.ts @@ -2,6 +2,7 @@ import { SmartAccount } from "@nilfoundation/smart-contracts"; import type { Abi } from "abitype"; import invariant from "tiny-invariant"; import { bytesToHex, encodeDeployData, encodeFunctionData } from "viem"; +import type { FaucetClient } from "../../clients/FaucetClient.js"; import type { PublicClient } from "../../clients/PublicClient.js"; import type { ContractFunctionName } from "../../contract-factory/ContractFactory.js"; import { prepareDeployPart } from "../../encoding/deployPart.js"; @@ -171,15 +172,15 @@ export class SmartAccountV1 implements SmartAccountInterface { * Deploys the smart account. * * @async - * @param {boolean} [waitTillConfirmation=true] The flag that determines whether the function waits for deployment confirmation before exiting. - * @param {bigint} [feeCredit] The fee credit for processing the deployment transaction. If not set, it will be estimated automatically. - * @returns {Promise} A Transaction object that can be awaited for completion. + * @param {string} [faucetEndpoint] Faucet endpoint. + * @returns {Promise} An Address of the smart account that can be awaited for completion. * @example * import { Faucet, HttpTransport, LocalECDSAKeySigner, PublicClient, + FaucetClient, SmartAccountV1, generateRandomPrivateKey, } from '@nilfoundation/niljs'; @@ -189,11 +190,14 @@ export class SmartAccountV1 implements SmartAccountInterface { }), shardId: 1, }); + * const faucetClient = new FaucetClient({ + transport: new HttpTransport({ + endpoint: FAUCET_ENDPOINT, + }), + }); * const signer = new LocalECDSAKeySigner({ privateKey: generateRandomPrivateKey(), }); - * const faucet = new Faucet(client); - * await faucet.withdrawTo(smartAccountAddress, 100000n); * const pubkey = signer.getPublicKey(); * const smartAccount = new SmartAccountV1({ pubkey: pubkey, @@ -207,9 +211,9 @@ export class SmartAccountV1 implements SmartAccountInterface { salt: 100n, }), }); - * await smartAccount.selfDeploy(true); + * await smartAccount.selfDeploy(faucetClient); */ - async selfDeploy(waitTillConfirmation = true, feeCredit?: bigint): Promise { + async selfDeploy(faucetClient: FaucetClient, amount = 1_000_000_000_000_000_000n): Promise { invariant( typeof this.salt !== "undefined", "Salt is required for external deployment. Please provide salt for walelt", @@ -221,7 +225,6 @@ export class SmartAccountV1 implements SmartAccountInterface { ]); invariant(code.length === 0, "Contract already deployed"); - invariant(balance > 0n, "Insufficient balance"); const { data } = prepareDeployPart({ abi: SmartAccount.abi as Abi, @@ -231,45 +234,12 @@ export class SmartAccountV1 implements SmartAccountInterface { shard: this.shardId, }); - let refinedCredit = feeCredit; - // TODO: remove hardcoded value - let maxPriorityFeePerGas = 0n; - // TODO: remove hardcoded value - let maxFeePerGas = 1_000_000_000_000_000n; - - if (!refinedCredit) { - const balance = await this.getBalance(); - - const estimatedFee = await this.client.estimateGas( - { - flags: ["Deploy"], - to: this.address, - data: data, - }, - "latest", - ); - - refinedCredit = refinedCredit || estimatedFee.feeCredit; - maxPriorityFeePerGas = estimatedFee.averagePriorityFee; - maxFeePerGas = estimatedFee.maxBaseFee; - } - - const { hash } = await this.requestToSmartAccount({ - data: data, - deploy: true, - seqno: 0, - feeCredit: refinedCredit, - maxPriorityFeePerGas: maxPriorityFeePerGas, - maxFeePerGas: maxFeePerGas, + return faucetClient.deploy({ + shardId: this.shardId, + code: data.slice(0, data.length - 32), // remove salt from code + salt: refineBigintSalt(this.salt), + amount: amount, }); - - const tx = new Transaction(hash, this.client); - - if (waitTillConfirmation) { - await tx.wait(); - } - - return tx; } /** diff --git a/niljs/src/utils/faucet.ts b/niljs/src/utils/faucet.ts index 2ba424ccf..e5db894c1 100644 --- a/niljs/src/utils/faucet.ts +++ b/niljs/src/utils/faucet.ts @@ -44,3 +44,30 @@ export async function topUp({ client, ); } + +export async function deploy({ + faucetEndpoint, + shardId, + code, + salt, + amount, +}: { + faucetEndpoint: string; + shardId: number; + code: Uint8Array | Hex; + salt: bigint; + amount: bigint; +}): Promise { + const faucetClient = new FaucetClient({ + transport: new HttpTransport({ + endpoint: faucetEndpoint, + }), + }); + + return await faucetClient.deploy({ + shardId, + code, + salt, + amount, + }); +} diff --git a/niljs/src/utils/smart-account.ts b/niljs/src/utils/smart-account.ts index 01463a78f..680e86c36 100644 --- a/niljs/src/utils/smart-account.ts +++ b/niljs/src/utils/smart-account.ts @@ -1,9 +1,9 @@ +import { FaucetClient } from "../clients/FaucetClient.js"; import { PublicClient } from "../clients/PublicClient.js"; import { LocalECDSAKeySigner } from "../signers/LocalECDSAKeySigner.js"; import { generateRandomPrivateKey } from "../signers/privateKey.js"; import { SmartAccountV1 } from "../smart-accounts/SmartAccountV1/SmartAccountV1.js"; import { HttpTransport } from "../transport/HttpTransport.js"; -import { topUp } from "./faucet.js"; export async function generateSmartAccount(options: { shardId: number; @@ -17,6 +17,13 @@ export async function generateSmartAccount(options: { shardId: options.shardId, }); + const faucetClient = new FaucetClient({ + transport: new HttpTransport({ + endpoint: options.faucetEndpoint, + }), + shardId: options.shardId, + }); + const signer = new LocalECDSAKeySigner({ privateKey: generateRandomPrivateKey(), }); @@ -29,13 +36,7 @@ export async function generateSmartAccount(options: { salt: BigInt(Math.floor(Math.random() * 10000)), }); - await topUp({ - address: smartAccount.address, - rpcEndpoint: options.rpcEndpoint, - faucetEndpoint: options.faucetEndpoint, - }); - - await smartAccount.selfDeploy(); + await smartAccount.selfDeploy(faucetClient); return smartAccount; } diff --git a/niljs/test/integration/cometa.test.ts b/niljs/test/integration/cometa.test.ts index 2d1a9b683..e8aecc4a8 100644 --- a/niljs/test/integration/cometa.test.ts +++ b/niljs/test/integration/cometa.test.ts @@ -67,7 +67,7 @@ test("register contract", async () => { bytecode: base64ToHex(contractCompilationResult.initCode), abi: JSON.parse(contractCompilationResult.abi), args: [], - feeCredit: convertEthToWei(0.00001), + feeCredit: convertEthToWei(0.001), salt: BigInt(Math.floor(Math.random() * 10000000000000000)), shardId: 1, }); diff --git a/rollup-bridge-contracts/task/nil-smart-account.ts b/rollup-bridge-contracts/task/nil-smart-account.ts index cd3ae56ef..024bc164a 100644 --- a/rollup-bridge-contracts/task/nil-smart-account.ts +++ b/rollup-bridge-contracts/task/nil-smart-account.ts @@ -124,35 +124,19 @@ export async function generateNilSmartAccount(networkName: string): Promise<[Sma transport: new HttpTransport({ endpoint: faucetEndpoint }), }); - console.log(`about to topup owner via faucet`); - let topUpFaucet = await faucetClient.topUp({ - smartAccountAddress: smartAccount.address, - amount: convertEthToWei(0.1), - faucetAddress: process.env.NIL as `0x${string}`, - }); - console.log(`faucet topup initiation done`); - await waitTillCompleted(client, topUpFaucet); - + console.log(`about to deploy owner via faucet`); if ((await smartAccount.checkDeploymentStatus()) === false) { - await smartAccount.selfDeploy(true); + await smartAccount.selfDeploy(faucetClient); } - console.log(`about to topup testing account via faucet`); - topUpFaucet = await faucetClient.topUp({ - smartAccountAddress: depositRecipientSmartAccount.address, - amount: convertEthToWei(0.0001), - faucetAddress: process.env.NIL as `0x${string}`, - }); - console.log(`faucet topup initiation done`); - await waitTillCompleted(client, topUpFaucet); - + console.log(`about to deploy testing account via faucet`); if ((await depositRecipientSmartAccount.checkDeploymentStatus()) === false) { - await depositRecipientSmartAccount.selfDeploy(true); + await depositRecipientSmartAccount.selfDeploy(faucetClient); } console.log("✅ Smart Account Funded (100 ETH)"); - // update + // update const config: L2NetworkConfig = loadNilNetworkConfig(networkName); config.l2CommonConfig.owner = getCheckSummedAddress(smartAccountAddress); diff --git a/smart-contracts/contracts/Faucet.sol b/smart-contracts/contracts/Faucet.sol index 04edd8190..88a6cad21 100644 --- a/smart-contracts/contracts/Faucet.sol +++ b/smart-contracts/contracts/Faucet.sol @@ -76,15 +76,21 @@ contract Faucet { emit Send(addr, value); } - function createSmartAccount(bytes memory ownerPubkey, bytes32 salt, uint256 value) external returns (address) { - SmartAccount smartAccount = new SmartAccount{salt: salt}(ownerPubkey); - address addr = address(smartAccount); + function deploy( + uint shardId, + bytes memory code, + bytes32 salt, + uint256 value + ) external returns (address) { + address addr = Nil.asyncDeploy( + shardId, + address(this), + value, + code, + uint256(salt) + ); emit Deploy(addr); - - bool success = payable(addr).send(value); - require(success, "Send value failed"); emit Send(addr, value); - return addr; } } diff --git a/wallet-extension/src/features/blockchain/smartAccount.ts b/wallet-extension/src/features/blockchain/smartAccount.ts index 181882f05..0d98af2ea 100644 --- a/wallet-extension/src/features/blockchain/smartAccount.ts +++ b/wallet-extension/src/features/blockchain/smartAccount.ts @@ -15,10 +15,8 @@ import { import { SmartAccount } from "@nilfoundation/smart-contracts"; import { encodeFunctionData } from "viem"; import { ActivityType } from "../../background/storage"; -import { TokenNames } from "../components/token"; import { addActivity } from "../store/model/activities.ts"; import { generateRandomSalt } from "../utils"; -import { topUpSpecificToken } from "./faucet.ts"; // Create Public Client export function createClient(rpcEndpoint: string, shardId: number): PublicClient { @@ -73,23 +71,9 @@ export async function initializeOrDeploySmartAccount(params: { signer, }); - try { - // Top up smartAccount with 0.1 native token - await topUpSpecificToken( - smartAccount, - faucetClient, - TokenNames.NIL, - convertEthToWei(0.009), - false, - ); - } catch (e) { - console.error("Failed to top up smartAccount during deployment:", e); - throw new Error("Failed to top up smartAccount"); - } - try { // Deploy the smartAccount - await smartAccount.selfDeploy(true); + await smartAccount.selfDeploy(faucetClient); console.log("SmartAccount deployed successfully at:", smartAccount.address); } catch (e) { console.error("Failed to self-deploy the smartAccount:", e);