From 6065b593ab9f690d927ece131a007cc71764ca7d Mon Sep 17 00:00:00 2001 From: Oleg Babin Date: Wed, 11 Jun 2025 00:31:13 +0400 Subject: [PATCH] drop external deployment This patch removes external deployment from public interfaces (CLI, niljs) and replaces it with deployment via faucet. We need it to support EOA because each address with positive balance and without contract code should be considered as EOA. But right now this address can be used to deploy a contract. --- clijs/src/commands/smart-account/new.ts | 18 +-- clijs/test/setup.ts | 15 +-- docs/nil/nilcli/deploying-smart-contract.mdx | 33 ------ .../working-with-smart-contracts-cli.test.mjs | 50 -------- .../src/features/account-connector/init.ts | 2 +- hardhat-plugin/src/core/wallet.ts | 21 +--- hardhat-plugin/src/index.ts | 7 ++ hardhat-plugin/src/types.ts | 2 + nil/cmd/nil/internal/contract/contract.go | 1 - nil/cmd/nil/internal/contract/deploy.go | 108 ------------------ nil/cmd/nil/internal/smartaccount/new.go | 4 +- .../internal/commands/client.go | 5 +- nil/internal/collate/proposer.go | 7 +- nil/internal/execution/transactions.go | 2 +- nil/services/cliservice/contract.go | 19 --- nil/services/cliservice/faucet.go | 53 +++------ nil/services/faucet/client.go | 19 +++ nil/services/faucet/jsonrpc.go | 80 ++++++++++--- .../nil_load_generator/contracts/utils.go | 3 +- nil/services/relayer/internal/l2/init.go | 9 +- nil/tests/cli/cli_test.go | 8 +- nil/tests/faucet/faucet_test.go | 14 ++- niljs/examples/deploySmartAccount.ts | 16 +-- niljs/examples/externalContractDeployment.ts | 55 --------- niljs/src/clients/FaucetClient.ts | 26 +++++ .../SmartAccountV1/SmartAccountV1.test.ts | 10 +- .../SmartAccountV1/SmartAccountV1.ts | 62 +++------- niljs/src/utils/faucet.ts | 27 +++++ niljs/src/utils/smart-account.ts | 17 +-- niljs/test/integration/cometa.test.ts | 2 +- .../task/nil-smart-account.ts | 26 +---- smart-contracts/contracts/Faucet.sol | 20 ++-- .../src/features/blockchain/smartAccount.ts | 18 +-- 33 files changed, 256 insertions(+), 503 deletions(-) delete mode 100644 nil/cmd/nil/internal/contract/deploy.go delete mode 100644 niljs/examples/externalContractDeployment.ts 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);