0.0.9-alpha.0 • Published 1 year ago

@next-wallet/provider v0.0.9-alpha.0

Weekly downloads
-
License
MIT
Repository
-
Last release
1 year ago

流程

iframe

alt text

需要解决的问题:

  1. 业务系统与 iframe-web 打通授权,现存市场上的钱包 "auth 内嵌钱包",业务系统集成 auth,业务系统使用 auth 的 jwt
  2. iframe 约束消息范围(注:现存浏览器插件钱包如:metamask 等,全 origin 广播,任何页面都可以收到消息)

新增设备

alt text

创建嵌入钱包

alt text

生成私钥

alt text

签名交易

alt text

发交易

alt text

其他 api 同 viem

alt text

example

alt text

Embedded Wallet Provider

This package provides an EmbeddedWalletProvider class to manage blockchain interactions through an embedded wallet within an iframe. It supports general transactions, blob transactions, message signing, and chain management.

Table of Contents

Installation

To use this package, you'll need to install the required dependencies:

npm install viem @next-wallet/provider

Usage

To use the EmbeddedWalletProvider, you need to import it and initialize it with the required parameters.

import { defineChain, http } from 'viem';
import { sepolia } from 'viem/chains';
import { EmbeddedWalletProvider } from '@next-wallet/provider';

const defaultChain = defineChain({
  id: 1,
  name: 'Ethereum Mainnet',
  rpcUrls: ['https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'],
});

const params: EmbeddedWalletProviderParams = {
  defaultChain: sepolia,
  supportedChains: [sepolia],
  address: '0xYourWalletAddress', // @next-wallet/provider 创建的钱包地址
  iframeSrc: 'https://your-iframe-source.com', // your iframe
  onReady: (res) => {
    console.log('Iframe is ready', res);
  },
};

const walletProvider = new EmbeddedWalletProvider(params);

API

Constructor

EmbeddedWalletProvider(parameters: EmbeddedWalletProviderParams)

  • parameters: An object containing the following properties:
    • defaultChain: The default blockchain chain.
    • supportedChains: An array of supported blockchain chains.
    • address: The address of the embedded wallet.
    • iframeSrc: The source URL of the embedded wallet iframe.
    • transport: (Optional) The transport method for blockchain communication.
    • onReady: A callback function that is called when the iframe is ready.

Methods

createWallet(): Promise<ResponsePayload>

Creates a new wallet.

exportDeviceShare(address: Hex): Promise<ResponsePayload>

Exports the device share for the given address.

exportPrivateKey(address: Hex): Promise<ResponsePayload>

Exports the private key for the given address.

switchAddress(address: Hex): void

Switches the current address to the provided address.

getAllAddress(): Promise<ResponsePayload>

Gets all addresses associated with the wallet.

generateClient(): Promise<void>

Generates a public and wallet client.

signTransaction(transaction: GeneralTransaction | BlobTransaction): Promise<Hex>

Signs a transaction.

sendTransaction(transaction: GeneralTransaction | BlobTransaction): Promise<Hex>

Sends a signed transaction.

sendRawTransaction(args: SendRawTransactionParameters): Promise<Hex>

Sends a raw transaction.

waitForTransactionReceipt(args: WaitForTransactionReceiptParameters): Promise<TransactionReceipt>

Waits for the transaction receipt of the given transaction hash.

signMessage(message: string): Promise<Hex>

Signs a message.

signTypedData(typedData: TypedDataParameter): Promise<Hex>

Signs typed data (EIP-712).

sendBlobDataTransaction(): Promise<void>

(Not implemented) Sends a blob data transaction.

addChain(chain: Chain): Promise<void>

Adds a new chain to the supported chains.

switchChain(id: Chain['id']): Promise<void>

Switches to the chain with the given ID.

getChains(): Promise<Chain[]>

Gets all supported chains.

Example

Here is an example of how to use the EmbeddedWalletProvider to sign and send a transaction:

import { defineChain, http } from 'viem';
import { EmbeddedWalletProvider, EmbeddedWalletProviderParams } from './path-to-your-provider-file';

const defaultChain = defineChain({
  id: 1,
  name: 'Ethereum Mainnet',
  rpcUrls: ['https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID'],
});

const params: EmbeddedWalletProviderParams = {
  defaultChain,
  supportedChains: [defaultChain],
  address: '0xYourWalletAddress',
  iframeSrc: 'https://your-iframe-source.com',
  onReady: (res) => {
    console.log('Iframe is ready', res);
  },
};

const walletProvider = new EmbeddedWalletProvider(params);

async function sendTransaction() {
  const transaction = {
    maxFeePerGas: BigInt(20000000000),
    maxPriorityFeePerGas: BigInt(2000000000),
    gas: 21000,
    to: '0xRecipientAddress',
    value: BigInt(1000000000000000000), // 1 ETH
  };

  try {
    const signedTransaction = await walletProvider.signTransaction(transaction);
    const txHash = await walletProvider.sendTransaction(signedTransaction);
    console.log('Transaction sent with hash:', txHash);
  } catch (error) {
    console.error('Error sending transaction:', error);
  }
}

sendTransaction();

This example demonstrates how to initialize the EmbeddedWalletProvider, sign a transaction, and send it to the blockchain.

React Example

import { useCallback, useEffect, useRef, useState } from 'react';

// import { sepolia } from 'viem/chains';

import { EmbeddedWalletProvider } from '@next-wallet/provider';
import { Hex, TransactionReceipt, defineChain, parseEther, parseGwei } from 'viem';

interface Receipt extends Omit<TransactionReceipt, 'gasUsed' | 'blockNumber' | 'cumulativeGasUsed' | 'effectiveGasPrice'> {
  gasUsed: string;
  blockNumber: string;
  cumulativeGasUsed: string;
  effectiveGasPrice: string;
}

export const sepolia = defineChain({
  id: 11_155_111,
  name: 'Sepolia',
  nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
  rpcUrls: {
    default: {
      http: ['https://ethereum-sepolia-rpc.publicnode.com'],
    },
  },
  blockExplorers: {
    default: {
      name: 'Etherscan',
      url: 'https://sepolia.etherscan.io',
      apiUrl: 'https://api-sepolia.etherscan.io/api',
    },
  },
  contracts: {
    multicall3: {
      address: '0xca11bde05977b3631167028862be2a173976ca11',
      blockCreated: 751532,
    },
    ensRegistry: { address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e' },
    ensUniversalResolver: {
      address: '0xc8Af999e38273D658BE1b921b88A9Ddf005769cC',
      blockCreated: 5_317_080,
    },
  },
  testnet: true,
});

function App() {
  const walletProviderInstance = useRef<EmbeddedWalletProvider>();
  const [address, setAddress] = useState<Hex>();
  const [addressList, setAddressList] = useState<Hex[]>([]);
  const [embeededWalletIsReady, setEmbeededWalletIsReady] = useState(false);
  const [txReceipt, setTxReceipt] = useState<Receipt>();
  const [loading, setLoading] = useState(false);
  const [deviceShare, setDeviceShare] = useState<Hex>();
  const [privateKey, setPrivateKey] = useState('');
  useEffect(() => {
    handelMountIframe();
  }, []);

  const handelSendTransaction = useCallback(async () => {
    try {
      setLoading(true);

      const txhash = await walletProviderInstance.current?.sendTransaction({
        maxFeePerGas: parseGwei('20'),
        maxPriorityFeePerGas: parseGwei('3'),
        gas: 21000,
        to: '0x88E4CaB47eDfE9BaB5dB525c0F3598Cad73436CD',
        value: parseEther('0.00001'),
      });
      const receipt = await walletProviderInstance.current?.waitForTransactionReceipt({
        hash: txhash!,
      });
      if (receipt) {
        console.log('receipt', receipt);
        setTxReceipt({
          ...receipt,
          blockNumber: receipt.blockNumber.toString(),
          cumulativeGasUsed: receipt.cumulativeGasUsed.toString(),
          gasUsed: receipt.gasUsed.toString(),
          effectiveGasPrice: receipt.effectiveGasPrice.toString(),
        } as Receipt);
        setLoading(false);
      }
    } catch (error) {
      console.error('error', error);
    }
  }, []);

  const handelMountIframe = useCallback(() => {
    walletProviderInstance.current = new EmbeddedWalletProvider({
      iframeSrc: 'http://localhost:3000/apps/293c01a03c7e78b9fcf70627562b1a1d0864bfb3',
      defaultChain: sepolia,
      supportedChains: [sepolia],
      address: '0xFaA1311Dab753F720afCbdF37082E110Dc66846c',
      onReady: (res) => {
        console.log('res->handelMountIframe', res);
        setEmbeededWalletIsReady(true);
        getAllAddress();
      },
    });
  }, []);

  const getAllAddress = useCallback(() => {
    walletProviderInstance.current?.getAllAddress().then((res) => {
      console.log('res', res);
      if (res.success) {
        setAddressList(res.data);
      }
    });
  }, []);

  const handleSwitchAccount = useCallback((address: Hex) => {
    walletProviderInstance.current?.switchAddress(address);
    setAddress(address);
  }, []);

  const createWallet = useCallback(async () => {
    const address = await walletProviderInstance.current?.createWallet();
    console.log('createWallet->address', address);
    getAllAddress();
  }, [getAllAddress]);

  const exportDeviceShare = useCallback(async () => {
    const res = await walletProviderInstance.current?.exportDeviceShare(address!);
    console.log('res->exportDeviceShare', res);
    if (res?.success) {
      setDeviceShare(res.data);
    }
  }, [address]);

  const exportPrivateKey = useCallback(async () => {
    const res = await walletProviderInstance.current?.exportPrivateKey(address!);
    console.log('res->exportDeviceShare', res);
    if (res?.success) {
      setPrivateKey(res.data);
    }
  }, [address]);

  return (
    <div>
      <button onClick={handelMountIframe}>step1: handelMountIframe</button>
      <br />
      <button disabled={!embeededWalletIsReady} onClick={getAllAddress}>
        step2: getAllAddress
      </button>
      {addressList.map((item) => (
        <p key={item} onClick={() => handleSwitchAccount(item)} style={{ color: item === address ? '#4980ff' : '' }}>
          {item}
        </p>
      ))}
      <br />
      <button disabled={!embeededWalletIsReady} onClick={createWallet}>
        创建一个嵌入钱包
      </button>

      <h3>step3: 选中第一个地址 account: {address}</h3>

      <button disabled={!embeededWalletIsReady || !address} onClick={handelSendTransaction}>
        step3: 选中第一个地址,发送交易
      </button>
      <pre>{loading ? '交易确认中...' : JSON.stringify(txReceipt, null, 4)}</pre>
      <hr />
      <button disabled={!embeededWalletIsReady || !address} onClick={exportDeviceShare}>
        选中第一个地址,导出deviceShare
      </button>
      <p>deviceShare:{deviceShare}</p>
      <button disabled={!embeededWalletIsReady || !address} onClick={exportPrivateKey}>
        选中第一个地址,导出priviteKey
      </button>
      <p>privateKey:{privateKey}</p>
    </div>
  );
}

export default App;
0.0.1-beta.2

1 year ago

0.0.1-beta.1

1 year ago

0.0.1-beta.0

1 year ago

0.0.9-alpha.0

1 year ago

0.0.5-alpha.0

1 year ago

0.0.3-alpha.0

1 year ago

0.0.4-alpha.0

1 year ago

0.0.2-alpha.0

1 year ago

0.0.1-alpha.0

1 year ago