diff --git a/pages/core-api/transaction-service-guides/delegates.mdx b/pages/core-api/transaction-service-guides/delegates.mdx index e7331ee4..4f681035 100644 --- a/pages/core-api/transaction-service-guides/delegates.mdx +++ b/pages/core-api/transaction-service-guides/delegates.mdx @@ -43,7 +43,6 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript import { ethers } from 'ethers' import SafeApiKit, { AddSafeDelegateProps } from '@safe-global/api-kit' - import { EthersAdapter } from '@safe-global/protocol-kit' ``` @@ -65,22 +64,11 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - const ethProvider = new ethers.JsonRpcProvider(config.RPC_URL) - - // Instantiate an EthAdapter with Owner A - const ownerA = new ethers.Wallet(config.OWNER_A_PRIVATE_KEY, ethProvider) - const ethAdapterOwnerA = new EthersAdapter({ - ethers, - signerOrProvider: ownerA - }) - // Initialize the API Kit const apiKit = new SafeApiKit({ chainId: 11155111n }) - const ownerAAddress = await ethAdapterOwnerA.getSignerAddress() - // Get the Safe delegates const delegates = await apiKit.getSafeDelegates({ delegatorAddress: config.SAFE_ADDRESS @@ -118,15 +106,13 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - const ownerB = new ethers.Wallet(config.OWNER_B_PRIVATE_KEY, ethProvider) + const provider = new ethers.JsonRpcProvider(config.RPC_URL) - // Instantiate an EthAdapter with Owner B - const ethAdapterOwnerB = new EthersAdapter({ - ethers, - signerOrProvider: ownerB - }) + const ownerA = new ethers.Wallet(config.OWNER_A_PRIVATE_KEY, provider) + const ownerAAddress = await ownerA.getAddress() - const ownerBAddress = await ethAdapterOwnerB.getSignerAddress() + const ownerB = new ethers.Wallet(config.OWNER_B_PRIVATE_KEY, provider) + const ownerBAddress = await ownerB.getAddress() const delegateConfig: AddSafeDelegateProps = { delegateAddress: ownerBAddress || '0x', diff --git a/pages/core-api/transaction-service-guides/messages.mdx b/pages/core-api/transaction-service-guides/messages.mdx index 11c45a89..2a9700da 100644 --- a/pages/core-api/transaction-service-guides/messages.mdx +++ b/pages/core-api/transaction-service-guides/messages.mdx @@ -22,7 +22,7 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```bash - yarn add ethers @safe-global/api-kit @safe-global/protocol-kit @safe-global/safe-core-sdk-types + yarn add @safe-global/api-kit @safe-global/protocol-kit @safe-global/safe-core-sdk-types ``` @@ -41,9 +41,8 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - import { ethers } from 'ethers' import SafeApiKit, { AddMessageProps } from '@safe-global/api-kit' - import Safe, { EthersAdapter, hashSafeMessage } from '@safe-global/protocol-kit' + import Safe, { hashSafeMessage } from '@safe-global/protocol-kit' ``` @@ -67,18 +66,10 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - const ethProvider = new ethers.JsonRpcProvider(config.RPC_URL) - - // Instantiate an EthAdapter with Owner A - const ownerA = new ethers.Wallet(config.OWNER_A_PRIVATE_KEY, ethProvider) - const ethAdapterOwnerA = new EthersAdapter({ - ethers, - signerOrProvider: ownerA - }) - // Initialize the Protocol Kit with Owner A const protocolKitOwnerA = await Safe.create({ - ethAdapter: ethAdapterOwnerA, + provider: config.RPC_URL, + signer: config.OWNER_A_PRIVATE_KEY, safeAddress: config.SAFE_ADDRESS }) @@ -194,16 +185,10 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - // Instantiate an EthAdapter with Owner B - const ownerB = new ethers.Wallet(config.OWNER_B_PRIVATE_KEY, ethProvider) - const ethAdapterOwnerB = new EthersAdapter({ - ethers, - signerOrProvider: ownerB - }) - // Initialize the Protocol Kit with Owner B const protocolKitOwnerB = await Safe.create({ - ethAdapter: ethAdapterOwnerB, + provider: config.RPC_URL, + signer: config.OWNER_B_PRIVATE_KEY, safeAddress: config.SAFE_ADDRESS }) @@ -243,7 +228,8 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r // Sign the message with Owner B const signedMessageOwnerB = await protocolKitOwnerB.signMessage(safeServiceMessage) - const ownerBAddress = (await ethAdapterOwnerB.getSignerAddress()) || '0x' + // Get Owner B address + const ownerBAddress = '0x...' // Send the message to the Transaction Service with the signature from Owner B await apiKit.addMessageSignature( diff --git a/pages/core-api/transaction-service-guides/transactions.mdx b/pages/core-api/transaction-service-guides/transactions.mdx index 82747e72..6a2c54ac 100644 --- a/pages/core-api/transaction-service-guides/transactions.mdx +++ b/pages/core-api/transaction-service-guides/transactions.mdx @@ -22,7 +22,7 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```bash - yarn add ethers @safe-global/api-kit @safe-global/protocol-kit @safe-global/safe-core-sdk-types + yarn add @safe-global/api-kit @safe-global/protocol-kit @safe-global/safe-core-sdk-types ``` @@ -41,9 +41,8 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - import { ethers } from 'ethers' import SafeApiKit from '@safe-global/api-kit' - import Safe, { EthersAdapter } from '@safe-global/protocol-kit' + import Safe from '@safe-global/protocol-kit' import { MetaTransactionData, OperationType @@ -69,18 +68,10 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - const ethProvider = new ethers.JsonRpcProvider(config.RPC_URL) - - // Instantiate an EthAdapter with Owner A - const ownerA = new ethers.Wallet(config.OWNER_A_PRIVATE_KEY, ethProvider) - const ethAdapterOwnerA = new EthersAdapter({ - ethers, - signerOrProvider: ownerA - }) - // Initialize the Protocol Kit with Owner A const protocolKitOwnerA = await Safe.create({ - ethAdapter: ethAdapterOwnerA, + provider: config.RPC_URL, + signer: config.OWNER_A_PRIVATE_KEY, safeAddress: config.SAFE_ADDRESS }) @@ -176,14 +167,12 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r chainId: 11155111n }) - const senderAddress = await ownerA.getAddress() - // Send the transaction to the Transaction Service with the signature from Owner A await apiKit.proposeTransaction({ safeAddress: config.SAFE_ADDRESS, safeTransactionData: safeTransaction.data, safeTxHash, - senderAddress, + senderAddress: config.OWNER_A_ADDRESS, senderSignature: signatureOwnerA.data }) ``` @@ -242,16 +231,10 @@ The different steps are implemented using [Curl](https://github.com/curl/curl) r ```typescript - // Instantiate an EthAdapter with Owner B - const ownerB = new ethers.Wallet(config.OWNER_B_PRIVATE_KEY, ethProvider) - const ethAdapterOwnerB = new EthersAdapter({ - ethers, - signerOrProvider: ownerB - }) - // Initialize the Protocol Kit with Owner B const protocolKitOwnerB = await Safe.create({ - ethAdapter: ethAdapterOwnerB, + provider: config.RPC_URL, + signer: config.OWNER_B_PRIVATE_KEY, safeAddress: config.SAFE_ADDRESS }) diff --git a/pages/sdk/api-kit.mdx b/pages/sdk/api-kit.mdx index b4e49df1..b271bdcf 100644 --- a/pages/sdk/api-kit.mdx +++ b/pages/sdk/api-kit.mdx @@ -1,4 +1,5 @@ import { Grid } from '@mui/material' +import { Steps } from 'nextra/components' import CustomCard from '../../components/CustomCard' # API Kit @@ -19,122 +20,132 @@ In this guide we will see how to propose transactions to the service and collect For more detailed information, see the guide [Integrating the Protocol Kit and API Kit](https://github.com/safe-global/safe-core-sdk/blob/main/guides/integrating-the-safe-core-sdk.md) and the [API Kit Reference](./api-kit/reference.md). -### Prerequisites +## Prerequisites 1. [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) 2. A Safe with several signers +## Steps -## Install dependencies + -To add the API Kit to your project, run: + ### Install dependencies -```bash -yarn add @safe-global/api-kit -``` + To add the API Kit to your project, run: -## Instantiate an EthAdapter + ```bash + yarn add @safe-global/api-kit + ``` -First of all, we need to create an `EthAdapter`, which contains all the required utilities for the SDKs to interact with the blockchain. It acts as a wrapper for [web3.js](https://web3js.readthedocs.io/) or [ethers.js](https://docs.ethers.org/v6/) Ethereum libraries. + ### Imports -Depending on the library used by the dapp, there are two options: + Here are all the necessary imports for this guide. -- [Create an `EthersAdapter` instance](https://github.com/safe-global/safe-core-sdk/tree/main/packages/protocol-kit/src/adapters/ethers) -- [Create a `Web3Adapter` instance](https://github.com/safe-global/safe-core-sdk/tree/main/packages/protocol-kit/src/adapters/web3) + ```typescript + import SafeApiKit from '@safe-global/api-kit' + import Safe from '@safe-global/protocol-kit' + ``` -Once the instance of `EthersAdapter` or `Web3Adapter` is created, it can be used in the initialization of the API Kit. + ### Initialize the API Kit -```typescript -import { EthersAdapter } from '@safe-global/protocol-kit' + Firstly, we need to create an instance of the API Kit. In chains where the [Safe Transaction Service](../core-api/transaction-service-overview.mdx) is supported, it's enough to specify the `chainId` property. -const provider = new ethers.JsonRpcProvider(config.RPC_URL) -const signer = new ethers.Wallet(config.SIGNER_ADDRESS_PRIVATE_KEY, provider) + ```typescript + const apiKit = new SafeApiKit({ + chainId: 1n + }) + ``` -const ethAdapter = new EthersAdapter({ - ethers, - signerOrProvider: signer -}) -``` + Alternatively, we can use a custom service using the optional `txServiceUrl` property. -## Initialize the API Kit + ```typescript + const apiKit = new SafeApiKit({ + chainId: 1n, // set the correct chainId + txServiceUrl: 'https://url-to-your-custom-service' + }) + ``` -We need to create an instance of the API Kit. In chains where Safe provides a Transaction Service, it's enough to specify the `chainId`. You can set your own service using the optional `txServiceUrl` parameter. + ### Initialize the Protocol Kit -```typescript -import SafeApiKit from '@safe-global/api-kit' + We need an instance of the Protocol Kit to handle transactions and signatures. To instantiate it, get a provider and signer first. -const apiKit = new SafeApiKit({ - chainId: 1n -}) + For this guide, we will be using Sepolia Testnet. + ```typescript + // https://chainlist.org/?search=sepolia&testnets=true + const RPC_URL = 'https://eth-sepolia.public.blastapi.io' + const PRIVATE_KEY = '' + ``` -// or using a custom service -const apiKit = new SafeApiKit({ - chainId: 1n, // set the correct chainId - txServiceUrl: 'https://url-to-your-custom-service' -}) -``` + After that, we instantiate the Protocol Kit with the `provider`, `signer` and `safeAddress`. -## Propose a transaction to the service + ```typescript + const protocolKit = await Safe.create({ + provider: RPC_URL, + signer: PRIVATE_KEY, + safeAddress: '' + }) + ``` -Before a transaction can be executed, any of the Safe signers needs to initiate the process by creating a proposal of a transaction. We send this transaction to the service to make it accessible by the other owners so they can give their approval and sign the transaction as well. + ### Propose a transaction to the service -```typescript -import Safe from '@safe-global/protocol-kit' + Before a transaction can be executed, any of the Safe signers needs to initiate the process by creating a proposal of a transaction. We send this transaction to the service to make it accessible by the other owners so they can give their approval and sign the transaction as well. -// Create Safe instance -const protocolKit = await Safe.create({ - ethAdapter, - safeAddress: config.SAFE_ADDRESS -}) + ```typescript + // Create transaction + const safeTransactionData: MetaTransactionData = { + to: '0x', + value: '1', // 1 wei + data: '0x', + operation: OperationType.Call + } -// Create transaction -const safeTransactionData: MetaTransactionData = { - to: '0x', - value: '1', // 1 wei - data: '0x', - operation: OperationType.Call -} + const safeTransaction = await protocolKit.createTransaction({ + transactions: [safeTransactionData] + }) -const safeTransaction = await protocolKit.createTransaction({ transactions: [safeTransactionData] }) + const senderAddress = await signer.getAddress() + const safeTxHash = await protocolKit.getTransactionHash(safeTransaction) + const signature = await protocolKit.signHash(safeTxHash) -const senderAddress = await signer.getAddress() -const safeTxHash = await protocolKit.getTransactionHash(safeTransaction) -const signature = await protocolKit.signHash(safeTxHash) + // Propose transaction to the service + await apiKit.proposeTransaction({ + safeAddress: await protocolKit.getAddress(), + safeTransactionData: safeTransaction.data, + safeTxHash, + senderAddress, + senderSignature: signature.data + }) + ``` -// Propose transaction to the service -await apiKit.proposeTransaction({ - safeAddress: await protocolKit.getAddress(), - safeTransactionData: safeTransaction.data, - safeTxHash, - senderAddress, - senderSignature: signature.data -}) -``` + ### Retrieve the pending transactions -## Retrieve the pending transactions + Different methods in the API Kit are available to retrieve pending transactions depending on the situation. To retrieve a transaction given the Safe transaction hash use the method that's not commented. -Different methods in the API Kit are available to retrieve pending transactions depending on the situation. To retrieve a transaction given the Safe transaction hash use the method that's not commented. + ```typescript + const transaction = await service.getTransaction("") + // const transactions = await service.getPendingTransactions() + // const transactions = await service.getIncomingTransactions() + // const transactions = await service.getMultisigTransactions() + // const transactions = await service.getModuleTransactions() + // const transactions = await service.getAllTransactions() + ``` -```typescript -const transaction = await service.getTransaction("") -// const transactions = await service.getPendingTransactions() -// const transactions = await service.getIncomingTransactions() -// const transactions = await service.getMultisigTransactions() -// const transactions = await service.getModuleTransactions() -// const transactions = await service.getAllTransactions() -``` + ### Confirm the transaction -## Confirm the transaction + In this step we need to sign the transaction with the Protocol Kit and submit the signature to the Safe Transaction Service using the `confirmTransaction` method. -In this step we need to sign the transaction with the Protocol Kit and submit the signature to the Safe Transaction Service using the `confirmTransaction` method. + ```typescript + const safeTxHash = transaction.transactionHash + const signature = await protocolKit.signHash(safeTxHash) -```typescript -const safeTxHash = transaction.transactionHash -const signature = await protocolKit.signHash(safeTxHash) + // Confirm the Safe transaction + const signatureResponse = await apiKit.confirmTransaction( + safeTxHash, + signature.data + ) + ``` -// Confirm the Safe transaction -const signatureResponse = await apiKit.confirmTransaction(safeTxHash, signature.data) -``` + The Safe transaction is now ready to be executed. This can be done using the Safe\{Wallet\} web interface, the Protocol Kit or any other tool that's available. -The Safe transaction is now ready to be executed. This can be done using the Safe\{Wallet\} web interface, the Protocol Kit or any other tool that's available. + diff --git a/pages/sdk/auth-kit/guides/safe-auth.mdx b/pages/sdk/auth-kit/guides/safe-auth.mdx index 54842c12..add7c45a 100644 --- a/pages/sdk/auth-kit/guides/safe-auth.mdx +++ b/pages/sdk/auth-kit/guides/safe-auth.mdx @@ -31,7 +31,6 @@ The `SafeAuthPack` is an authentication system that utilizes the [Web3Auth](http SafeAuthConfig, SafeAuthInitOptions, } from '@safe-global/auth-kit' - import { EthersAdapter } from '@safe-global/protocol-kit' ``` ### Create a SafeAuthPack instance @@ -54,7 +53,7 @@ The `SafeAuthPack` is an authentication system that utilizes the [Web3Auth](http showWidgetButton: false, chainConfig: { chainId: '0x1', - rpcTarget: `${rpcUrl}` + rpcTarget: RPC_URL } } @@ -93,7 +92,7 @@ The `SafeAuthPack` is an authentication system that utilizes the [Web3Auth](http await safeAuthPack.signOut() ``` - After the user is authenticated, call `getProvider()` to get the Ethereum provider instance. This is a [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible provider you can wrap using your favorite library (web3, ethers). + After the user is authenticated, call `getProvider()` to get an Ethereum [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible provider. ```typescript safeAuthPack.getProvider() @@ -118,24 +117,14 @@ The `SafeAuthPack` is an authentication system that utilizes the [Web3Auth](http ### Sign and execute transactions - The `SafeAuthPack` can be used with the [Protocol Kit](../../protocol-kit.mdx) to establish a connection to a Safe. This connection is made using the `provider` and `signer` associated with the authenticated account. + The `SafeAuthPack` can be used with the [Protocol Kit](../../protocol-kit.mdx) to establish a connection to a Safe using the `provider`. We don't need to pass the `signer` property in the `create()` method as it will be automatically taken from the passed `provider`. After connecting, you can use any of the methods provided in the [Protocol Kit](https://github.com/safe-global/safe-core-sdk/tree/main/packages/protocol-kit#sdk-api). ```typescript - // Wrap EIP-1193 provider with ethers - const provider = new ethers.BrowserProvider(safeAuthPack.getProvider()) - const signer = provider.getSigner() - - // Create the Safe EthersAdapter - const ethAdapter = new EthersAdapter({ - ethers, - signerOrProvider: signer || provider, - }) - // Instantiate the Protocol Kit const protocolKit = await Safe.create({ - ethAdapter, + provider: safeAuthPack.getProvider(), safeAddress, }) diff --git a/pages/sdk/protocol-kit.mdx b/pages/sdk/protocol-kit.mdx index 5261483d..bb94fa3d 100644 --- a/pages/sdk/protocol-kit.mdx +++ b/pages/sdk/protocol-kit.mdx @@ -26,75 +26,46 @@ For a more detailed guide, including how to integrate with `web3.js` and more Sa ### Install dependencies -First, we need to install some dependencies from `safe-core-sdk` and the `ethers` library. - -To interact with Ethereum and other EVM blockchains in Node, we can either use: web3.js or ethers.js. In this tutorial, we will use the ethers.js library. To use `web3js`, see [Instantiate an EthAdapter section in Guide: Integrating the Safe Core SDK](https://github.com/safe-global/safe-core-sdk/blob/main/guides/integrating-the-safe-core-sdk.md#instantiate-an-ethadapter). - -The Protocol Kit is compatible only with **ethers.js v6**. Make sure you specify this version when installing the SDK. +First, we need to install some dependencies. You can store your environment variables such as private keys in a `.env` file. To read easily from `.env` files, use the `dotenv` library. ```bash -yarn add ethers @safe-global/protocol-kit \ +yarn add @safe-global/protocol-kit \ @safe-global/api-kit \ @safe-global/safe-core-sdk-types \ dotenv ``` -Create the `.env` file: - -```bash -touch .env -``` - -Put your signing account private keys into the `.env` file you just created. - -```bash -export OWNER_1_PRIVATE_KEY='' -export OWNER_2_PRIVATE_KEY='' -export OWNER_3_PRIVATE_KEY='' -``` - -Create an `index.ts` file that you will use to run the following code snippets. - -```bash -touch index.ts -``` - -Tip: Use [ts-node](https://github.com/TypeStrong/ts-node) to run a Typescript file in Node.js. +Use [ts-node](https://github.com/TypeStrong/ts-node) to run a TypeScript file in Node.js. ```bash npx ts-node examples/protocol-kit/index.ts ``` -### Initialize Signers, Providers, and EthAdapter +### Initialize Signers and Providers The signers trigger transactions to the Ethereum blockchain or off-chain transactions. The provider connects to the Ethereum blockchain. You can get a public RPC URL from [Chainlist](https://chainlist.org), however, public RPC URLs can be unreliable so you can also try a dedicated provider like Infura or Alchemy. -For this tutorial, we will be creating a Safe on the Sepolia Testnet. +For this guide, we will be creating a Safe on the Sepolia Testnet. ```tsx -import { ethers } from 'ethers' -import { EthersAdapter } from '@safe-global/protocol-kit' -import dotenv from 'dotenv' - -dotenv.config() - // https://chainlist.org/?search=sepolia&testnets=true -const RPC_URL='https://eth-sepolia.public.blastapi.io' -const provider = new ethers.JsonRpcProvider(RPC_URL) +const RPC_URL = 'https://eth-sepolia.public.blastapi.io' // Initialize signers -const owner1Signer = new ethers.Wallet(process.env.OWNER_1_PRIVATE_KEY!, provider) -const owner2Signer = new ethers.Wallet(process.env.OWNER_2_PRIVATE_KEY!, provider) -const owner3Signer = new ethers.Wallet(process.env.OWNER_3_PRIVATE_KEY!, provider) +const OWNER_1_ADDRESS = // ... +const OWNER_1_PRIVATE_KEY = // ... -const ethAdapterOwner1 = new EthersAdapter({ - ethers, - signerOrProvider: owner1Signer -}) +const OWNER_2_ADDRESS = // ... +const OWNER_2_PRIVATE_KEY = // ... + +const OWNER_3_ADDRESS = // ... + +const provider = new ethers.JsonRpcProvider(RPC_URL) +const owner1Signer = new ethers.Wallet(OWNER_1_PRIVATE_KEY, provider) ``` ### Initialize the API Kit @@ -111,7 +82,7 @@ const apiKit = new SafeApiKit({ }) -// or using a custom service +// Or using a custom service const apiKit = new SafeApiKit({ chainId: 1n, // set the correct chainId txServiceUrl: 'https://url-to-your-custom-service' @@ -120,14 +91,15 @@ const apiKit = new SafeApiKit({ ### Initialize the Protocol Kit -Sepolia is a supported network so you don't need to specify the contract addresses, however, to see how to create a safe on a local or unsupported network, see [Instantiate an EthAdapter](https://github.com/safe-global/safe-core-sdk/blob/main/guides/integrating-the-safe-core-sdk.md#instantiate-an-ethadapter). - -Safe Factory is used to create Safes. While Safe class represents an instance of a specific Safe account. +The `SafeFactory` class allows the deployment of new Safe accounts while the `Safe` class represents an instance of a specific Safe account. ```tsx import { SafeFactory } from '@safe-global/protocol-kit' -const safeFactory = await SafeFactory.create({ ethAdapter: ethAdapterOwner1 }) +const safeFactory = await SafeFactory.create({ + provider: RPC_URL, + signer: OWNER_1_PRIVATE_KEY +}) ``` ### Deploy a Safe @@ -139,16 +111,15 @@ import { SafeAccountConfig } from '@safe-global/protocol-kit' const safeAccountConfig: SafeAccountConfig = { owners: [ - await owner1Signer.getAddress(), - await owner2Signer.getAddress(), - await owner3Signer.getAddress() + await OWNER_1_ADDRESS, + await OWNER_2_ADDRESS, + await OWNER_3_ADDRESS ], threshold: 2, - // ... (Optional params) + // Optional params } -/* This Safe is tied to owner 1 because the factory was initialized with -an adapter that had owner 1 as the signer. */ +/* This Safe is tied to owner 1 because the factory was initialized with the owner 1 as the signer. */ const protocolKitOwner1 = await safeFactory.deploySafe({ safeAccountConfig }) const safeAddress = await protocolKitOwner1.getAddress() @@ -238,7 +209,7 @@ await apiKit.proposeTransaction({ safeAddress, safeTransactionData: safeTransaction.data, safeTxHash, - senderAddress: await owner1Signer.getAddress(), + senderAddress: OWNER_1_ADDRESS, senderSignature: senderSignature.data, }) ``` @@ -260,13 +231,9 @@ When owner 2 is connected to the application, the Protocol Kit should be initial const transaction = pendingTransactions[0] const safeTxHash = transaction.safeTxHash -const ethAdapterOwner2 = new EthersAdapter({ - ethers, - signerOrProvider: owner2Signer -}) - const protocolKitOwner2 = await Safe.create({ - ethAdapter: ethAdapterOwner2, + provider: RPC_URL, + signer: OWNER_2_PRIVATE_KEY, safeAddress }) diff --git a/pages/sdk/protocol-kit/guides/signatures.md b/pages/sdk/protocol-kit/guides/signatures.md index 34a5403f..e37cbc6d 100644 --- a/pages/sdk/protocol-kit/guides/signatures.md +++ b/pages/sdk/protocol-kit/guides/signatures.md @@ -12,24 +12,28 @@ In the following guides, there are different accounts involved that will be used | Who | Description | Address for this example | | :--- | :--- | :--- | -| `safe3_4` | 3/4 Safe (3 signatures required out of 4 owners) | 0xb3b3862D8e38a1E965eb350B09f2167B2371D652 | -| `owner1` | EOA and owner of `safe3_4` | 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 | -| `owner2` | EOA and owner of `safe3_4` | 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0 | -| `safe1_1` | 1/1 Safe and owner of `safe3_4` | 0x215033cdE0619D60B7352348F4598316Cc39bC6E | -| `owner3` | EOA and owner of `safe1_1` | 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b | -| `safe2_3` | 2/3 Safe and owner of `safe3_4` | 0xf75D61D6C27a7CC5788E633c1FC130f0F4a62D33 | -| `owner4` | EOA and owner of `safe2_3` | 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d | -| `owner5` | EOA and owner of `safe2_3` | 0xd03ea8624C8C5987235048901fB614fDcA89b117 | - -Each EOA owner described above is bound to an `EthAdapter` instance that should also be initialized. - -| Adapter | Signer | -| :--- | :--- | -| `ethAdapter1` | `owner1` | -| `ethAdapter2` | `owner2` | -| `ethAdapter3` | `owner3` | -| `ethAdapter4` | `owner4` | -| `ethAdapter5` | `owner5` | +| `SAFE_3_4_ADDRESS` | 3/4 Safe (3 signatures required out of 4 owners) | 0xb3b3862D8e38a1E965eb350B09f2167B2371D652 | +| `OWNER_1_ADDRESS` | EOA and owner of `SAFE_3_4_ADDRESS` | 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 | +| `OWNER_2_ADDRESS` | EOA and owner of `SAFE_3_4_ADDRESS` | 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0 | +| `SAFE_1_1_ADDRESS` | 1/1 Safe and owner of `SAFE_3_4_ADDRESS` | 0x215033cdE0619D60B7352348F4598316Cc39bC6E | +| `OWNER_3_ADDRESS` | EOA and owner of `SAFE_1_1_ADDRESS` | 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b | +| `SAFE_2_3_ADDRESS` | 2/3 Safe and owner of `SAFE_3_4_ADDRESS` | 0xf75D61D6C27a7CC5788E633c1FC130f0F4a62D33 | +| `OWNER_4_ADDRESS` | EOA and owner of `SAFE_2_3_ADDRESS` | 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d | +| `OWNER_5_ADDRESS` | EOA and owner of `SAFE_2_3_ADDRESS` | 0xd03ea8624C8C5987235048901fB614fDcA89b117 | + +We need to instantiate all the signers based on the Safe owner accounts. + +```typescript +// https://chainlist.org/?search=sepolia&testnets=true +const RPC_URL = 'https://eth-sepolia.public.blastapi.io' + +// Initialize signers +const OWNER_1_PRIVATE_KEY = // ... +const OWNER_2_PRIVATE_KEY = // ... +const OWNER_3_PRIVATE_KEY = // ... +const OWNER_4_PRIVATE_KEY = // ... +const OWNER_5_PRIVATE_KEY = // ... +``` ## Guides diff --git a/pages/sdk/protocol-kit/guides/signatures/messages.mdx b/pages/sdk/protocol-kit/guides/signatures/messages.mdx index 179ae796..6660e91f 100644 --- a/pages/sdk/protocol-kit/guides/signatures/messages.mdx +++ b/pages/sdk/protocol-kit/guides/signatures/messages.mdx @@ -104,32 +104,38 @@ Using the Protocol Kit, this guide explains how to generate and sign messages fr Once the `safeMessage` object is created, we need to collect the signatures from the signers who will sign it. - Following our [setup](../signatures.md), we will sign a message with `safe3_4`, the main Safe account in this guide. To do that, we first need to sign the same message with its owners: `owner1`, `owner2`, `safe1_1`, and `safe2_3`. + Following our [setup](../signatures.md), we will sign a message with `SAFE_3_4_ADDRESS`, the main Safe account in this guide. To do that, we first need to sign the same message with its owners: `OWNER_1_ADDRESS`, `OWNER_2_ADDRESS`, `SAFE_1_1_ADDRESS`, and `SAFE_2_3_ADDRESS`. #### ECDSA signatures - This applies to `owner1` and `owner2` accounts, as both are EOAs. + This applies to `OWNER_1_ADDRESS` and `OWNER_2_ADDRESS` accounts, as both are EOAs. The `signMessage` method takes the `safeMessage` together with a `SigningMethod` and adds the new signature to the `signMessage.signatures` map. Depending on the type of message, the `SigningMethod` can take these values: - `SigningMethod.ETH_SIGN` - `SigningMethod.ETH_SIGN_TYPED_DATA_V4` ```typescript - // Connect the EthAdapter from owner1 - protocolKit = await protocolKit.connect({ ethAdapter: ethAdapter1 }) + // Connect OWNER_1_ADDRESS + protocolKit = await protocolKit.connect({ + provider: RPC_URL + signer: OWNER_1_PRIVATE_KEY + }) - // Sign the safeMessage with owner1 - // After this, the safeMessage contains the signature from owner1 + // Sign the safeMessage with OWNER_1_ADDRESS + // After this, the safeMessage contains the signature from OWNER_1_ADDRESS safeMessage = await protocolKit.signMessage( safeMessage, SigningMethod.ETH_SIGN_TYPED_DATA_V4 ) - // Connect the EthAdapter from owner2 - protocolKit = await protocolKit.connect({ ethAdapter: ethAdapter2 }) + // Connect OWNER_2_ADDRESS + protocolKit = await protocolKit.connect({ + provider: RPC_URL + signer: OWNER_2_PRIVATE_KEY + }) - // Sign the safeMessage with owner2 - // After this, the safeMessage contains the signature from owner1 and owner2 + // Sign the safeMessage with OWNER_2_ADDRESS + // After this, the safeMessage contains the signature from OWNER_1_ADDRESS and OWNER_2_ADDRESS safeMessage = await protocolKit.signMessage( safeMessage, SigningMethod.ETH_SIGN_TYPED_DATA_V4 @@ -142,36 +148,37 @@ Using the Protocol Kit, this guide explains how to generate and sign messages fr ##### 1/1 Safe account - This applies to the `safe1_1` account, another owner of `safe3_4`. + This applies to the `SAFE_1_1_ADDRESS` account, another owner of `SAFE_3_4_ADDRESS`. - We need to connect the Protocol Kit to `safe1_1` and the `owner3` account (the only owner of `safe1_1`) and sign the message. + We need to connect the Protocol Kit to `SAFE_1_1_ADDRESS` and the `OWNER_3_ADDRESS` account (the only owner of `SAFE_1_1_ADDRESS`) and sign the message. ```typescript // Create a new message object let messageSafe1_1 = await createMessage(TYPED_MESSAGE) - // Connect the EthAdapter from owner3 and the address of safe1_1 + // Connect OWNER_3_ADDRESS and SAFE_1_1_ADDRESS protocolKit = await protocolKit.connect({ - ethAdapter: ethAdapter3, - safeAddress: safe1_1 + provider: RPC_URL + signer: OWNER_3_PRIVATE_KEY, + safeAddress: SAFE_1_1_ADDRESS }) - // Sign the messageSafe1_1 with owner3 - // After this, the messageSafe1_1 contains the signature from owner3 + // Sign the messageSafe1_1 with OWNER_3_ADDRESS + // After this, the messageSafe1_1 contains the signature from OWNER_3_ADDRESS messageSafe1_1 = await signMessage( messageSafe1_1, SigningMethod.SAFE_SIGNATURE, - safe3_4 // Parent Safe address + SAFE_3_4_ADDRESS // Parent Safe address ) - // Build the contract signature of safe1_1 + // Build the contract signature of SAFE_1_1_ADDRESS const signatureSafe1_1 = await buildContractSignature( Array.from(messageSafe1_1.signatures.values()), - safe1_1 + SAFE_1_1_ADDRESS ) // Add the signatureSafe1_1 to safeMessage - // After this, the safeMessage contains the signature from owner1, owner2 and safe1_1 + // After this, the safeMessage contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS and SAFE_1_1_ADDRESS safeMessage.addSignature(signatureSafe1_1) ``` @@ -179,47 +186,51 @@ Using the Protocol Kit, this guide explains how to generate and sign messages fr ##### 2/3 Safe account - This applies to the `safe2_3` account, another owner of `safe3_4`. + This applies to the `SAFE_2_3_ADDRESS` account, another owner of `SAFE_3_4_ADDRESS`. - We need to connect the Protocol Kit to `safe2_3` and the `owner4` and `owner5` accounts (owners of `safe2_3`) and sign the message. + We need to connect the Protocol Kit to `SAFE_2_3_ADDRESS` and the `OWNER_4_ADDRESS` and `OWNER_5_ADDRESS` accounts (owners of `SAFE_2_3_ADDRESS`) and sign the message. ```typescript // Create a new message object let messageSafe2_3 = await createMessage(TYPED_MESSAGE) - // Connect the EthAdapter from owner4 and the address of safe2_3 + // Connect OWNER_4_ADDRESS and SAFE_2_3_ADDRESS protocolKit = await protocolKit.connect({ - ethAdapter: ethAdapter4, - safeAddress: safe2_3 + provider: RPC_URL, + signer: OWNER_4_PRIVATE_KEY, + safeAddress: SAFE_2_3_ADDRESS }) - // Sign the messageSafe2_3 with owner4 - // After this, the messageSafe2_3 contains the signature from owner4 + // Sign the messageSafe2_3 with OWNER_4_ADDRESS + // After this, the messageSafe2_3 contains the signature from OWNER_4_ADDRESS messageSafe2_3 = await protocolKit.signMessage( messageSafe2_3, SigningMethod.SAFE_SIGNATURE, - safe3_4 // Parent Safe address + SAFE_3_4_ADDRESS // Parent Safe address ) - // Connect the EthAdapter from owner5 - protocolKit = await protocolKit.connect({ ethAdapter: ethAdapter5 }) + // Connect OWNER_5_ADDRESS + protocolKit = await protocolKit.connect({ + provider: RPC_URL, + signer: OWNER_5_PRIVATE_KEY + }) - // Sign the messageSafe2_3 with owner5 - // After this, the messageSafe2_3 contains the signature from owner5 + // Sign the messageSafe2_3 with OWNER_5_ADDRESS + // After this, the messageSafe2_3 contains the signature from OWNER_5_ADDRESS messageSafe2_3 = await protocolKit.signMessage( messageSafe2_3, SigningMethod.SAFE_SIGNATURE, - safe3_4 // Parent Safe address + SAFE_3_4_ADDRESS // Parent Safe address ) - // Build the contract signature of safe2_3 + // Build the contract signature of SAFE_2_3_ADDRESS const signatureSafe2_3 = await buildContractSignature( Array.from(messageSafe2_3.signatures.values()), - safe2_3 + SAFE_2_3_ADDRESS ) // Add the signatureSafe2_3 to safeMessage - // After this, the safeMessage contains the signature from owner1, owner2, safe1_1 and safe2_3 + // After this, the safeMessage contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS, SAFE_1_1_ADDRESS and SAFE_2_3_ADDRESS safeMessage.addSignature(signatureSafe2_3) ``` @@ -249,17 +260,15 @@ Using the Protocol Kit, this guide explains how to generate and sign messages fr To store a new message, we need to call the `addMessage` from the API Kit, passing the Safe address, an object with the message, and a signature from one owner. ```typescript - const signerAddress = (await ethAdapter1.getSignerAddress()) || '0x' - - // Get the signature from owner1 - const signatureOwner1 = safeMessage.getSignature(signerAddress) as EthSafeSignature + // Get the signature from OWNER_1_ADDRESS + const signatureOwner1 = safeMessage.getSignature(OWNER_1_PRIVATE_KEY) as EthSafeSignature // Instantiate the API Kit // Use the chainId where you have the Safe account deployed const apiKit = new SafeApiKit({ chainId }) // Propose the message - apiKit.addMessage(safe3_4, { + apiKit.addMessage(SAFE_3_4_ADDRESS, { message: TYPED_MESSAGE, // or STRING_MESSAGE signature: buildSignatureBytes([signatureOwner1]) }) @@ -277,23 +286,22 @@ Using the Protocol Kit, this guide explains how to generate and sign messages fr hashSafeMessage(TYPED_MESSAGE) // or STRING_MESSAGE ) - // Get the signature from owner2 - const signerAddress = (await ethAdapter2.getSignerAddress()) || '0x' - const signatureOwner2 = safeMessage.getSignature(signerAddress) as EthSafeSignature + // Get the signature from OWNER_2_ADDRESS + const signatureOwner2 = safeMessage.getSignature(OWNER_2_ADDRESS) as EthSafeSignature - // Add signature from owner2 + // Add signature from OWNER_2_ADDRESS await apiKit.addMessageSignature( safeMessageHash, buildSignatureBytes([signatureOwner2]) ) - // Add signature from the owner safe1_1 + // Add signature from the owner SAFE_1_1_ADDRESS await apiKit.addMessageSignature( safeMessageHash, buildSignatureBytes([signatureSafe1_1]) ) - // Add signature from the owner safe2_3 + // Add signature from the owner SAFE_2_3_ADDRESS await apiKit.addMessageSignature( safeMessageHash, buildSignatureBytes([signatureSafe2_3]) @@ -320,7 +328,7 @@ Using the Protocol Kit, this guide explains how to generate and sign messages fr ```typescript // Get the contract with the correct version - const signMessageLibContract = await ethAdapter1.getSignMessageLibContract({ + const signMessageLibContract = await getSignMessageLibContract({ safeVersion: '1.4.1' }) ``` diff --git a/pages/sdk/protocol-kit/guides/signatures/transactions.mdx b/pages/sdk/protocol-kit/guides/signatures/transactions.mdx index c1ae45a3..0d863f55 100644 --- a/pages/sdk/protocol-kit/guides/signatures/transactions.mdx +++ b/pages/sdk/protocol-kit/guides/signatures/transactions.mdx @@ -56,32 +56,38 @@ This guide explains how transactions are signed by the Safe owners using the Pro Once the `safeTransaction` object is created, we need to collect the signatures from the signers who will sign it. - Following our [setup](../signatures.md), we will sign a Safe transaction from `safe3_4`, the main Safe account in this guide. To do that, we first need to sign the same transaction with its owners: `owner1`, `owner2`, `safe1_1`, and `safe2_3`. + Following our [setup](../signatures.md), we will sign a Safe transaction from `SAFE_3_4_ADDRESS`, the main Safe account in this guide. To do that, we first need to sign the same transaction with its owners: `OWNER_1_ADDRESS`, `OWNER_2_ADDRESS`, `SAFE_1_1_ADDRESS`, and `SAFE_2_3_ADDRESS`. #### ECDSA signature - This applies to `owner1` and `owner2` accounts, as both are EOAs. + This applies to `OWNER_1_ADDRESS` and `OWNER_2_ADDRESS` accounts, as both are EOAs. The `signTransaction` method takes the `safeTransaction` together with a `SigningMethod` and adds the new signature to the `safeTransaction.signatures` map. Depending on the type of message, the `SigningMethod` can take these values: - `SigningMethod.ETH_SIGN` - `SigningMethod.ETH_SIGN_TYPED_DATA_V4` ```typescript - // Connect the EthAdapter from owner1 - protocolKit = await protocolKit.connect({ ethAdapter: ethAdapter1 }) + // Connect OWNER_1_ADDRESS + protocolKit = await protocolKit.connect({ + provider: RPC_URL, + signer: OWNER_1_PRIVATE_KEY + }) - // Sign the safeTransaction with owner1 - // After this, the safeTransaction contains the signature from owner1 + // Sign the safeTransaction with OWNER_1_ADDRESS + // After this, the safeTransaction contains the signature from OWNER_1_ADDRESS safeTransaction = await protocolKit.signTransaction( safeTransaction, SigningMethod.ETH_SIGN ) - // Connect the EthAdapter from owner2 - protocolKit = await protocolKit.connect({ ethAdapter: ethAdapter2 }) + // Connect OWNER_2_ADDRESS + protocolKit = await protocolKit.connect({ + provider: RPC_URL, + signer: OWNER_2_PRIVATE_KEY + }) - // Sign the safeTransaction with owner2 - // After this, the safeTransaction contains the signature from owner1 and owner2 + // Sign the safeTransaction with OWNER_2_ADDRESS + // After this, the safeTransaction contains the signature from OWNER_1_ADDRESS and OWNER_2_ADDRESS safeTransaction = await protocolKit.signTransaction( safeTransaction, SigningMethod.ETH_SIGN_TYPED_DATA_V4 @@ -143,9 +149,9 @@ This guide explains how transactions are signed by the Safe owners using the Pro ##### 1/1 Safe account - This applies to the `safe1_1` account, another owner of `safe3_4`. + This applies to the `SAFE_1_1_ADDRESS` account, another owner of `SAFE_3_4_ADDRESS`. - We need to connect the Protocol Kit to `safe1_1` and the `owner3` account (the only owner of `safe1_1`) and sign the transaction. + We need to connect the Protocol Kit to `SAFE_1_1_ADDRESS` and the `OWNER_3_ADDRESS` account (the only owner of `SAFE_1_1_ADDRESS`) and sign the transaction. ```typescript // Create a new transaction object @@ -153,18 +159,19 @@ This guide explains how transactions are signed by the Safe owners using the Pro transactions: [safeTransactionData] }) - // Connect the adapter from owner3 and the address of safe1_1 + // Connect OWNER_3_ADDRESS and SAFE_1_1_ADDRESS protocolKit = await protocolKit.connect({ - ethAdapter: ethAdapter3, - safeAddress: safe1_1 + provider: RPC_URL, + signer: OWNER_3_PRIVATE_KEY, + safeAddress: SAFE_1_1_ADDRESS }) - // Sign the transactionSafe1_1 with owner3 - // After this, transactionSafe1_1 contains the signature from owner3 + // Sign the transactionSafe1_1 with OWNER_3_ADDRESS + // After this, transactionSafe1_1 contains the signature from OWNER_3_ADDRESS transactionSafe1_1 = await protocolKit.signTransaction( transactionSafe1_1, SigningMethod.SAFE_SIGNATURE, - safe3_4 // Parent Safe address + SAFE_3_4_ADDRESS // Parent Safe address ) ``` @@ -187,17 +194,17 @@ This guide explains how transactions are signed by the Safe owners using the Pro The `signatures.data` represents a specific signature. The `isContractSignature` flag set to `false` indicates that the signature isn't a smart contract signature but an ECDSA signature instead. - To generate a Safe compatible signature, we use the `buildContractSignature` method, which takes an array of signatures and returns another signature that can be used with Safe accounts. After that, we add the signature from `safe1_1` to our initial transaction. + To generate a Safe compatible signature, we use the `buildContractSignature` method, which takes an array of signatures and returns another signature that can be used with Safe accounts. After that, we add the signature from `SAFE_1_1_ADDRESS` to our initial transaction. ```typescript - // Build the contract signature of safe1_1 + // Build the contract signature of SAFE_1_1_ADDRESS const signatureSafe1_1 = await buildContractSignature( Array.from(transactionSafe1_1.signatures.values()), - safe1_1 + SAFE_1_1_ADDRESS ) // Add the signatureSafe1_1 to safeTransaction - // After this, the safeTransaction contains the signature from owner1, owner2 and safe1_1 + // After this, the safeTransaction contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS and SAFE_1_1_ADDRESS safeTransaction.addSignature(signatureSafe1_1) ``` @@ -211,7 +218,7 @@ This guide explains how transactions are signed by the Safe owners using the Pro } ``` - The `isContractSignature` flag is now `true` because `signatureSafe1_1` is an EIP-1271 smart contract signature from the `safe1_1` account. + The `isContractSignature` flag is now `true` because `signatureSafe1_1` is an EIP-1271 smart contract signature from the `SAFE_1_1_ADDRESS` account. The `signatureSafe1_1.data` signature should look like this: @@ -230,9 +237,9 @@ This guide explains how transactions are signed by the Safe owners using the Pro ##### 2/3 Safe account - This applies to the `safe2_3` account, another owner of `safe3_4`. + This applies to the `SAFE_2_3_ADDRESS` account, another owner of `SAFE_3_4_ADDRESS`. - We need to connect the Protocol Kit to `safe2_3` and the `owner4` and `owner5` accounts (owners of `safe2_3`) and sign the transaction. + We need to connect the Protocol Kit to `SAFE_2_3_ADDRESS` and the `OWNER_4_ADDRESS` and `OWNER_5_ADDRESS` accounts (owners of `SAFE_2_3_ADDRESS`) and sign the transaction. ```typescript // Create a new transaction object @@ -240,29 +247,33 @@ This guide explains how transactions are signed by the Safe owners using the Pro transactions: [safeTransactionData] }) - // Connect the EthAdapter from owner4 and the address of safe2_3 + // Connect OWNER_4_ADDRESS and the address of SAFE_2_3_ADDRESS protocolKit = await protocolKit.connect({ - ethAdapter: ethAdapter4, - safeAddress: safe2_3 + provider: RPC_URL, + signer: OWNER_4_ADDRESS, + safeAddress: SAFE_2_3_ADDRESS }) - // Sign the transactionSafe2_3 with owner4 - // After this, the transactionSafe2_3 contains the signature from owner4 + // Sign the transactionSafe2_3 with OWNER_4_ADDRESS + // After this, the transactionSafe2_3 contains the signature from OWNER_4_ADDRESS transactionSafe2_3 = await protocolKit.signTransaction( transactionSafe2_3, SigningMethod.SAFE_SIGNATURE, - safe3_4 // Parent Safe address + SAFE_3_4_ADDRESS // Parent Safe address ) - // Connect the adapter for owner5 - protocolKit = await protocolKit.connect({ ethAdapter: ethAdapter5 }) + // Connect OWNER_5_ADDRESS + protocolKit = await protocolKit.connect({ + provider: RPC_URL, + signer: OWNER_5_ADDRESS + }) - // Sign the transactionSafe2_3 with owner5 - // After this, the transactionSafe2_3 contains the signature from owner5 + // Sign the transactionSafe2_3 with OWNER_5_ADDRESS + // After this, the transactionSafe2_3 contains the signature from OWNER_5_ADDRESS transactionSafe2_3 = await protocolKit.signTransaction( transactionSafe2_3, SigningMethod.SAFE_SIGNATURE, - safe3_4 // Parent Safe address + SAFE_3_4_ADDRESS // Parent Safe address ) ``` @@ -286,21 +297,21 @@ This guide explains how transactions are signed by the Safe owners using the Pro } ``` - We now have two signatures from the owners, `owner4` and `owner5`. Following the same process, we can create the contract signature and examine the result. + We now have two signatures from the owners, `OWNER_4_ADDRESS` and `OWNER_5_ADDRESS`. Following the same process, we can create the contract signature and examine the result. The `signatures.data` represents a specific signature. The `isContractSignature` flag set to `false` indicates that the signature isn't a smart contract signature but an ECDSA signature instead. To generate a Safe compatible signature, we use the `buildContractSignature` method, which takes an array of signatures and returns another signature that can be used with Safe accounts. After that, we add the signature from `safe1_1` to our initial transaction. ```typescript - // Build the contract signature of safe2_3 + // Build the contract signature of SAFE_2_3_ADDRESS const signatureSafe2_3 = await buildContractSignature( Array.from(transactionSafe2_3.signatures.values()), - safe2_3 + SAFE_2_3_ADDRESS ) // Add the signatureSafe2_3 to safeTransaction - // After this, the safeTransaction contains the signature from owner1, owner2, safe1_1 and safe2_3 + // After this, the safeTransaction contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS, SAFE_1_1_ADDRESS and SAFE_2_3_ADDRESS safeTransaction.addSignature(signatureSafe2_3) ``` @@ -375,10 +386,8 @@ This guide explains how transactions are signed by the Safe owners using the Pro To store a new transaction, we need to call the `proposeTransaction` from the API Kit, passing the Safe address, an object with the transaction, and a signature from one owner. ```typescript - const signerAddress = (await ethAdapter1.getSignerAddress()) || '0x' - - // Get the signature from owner1 - const signatureOwner1 = safeTransaction.getSignature(signerAddress) as EthSafeSignature + // Get the signature from OWNER_1_ADDRESS + const signatureOwner1 = safeTransaction.getSignature(OWNER_1_ADDRESS) as EthSafeSignature // Get the transaction hash of the safeTransaction const safeTransactionHash = await protocolKit.getTransactionHash(safeTransaction) @@ -389,7 +398,7 @@ This guide explains how transactions are signed by the Safe owners using the Pro // Propose the transaction await apiKit.proposeTransaction({ - safeAddress: safe3_4, + safeAddress: SAFE_3_4_ADDRESS, safeTransactionData: safeTransaction.data, safeTxHash: safeTransactionHash, senderAddress: signerAddress, @@ -406,23 +415,21 @@ This guide explains how transactions are signed by the Safe owners using the Pro Once a transaction is proposed, it becomes available on [Safe\{Wallet\}](https://app.safe.global). However, to execute the transaction, all the confirmations from the owners are needed. ```typescript - const signerAddress = (await ethAdapter2.getSignerAddress()) || '0x' - - const signatureOwner2 = safeTransaction.getSignature(signerAddress) as EthSafeSignature + const signatureOwner2 = safeTransaction.getSignature(OWNER_2_ADDRESS) as EthSafeSignature - // Confirm the transaction from owner2 + // Confirm the transaction from OWNER_2_ADDRESS await apiKit.confirmTransaction( safeTransactionHash, buildSignatureBytes([signatureOwner2]) ) - // Confirm the transaction with the owner safe1_1 + // Confirm the transaction with the owner SAFE_1_1_ADDRESS await apiKit.confirmTransaction( safeTransactionHash, buildSignatureBytes([signatureSafe1_1]) ) - // Add signature from the owner safe2_3 + // Add signature from the owner SAFE_2_3_ADDRESS await apiKit.confirmTransaction( safeTransactionHash, buildSignatureBytes([signerSafeSig2_3]) @@ -449,12 +456,13 @@ This guide explains how transactions are signed by the Safe owners using the Pro ### Execute the transaction - Connect the Safe and the adapter of an owner to the Protocol Kit. Ensure enough funds are available in the owner's account to execute the transaction and cover the gas costs. Once the Protocol Kit is initialized, the `executeTransaction` method receives and executes the transaction with the required signatures. + Connect the Safe and an a signer to the Protocol Kit. Ensure enough funds are available in the owner's account to execute the transaction and cover the gas costs. Once the Protocol Kit is initialized, the `executeTransaction` method receives and executes the transaction with the required signatures. ```typescript protocolKit = await protocolKit.connect({ - ethAdapter: ethAdapter1, - safeAddress: safe3_4 + provider: RPC_URL, + signer: OWNER_1_PRIVATE_KEY, + safeAddress: SAFE_3_4_ADDRESS }) // Execute the Safe transaction @@ -479,14 +487,14 @@ This guide explains how transactions are signed by the Safe owners using the Pro | 1/1 Safe signer | Safe Address | 32 | 32 |
000000000000000000000000215033cdE0619D60B7352348F4598316Cc39bC6E
| | Data position for 1/1 Safe | 104 hex = Signature data for 1/1 Safe start at byte 260 | 32 | 64 |
0000000000000000000000000000000000000000000000000000000000000104
| | Signature Type | Smart contract signature | 1 | 65 |
00
| - | Owner signature | `owner1` signature | 65 | 130 |
969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f
| + | Owner signature | `OWNER_1_ADDRESS` signature | 65 | 130 |
969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f
| | 2/3 Safe signer | Safe Address | 32 | 162 |
000000000000000000000000f75D61D6C27a7CC5788E633c1FC130f0F4a62D33
| | Data position for 2/3 Verifier | 165 hex = Signature data for 2/3 Safe start at byte 357 | 32 | 194 |
0000000000000000000000000000000000000000000000000000000000000165
| | Signature | Type Smart contract signature | 1 | 195 |
00
| - | Owner signature | `owner2` signature | 65 | 260 |
4d63c79cf9d743782bc31ad58c1a316020b39839ab164caee7ecac9829f685cc44ec0d066a5dfe646b2ffeeb37575df131daf9c96ced41b8c7c4aea8dc5461801c
| + | Owner signature | `OWNER_2_ADDRESS` signature | 65 | 260 |
4d63c79cf9d743782bc31ad58c1a316020b39839ab164caee7ecac9829f685cc44ec0d066a5dfe646b2ffeeb37575df131daf9c96ced41b8c7c4aea8dc5461801c
| | 1/1 Safe Signature Length | Start of the 1/1 Safe Signature. 41 hex = 65 bytes | 32 | 292 |
0000000000000000000000000000000000000000000000000000000000000041
| - | Signature | `owner3` signature | 65 | 357 |
5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f
| + | Signature | `OWNER_3_ADDRESS` signature | 65 | 357 |
5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f
| | 2/3 Safe Signature length | Start of the 2/3 Safe Signature. 82 hex = 130 bytes | 32 | 389 |
0000000000000000000000000000000000000000000000000000000000000082
| - | Signature | `owner4` and `owner5` concatenated signatures | 130 | 519 |
023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f
| + | Signature | `OWNER_4_ADDRESS` and `OWNER_5_ADDRESS` concatenated signatures | 130 | 519 |
023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f
| diff --git a/pages/sdk/protocol-kit/reference/safe-factory.md b/pages/sdk/protocol-kit/reference/safe-factory.md index 6e437f0c..2b3554ca 100644 --- a/pages/sdk/protocol-kit/reference/safe-factory.md +++ b/pages/sdk/protocol-kit/reference/safe-factory.md @@ -2,12 +2,17 @@ ### `create` -Returns an instance of the Safe Factory. +It receives a provider and a signer as required parameters and returns an instance of the Safe Factory. + +The `provider` property can be an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible provider or an RPC URL. The `signer` property can be the signer address or its private key. ```typescript import { SafeFactory } from '@safe-global/protocol-kit' -const safeFactory = await SafeFactory.create({ ethAdapter }) +const safeFactory = await SafeFactory.create({ + provider, + signer +}) ``` - The `isL1SafeSingleton` flag @@ -17,7 +22,11 @@ const safeFactory = await SafeFactory.create({ ethAdapter }) By default, `Safe.sol` will only be used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `SafeL2.sol` contract will be used unless you add the `isL1SafeSingleton` flag to force using the `Safe.sol` contract. ```typescript - const safeFactory = await SafeFactory.create({ ethAdapter, isL1SafeSingleton: true }) + const safeFactory = await SafeFactory.create({ + provider, + signer, + isL1SafeSingleton: true + }) ``` - The `contractNetworks` property @@ -25,9 +34,14 @@ const safeFactory = await SafeFactory.create({ ethAdapter }) If the Safe contracts aren't deployed to your current network, the `contractNetworks` property will be required to point to the addresses of the Safe contracts previously deployed by you. ```typescript - import { ContractNetworksConfig } from '@safe-global/protocol-kit' + import { + ContractNetworksConfig, + SafeProvider + } from '@safe-global/protocol-kit' + + const safeProvider = new SafeProvider({ provider, signer }) + const chainId = await safeProvider.getChainId() - const chainId = await ethAdapter.getChainId() const contractNetworks: ContractNetworksConfig = { [chainId]: { safeSingletonAddress: '', @@ -49,7 +63,11 @@ const safeFactory = await SafeFactory.create({ ethAdapter }) } } - const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks }) + const safeFactory = await SafeFactory.create({ + provider, + signer, + contractNetworks + }) ``` - The `safeVersion` property @@ -58,7 +76,11 @@ const safeFactory = await SafeFactory.create({ ethAdapter }) ```typescript const safeVersion = 'X.Y.Z' - const safeFactory = await SafeFactory.create({ ethAdapter, safeVersion }) + const safeFactory = await SafeFactory.create({ + provider, + signer, + safeVersion + }) ``` ### `deploySafe` @@ -102,18 +124,7 @@ const protocolKit = await safeFactory.deploySafe({ safeAccountConfig, saltNonce Optionally, some properties can be passed as execution options: ```typescript -const options: Web3TransactionOptions = { - from, // Optional - gas, // Optional - gasPrice, // Optional - maxFeePerGas, // Optional - maxPriorityFeePerGas // Optional - nonce // Optional -} -``` - -```typescript -const options: EthersTransactionOptions = { +const options: TransactionOptions = { from, // Optional gasLimit, // Optional gasPrice, // Optional diff --git a/pages/sdk/protocol-kit/reference/safe.md b/pages/sdk/protocol-kit/reference/safe.md index 809332fa..31ae54ff 100644 --- a/pages/sdk/protocol-kit/reference/safe.md +++ b/pages/sdk/protocol-kit/reference/safe.md @@ -2,30 +2,39 @@ ## Initialization -### `connect` +### `create` -Returns a new instance of the Protocol Kit connected to a new Safe or a new Signer. The new connected signer can be passed via the `ethAdapter` property while the new connected Safe can be passed using a `safeAddress` or a `predictedSafe`. +Returns an instance of the Protocol Kit connected to a Safe. -Connection of a deployed Safe using the `safeAddress` property: +The `provider` property can be an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible provider or an RPC URL. The `signer` property can be the signer address or its private key. The provided Safe must be a `safeAddress` or a `predictedSafe`. + +Initialization of a deployed Safe using the `safeAddress` property: ```typescript -let protocolKit = await Safe.create({ ethAdapter, safeAddress }) -protocolKit = await protocolKit.connect({ ethAdapter: anotherEthAdapter, safeAddress: anotherSafeAddress }) +import Safe from '@safe-global/protocol-kit' + +const protocolKit = await Safe.create({ + provider, + signer, + safeAddress +}) ``` -Connection of an undeployed Safe using the `predictedSafe` property. Because Safes are deployed in a deterministic way, passing a `predictedSafe` will allow to connect a Safe to the SDK with the Safe configuration: +Initialization of an undeployed Safe using the `predictedSafe` property. Because Safes are deployed in a deterministic way, passing a `predictedSafe` will allow to initialize the SDK with the Safe configuration and use it to some extent before it's deployed: ```typescript -import { PredictedSafeProps } from '@safe-global/protocol-kit' +import Safe, { PredictedSafeProps } from '@safe-global/protocol-kit' const predictedSafe: PredictedSafeProps = { safeAccountConfig, safeDeploymentConfig } -let protocolKit = await Safe.create({ ethAdapter, safeAddress }) -... -protocolKit = await protocolKit.connect({ predictedSafe }) +const protocolKit = await Safe.create({ + provider, + signer, + predictedSafe +}) ``` - The `isL1SafeSingleton` flag @@ -35,7 +44,12 @@ protocolKit = await protocolKit.connect({ predictedSafe }) By default `Safe.sol` will only be used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `SafeL2.sol` contract will be used unless you add the `isL1SafeSingleton` flag to force using the `Safe.sol` contract. ```typescript - protocolKit = await protocolKit.connect({ ethAdapter, safeAddress, isL1SafeSingleton: true }) + const protocolKit = await Safe.create({ + provider, + signer, + safeAddress, + isL1SafeSingleton: true + }) ``` - The `contractNetworks` property @@ -43,9 +57,14 @@ protocolKit = await protocolKit.connect({ predictedSafe }) If the Safe contracts aren't deployed to your current network, the `contractNetworks` property will be required to point to the addresses of the Safe contracts previously deployed by you. ```typescript - import { ContractNetworksConfig } from '@safe-global/protocol-kit' + import { + ContractNetworksConfig, + SafeProvider + } from '@safe-global/protocol-kit' + + const safeProvider = new SafeProvider({ provider, signer }) + const chainId = await safeProvider.getChainId() - const chainId = await ethAdapter.getChainId() const contractNetworks: ContractNetworksConfig = { [chainId]: { safeSingletonAddress: '', @@ -66,34 +85,55 @@ protocolKit = await protocolKit.connect({ predictedSafe }) simulateTxAccessorAbi: '' // Optional. Only needed with web3.js } } - let protocolKit = await Safe.create({ ethAdapter, safeAddress }) - ... - protocolKit = await protocolKit.connect({ contractNetworks }) + + const protocolKit = await Safe.create({ + provider, + signer, + safeAddress, + contractNetworks + }) ``` -### `create` +### `connect` -Returns an instance of the Protocol Kit connected to a Safe. The provided Safe must be a `safeAddress` or a `predictedSafe`. +Returns a new instance of the Protocol Kit connected to a new Safe or a new signer. The new connected signer can be passed via the `signer` property while the new connected Safe can be passed using a `safeAddress` or a `predictedSafe`. -Initialization of a deployed Safe using the `safeAddress` property: +The `provider` property can be an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible provider or an RPC URL. The `signer` property can be the signer address or its private key. + +Connection of a deployed Safe using the `safeAddress` property: ```typescript -import Safe from '@safe-global/protocol-kit' +let protocolKit = await Safe.create({ + provider, + signer, + safeAddress +}) -const protocolKit = await Safe.create({ ethAdapter, safeAddress }) +protocolKit = await protocolKit.connect({ + signer: anotherSigner, + safeAddress: anotherSafeAddress +}) ``` -Initialization of an undeployed Safe using the `predictedSafe` property. Because Safes are deployed in a deterministic way, passing a `predictedSafe` will allow to initialize the SDK with the Safe configuration and use it to some extent before it's deployed: +Connection of an undeployed Safe using the `predictedSafe` property. Because Safes are deployed in a deterministic way, passing a `predictedSafe` will allow to connect a Safe to the SDK with the Safe configuration: ```typescript -import Safe, { PredictedSafeProps } from '@safe-global/protocol-kit' +import { PredictedSafeProps } from '@safe-global/protocol-kit' const predictedSafe: PredictedSafeProps = { safeAccountConfig, safeDeploymentConfig } -const protocolKit = await Safe.create({ ethAdapter, predictedSafe }) +let protocolKit = await Safe.create({ + provider, + signer, + safeAddress +}) + +// ... + +protocolKit = await protocolKit.connect({ predictedSafe }) ``` - The `isL1SafeSingleton` flag @@ -103,7 +143,12 @@ const protocolKit = await Safe.create({ ethAdapter, predictedSafe }) By default `Safe.sol` will only be used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `SafeL2.sol` contract will be used unless you add the `isL1SafeSingleton` flag to force using the `Safe.sol` contract. ```typescript - const protocolKit = await Safe.create({ ethAdapter, safeAddress, isL1SafeSingleton: true }) + protocolKit = await protocolKit.connect({ + provider, + signer, + safeAddress, + isL1SafeSingleton: true + }) ``` - The `contractNetworks` property @@ -111,9 +156,14 @@ const protocolKit = await Safe.create({ ethAdapter, predictedSafe }) If the Safe contracts aren't deployed to your current network, the `contractNetworks` property will be required to point to the addresses of the Safe contracts previously deployed by you. ```typescript - import { ContractNetworksConfig } from '@safe-global/protocol-kit' + import { + ContractNetworksConfig, + SafeProvider + } from '@safe-global/protocol-kit' + + const safeProvider = new SafeProvider({ provider, signer }) + const chainId = await safeProvider.getChainId() - const chainId = await ethAdapter.getChainId() const contractNetworks: ContractNetworksConfig = { [chainId]: { safeSingletonAddress: '', @@ -135,7 +185,15 @@ const protocolKit = await Safe.create({ ethAdapter, predictedSafe }) } } - const protocolKit = await Safe.create({ ethAdapter, safeAddress, contractNetworks }) + let protocolKit = await Safe.create({ + provider, + signer, + safeAddress + }) + + // ... + + protocolKit = await protocolKit.connect({ contractNetworks }) ``` ## Safe Info @@ -296,18 +354,7 @@ await txResponse.transactionResponse?.wait() Optionally, some properties can be passed as execution options: ```typescript -const options: Web3TransactionOptions = { - from, // Optional - gas, // Optional - gasPrice, // Optional - maxFeePerGas, // Optional - maxPriorityFeePerGas // Optional - nonce // Optional -} -``` - -```typescript -const options: EthersTransactionOptions = { +const options: TransactionOptions = { from, // Optional gasLimit, // Optional gasPrice, // Optional @@ -348,18 +395,7 @@ const isValidTx = await protocolKit.isValidTransaction(safeTransaction) Optionally, some properties can be passed as execution options: ```typescript -const options: Web3TransactionOptions = { - from, // Optional - gas, // Optional - gasPrice, // Optional - maxFeePerGas, // Optional - maxPriorityFeePerGas // Optional - nonce // Optional -} -``` - -```typescript -const options: EthersTransactionOptions = { +const options: TransactionOptions = { from, // Optional gasLimit, // Optional gasPrice, // Optional @@ -392,18 +428,7 @@ await txResponse.transactionResponse?.wait() Optionally, some properties can be passed as execution options: ```typescript -const options: Web3TransactionOptions = { - from, // Optional - gas, // Optional - gasPrice, // Optional - maxFeePerGas, // Optional - maxPriorityFeePerGas // Optional - nonce // Optional -} -``` - -```typescript -const options: EthersTransactionOptions = { +const options: TransactionOptions = { from, // Optional gasLimit, // Optional gasPrice, // Optional diff --git a/pages/sdk/relay-kit/guides/4337-safe-sdk.mdx b/pages/sdk/relay-kit/guides/4337-safe-sdk.mdx index 5ae95d40..09fb3dfb 100644 --- a/pages/sdk/relay-kit/guides/4337-safe-sdk.mdx +++ b/pages/sdk/relay-kit/guides/4337-safe-sdk.mdx @@ -16,7 +16,7 @@ Read the [Safe4337Module documentation](../../../home/4337-safe) to understand i ## Install dependencies ```bash -yarn add ethers @safe-global/protocol-kit @safe-global/relay-kit +yarn add @safe-global/relay-kit ``` ## Steps @@ -30,8 +30,6 @@ yarn add ethers @safe-global/protocol-kit @safe-global/relay-kit {/* */} ```typescript - import { ethers } from 'ethers' - import { EthersAdapter } from '@safe-global/protocol-kit' import { Safe4337Pack } from '@safe-global/relay-kit' ``` @@ -39,21 +37,16 @@ yarn add ethers @safe-global/protocol-kit @safe-global/relay-kit ### Create a signer - Firstly, we need to get and instantiate a signer using the `EthersAdapter` from the Protocol Kit, which will be the owner of a Safe account after it's deployed. + Firstly, we need to get a signer, which will be the owner of a Safe account after it's deployed. - In this example, we use a private key, but any method to get a [signer](https://docs.ethers.org/v6/api/providers/#Signer) can be used. + In this example, we use a private key, but any way to get an EIP-1193 compatible signer can be used. {/* */} ```typescript - const PRIVATE_KEY = '0x...' - - const provider = new ethers.JsonRpcProvider('https://rpc.ankr.com/eth_sepolia') - const signer = new ethers.Wallet(PRIVATE_KEY, provider) - const ethersAdapter = new EthersAdapter({ - ethers, - signerOrProvider: signer - }) + const SIGNER_ADDRESS = // ... + const SIGNER_PRIVATE_KEY = // ... + const RPC_URL = 'https://rpc.ankr.com/eth_sepolia' ``` {/* */} @@ -72,11 +65,12 @@ yarn add ethers @safe-global/protocol-kit @safe-global/relay-kit ```typescript const safe4337Pack = await Safe4337Pack.init({ - ethersAdapter, - rpcUrl: 'https://rpc.ankr.com/eth_sepolia', + provider: RPC_URL, + signer: SIGNER_PRIVATE_KEY, + rpcUrl: RPC_URL, bundlerUrl: `https://api.pimlico.io/v1/sepolia/rpc?apikey=${PIMLICO_API_KEY}`, options: { - owners: [await signer.getAddress()], + owners: [SIGNER_ADDRESS], threshold: 1 }, // ... @@ -88,8 +82,9 @@ yarn add ethers @safe-global/protocol-kit @safe-global/relay-kit ```typescript const safe4337Pack = await Safe4337Pack.init({ - ethersAdapter, - rpcUrl: 'https://rpc.ankr.com/eth_sepolia', + provider: RPC_URL, + signer: SIGNER_PRIVATE_KEY, + rpcUrl: RPC_URL, bundlerUrl: `https://api.pimlico.io/v1/sepolia/rpc?apikey=${PIMLICO_API_KEY}`, options: { safeAddress: '0x...' diff --git a/pages/sdk/relay-kit/guides/gelato-relay.mdx b/pages/sdk/relay-kit/guides/gelato-relay.mdx index 604acc3d..3a7772ca 100644 --- a/pages/sdk/relay-kit/guides/gelato-relay.mdx +++ b/pages/sdk/relay-kit/guides/gelato-relay.mdx @@ -47,7 +47,7 @@ For the 1Balance quickstart tutorial, you will use the Gelato relayer to pay for ```typescript import { ethers } from 'ethers' import { GelatoRelayPack } from '@safe-global/relay-kit' - import Safe, { EthersAdapter } from '@safe-global/protocol-kit' + import Safe from '@safe-global/protocol-kit' import { MetaTransactionData, MetaTransactionOptions @@ -60,9 +60,8 @@ For the 1Balance quickstart tutorial, you will use the Gelato relayer to pay for ```typescript // https://chainlist.org - const RPC_URL='https://endpoints.omniatech.io/v1/bsc/mainnet/public' - const provider = new ethers.JsonRpcProvider(RPC_URL) - const signer = new ethers.Wallet(process.env.OWNER_1_PRIVATE_KEY!, provider) + const RPC_URL = 'https://endpoints.omniatech.io/v1/bsc/mainnet/public' + const OWNER_PRIVATE_KEY = process.env.OWNER_PRIVATE_KEY const safeAddress = '0x...' // Safe from which the transaction will be sent // Any address can be used for destination. In this example, we use vitalik.eth @@ -88,13 +87,9 @@ For the 1Balance quickstart tutorial, you will use the Gelato relayer to pay for ### Instantiate the Protocol Kit and Relay Kit ```typescript - const ethAdapter = new EthersAdapter({ - ethers, - signerOrProvider: signer - }) - const protocolKit = await Safe.create({ - ethAdapter, + provider: RPC_URL, + signer: OWNER_PRIVATE_KEY, safeAddress }) @@ -141,7 +136,7 @@ For the SyncFee quickstart tutorial, you will use the Gelato relayer to pay for ```typescript import { ethers } from 'ethers' import { GelatoRelayPack } from '@safe-global/relay-kit' - import Safe, { EthersAdapter } from '@safe-global/protocol-kit' + import Safe from '@safe-global/protocol-kit' import { MetaTransactionData } from '@safe-global/safe-core-sdk-types' ``` @@ -151,9 +146,8 @@ For the SyncFee quickstart tutorial, you will use the Gelato relayer to pay for ```typescript // https://chainlist.org - const RPC_URL='https://endpoints.omniatech.io/v1/bsc/mainnet/public' - const provider = new ethers.JsonRpcProvider(RPC_URL) - const signer = new ethers.Wallet(process.env.OWNER_1_PRIVATE_KEY!, provider) + const RPC_URL = 'https://endpoints.omniatech.io/v1/bsc/mainnet/public' + const OWNER_PRIVATE_KEY = process.env.OWNER_PRIVATE_KEY const safeAddress = '0x...' // Safe from which the transaction will be sent // Any address can be used for destination. In this example, we use vitalik.eth @@ -175,13 +169,9 @@ For the SyncFee quickstart tutorial, you will use the Gelato relayer to pay for ### Instantiate the Protocol Kit and Relay Kit ```typescript - const ethAdapter = new EthersAdapter({ - ethers, - signerOrProvider: signer - }) - const protocolKit = await Safe.create({ - ethAdapter, + provider: RPC_URL, + signer: OWNER_PRIVATE_KEY, safeAddress }) diff --git a/pages/sdk/relay-kit/reference/safe-4337-pack.mdx b/pages/sdk/relay-kit/reference/safe-4337-pack.mdx index 1b987b15..9d963667 100644 --- a/pages/sdk/relay-kit/reference/safe-4337-pack.mdx +++ b/pages/sdk/relay-kit/reference/safe-4337-pack.mdx @@ -18,7 +18,8 @@ The `Safe4337Pack` class make easy to use the [Safe 4337 Module](https://github. ```typescript const safe4337Pack = await Safe4337Pack.init({ - ethersAdapter, + provider, + signer, rpcUrl, bundlerUrl, safeModulesVersion, @@ -38,7 +39,8 @@ The `Safe4337InitOptions` used in the `init()` method are: ```typescript Safe4337InitOptions = { - ethersAdapter: EthersAdapter + provider: Eip1193Provider | HttpTransport | SocketTransport + signer?: HexAddress | PrivateKey bundlerUrl: string rpcUrl: string safeModulesVersion?: string @@ -51,6 +53,20 @@ Safe4337InitOptions = { paymasterOptions?: PaymasterOptions } +HexAddress = string +PrivateKey = string +HttpTransport = string +SocketTransport = string + +Eip1193Provider = { + request: (args: RequestArguments) => Promise +} + +RequestArguments = { + method: string + params?: readonly unknown[] | object +} + ExistingSafeOptions = { safeAddress: string } @@ -72,8 +88,9 @@ PaymasterOptions = { } ``` -- **`ethersAdapter`** : An instance of the `EthersAdapter` class. -- **`rpcUrl`** : The RPC for the selected chain. +- **`provider`** : The EIP-1193 compatible provider or RPC URL of the selected chain. +- **`signer`** : The signer private address if the `provider` doesn't resolve to a signer account. If the `provider` resolves to multiple signer addresses, the `signer` property can be used to specify which account to connect, otherwise the first address returned will be used. +- **`rpcUrl`** : The RPC URL of the selected chain. - **`bundlerUrl`** : The bundler's URL. - **`safeModulesVersion`** : The version of the [Safe Modules contract](https://github.com/safe-global/safe-modules-deployments/tree/main/src/assets/safe-4337-module). - **`customContracts`** : An object with custom contract addresses. This is optional, if no custom contracts are provided, default ones will be used. @@ -100,7 +117,6 @@ A promise that resolves to an instance of the `Safe4337Pack`. **Caveats** - Use this method to create the initial instance instead of the standard constructor. -- You can refer to [this link](https://github.com/safe-global/safe-core-sdk/tree/main/packages/protocol-kit/src/adapters/ethers) to create instances of `EthersAdapter`. - You should search for some API services URLs and contract addresses in the management dashboards of your selected provider. These include `bundlerUrl`, `paymasterUrl`, `paymasterAddress`, `paymasterTokenAddress`, `sponsorshipPolicyId`, and `rpcUrl` (In this case any valid RPC should be fine). - The SDK uses default versions when `safeModulesVersion` or `safeVersion` are not specified. You can find more details about the current versions [here](https://github.com/safe-global/safe-core-sdk/blob/924ae56ff707509e561c99296fb5e1fbc2050d28/packages/relay-kit/src/packs/safe-4337/Safe4337Pack.ts#L34-L35). - The `saltNonce` derives different Safe addresses by using the `protocol-kit` method `predictSafeAddress`. You can find more details about this process [here](https://github.com/safe-global/safe-core-sdk/blob/924ae56ff707509e561c99296fb5e1fbc2050d28/packages/protocol-kit/src/contracts/utils.ts#L245-L315). @@ -195,7 +211,7 @@ A promise that resolves to the signed `SafeOperation`. **Caveats** - Use this method after the `SafeOperation` is generated with the `createTransaction` method. -- This method adds the signer's signature from the `EthersAdapter` to the signatures map of the `SafeOperation` object. Additional signatures can be included from multiple owners. +- This method adds the signer's signature to the signatures map of the `SafeOperation` object. Additional signatures can be included from multiple owners. - It works similar to `signTransaction` and `signMessage` methods in the `protocol-kit` but using `SafeOperation` instead of `SafeTransaction` or `SafeMessage`. For more information, refer to the Safe [docs](https://docs.safe.global/sdk/protocol-kit/guides/signatures). ### `executeTransaction(safe4337ExecutableProps)` @@ -225,7 +241,7 @@ A promise, resolves to the user operation hash. **Caveats** -- The process converts the `SafeOperation` to a standard user operation, then forwards it to the bundler. The `SafeOperation` must be created and signed by the Safe owner using `EthersAdapter`. +- The process converts the `SafeOperation` to a standard user operation, then forwards it to the bundler. The `SafeOperation` must be created and signed by the Safe owner. - You can use the user operation hash to browse the status (e.g `https://jiffyscan.xyz/userOpHash/{userOpHash}`) ### `getUserOperationByHash(userOpHash)`