coinbase / coinbase-wallet-sdk

An open protocol that lets users connect their mobile wallets to your DApp
https://coinbase.github.io/coinbase-wallet-sdk/
MIT License
1.4k stars 521 forks source link

Bug: Can't deploy contract instance #1319

Closed mrosendin closed 3 weeks ago

mrosendin commented 3 weeks ago

Describe the bug

"Something went wrong" in contract deployment

Steps

Using the Smart Wallet connector for wagmi, I'm unable to deploy a contract instance but otherwise able to send transactions. In the JS console I see InvalidAddressError which I can only assume is because the wagmi/viem transaction builder sets tx.to to the zero address for contract deployments and this is not handled by Smart Wallet (???).

Deploying contract instances from the user smart wallet is kinda important for my use case.

Minimum reproducible example:

https://codesandbox.io/p/live/a2da5e3a-178c-480f-bca1-f955940c3eba

Simple Storage Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleStorage {
    uint256 private storedValue;

    constructor(uint256 initialValue) {
        storedValue = initialValue;
    }

    function set(uint256 newValue) public {
        storedValue = newValue;
    }

    function get() public view returns (uint256) {
        return storedValue;
    }
}

useDeployContract Hook

import { useCallback } from 'react';
import { usePublicClient, useWalletClient } from 'wagmi';

export const useDeployContract = () => {
  const publicClient = usePublicClient();
  const { data: walletClient } = useWalletClient();

  const deployContract = useCallback(
    async (abi: any[], bytecode: `0x${string}`, args: any[]) => {
      if (!walletClient) throw new Error('Wallet client not available');

      const hash = await walletClient.deployContract({
        abi,
        bytecode,
        args,
      });

      const receipt = await publicClient?.waitForTransactionReceipt({ hash });
      return receipt?.contractAddress;
    },
    [publicClient, walletClient]
  );

  return { deployContract };
};

Wagmi config

import { http, createConfig } from 'wagmi';
import { baseSepolia } from 'wagmi/chains';
import { coinbaseWallet } from 'wagmi/connectors';

export const config = createConfig({
  chains: [baseSepolia],
  connectors: [
    coinbaseWallet({ appName: 'app', preference: 'smartWalletOnly' }),
  ],
  transports: {
    [baseSepolia.id]: http()
  },
});

App Component

import React, { useState, useCallback } from 'react';
import { useConnect, useAccount, usePublicClient, useWalletClient } from 'wagmi';
import { useDeployContract } from './useDeployContract';

const abi = [
  {
    inputs: [{ internalType: 'uint256', name: 'initialValue', type: 'uint256' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'get',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'uint256', name: 'newValue', type: 'uint256' }],
    name: 'set',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];

const bytecode = '0x608060405234801561001057600080fd5b5060405161013838038061013883398181016040528101906100329190610088565b806000806101000a81548160ff021916908315150217905550506100f6565b600081519050610052816100ff565b92915050565b60006020828403121561006e57600080fd5b600061007c84828501610047565b91505092915050565b600061008f826100b5565b9050919050565b61009f816100c5565b81146100aa57600080fd5b50565b6000813590506100bc816100d6565b92915050565b6000602082840312156100d857600080fd5b60006100e6848285016100a3565b91505092915050565b6000806040838503121561010357600080fd5b6000610111858286016100a3565b9250506020610122858286016100a3565b9150509250929050565b600080fd5b610139816100c5565b811461014457600080fd5b50565b600081359050610156816100d6565b92915050565b60006020828403121561017257600080fd5b6000610180848285016100a3565b91505092915050565b6000806040838503121561019d57600080fd5b60006101ab858286016100a3565b92505060206101bc858286016100a3565b915050925092905056fea26469706673582212201d9f6a8dd1529a6d1da9c5f682f68e78c3f5f784b2e22b2c4d5fc758e248557164736f6c63430008000033';

const App = () => {
  const [loading, setLoading] = useState(false);
  const { connect, connectors } = useConnect();
  const { data: account } = useAccount();
  const { deployContract } = useDeployContract();
  const { data: walletClient } = useWalletClient();

  const connectWallet = useCallback(() => {
    const connector = connectors.find((c) => c.id === 'coinbaseWalletSDK'); 
    if (connector) {
      connect({ connector });
    }
  }, [connect, connectors]);

  const deployTestContract = async () => {
    try {
      setLoading(true);
      const contractAddress = await deployContract(abi, bytecode, [42]); // 42 is the initial value
      console.log(`Contract deployed at address: ${contractAddress}`);
      alert(`Contract deployed at address: ${contractAddress}`);
    } catch (error) {
      console.error('Error deploying contract:', error);
      alert(`Error deploying contract: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      {account?.status === 'disconnected' ? (
        <button onClick={connectWallet}>Connect Wallet</button>
      ) : (
        <button onClick={deployTestContract} disabled={loading}>
          {loading ? 'Deploying...' : 'Deploy Contract'}
        </button>
      )}
    </div>
  );
};

export default App;

Expected behavior

No error should be returned, the smart wallet modal should allow the user to sign the tx

Version

latest

Additional info

Screenshot 2024-06-10 at 10 25 43 PM

Screenshot 2024-06-10 at 10 26 09 PM

Desktop

macOS, Chrome Version 125.0.6422.142 (Official Build) (arm64)

Smartphone

No response

mrosendin commented 3 weeks ago

Solution: https://x.com/WilsonCusack/status/1800406476563038698

Call a create2deployer contract