From cfa8c29c49242e5b770f6c94efc6437d39512fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Nov 2021 17:45:19 +0100 Subject: [PATCH 1/5] Update guide README file --- packages/guides/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/guides/README.md b/packages/guides/README.md index 67d62f27e..a871da3c5 100644 --- a/packages/guides/README.md +++ b/packages/guides/README.md @@ -8,7 +8,7 @@ Read about the basics of Gnosis Safe and how it compares to other solutions [her The [Safe Core SDK](https://github.com/gnosis/safe-core-sdk) is a monorepo that contains software developer tools that allows interaction with the [Safe contracts](https://github.com/gnosis/safe-contracts) and the [Safe Transaction Service](https://github.com/gnosis/safe-transaction-service). -In this guide we will be using the following packages to deploy new Safes, create transactions, collect the off-chain signatures and execute those transactions: +In this guide we will be using the following packages to deploy new Safes, create transactions, collect the signatures off-chain and execute those transactions: * **safe-core-sdk-types** From c8e07df6bcf18a8d1aafdf08562803ff7e478c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Nov 2021 17:47:01 +0100 Subject: [PATCH 2/5] Update Safe Core SDK README file --- packages/safe-core-sdk/README.md | 430 +++++++++++++++++++++---------- 1 file changed, 292 insertions(+), 138 deletions(-) diff --git a/packages/safe-core-sdk/README.md b/packages/safe-core-sdk/README.md index 7b207762d..1e731fd25 100644 --- a/packages/safe-core-sdk/README.md +++ b/packages/safe-core-sdk/README.md @@ -3,11 +3,17 @@ [![NPM Version](https://badge.fury.io/js/%40gnosis.pm%2Fsafe-core-sdk.svg)](https://badge.fury.io/js/%40gnosis.pm%2Fsafe-core-sdk) [![GitHub Release](https://img.shields.io/github/release/gnosis/safe-core-sdk.svg?style=flat)](https://github.com/gnosis/safe-core-sdk/releases) [![GitHub](https://img.shields.io/github/license/gnosis/safe-core-sdk)](https://github.com/gnosis/safe-core-sdk/blob/main/LICENSE.md) -[![Coverage Status](https://coveralls.io/repos/github/gnosis/safe-core-sdk/badge.svg?branch=main)](https://coveralls.io/github/gnosis/safe-core-sdk?branch=main) Software development kit that facilitates the interaction with the [Gnosis Safe contracts](https://github.com/gnosis/safe-contracts). -## Installation +## Table of contents +* [Installation](#installation) +* [Build](#build) +* [Getting Started](#getting-started) +* [Safe Factory API Reference](#factory-api) +* [Safe Core SDK API Reference](#sdk-api) + +## Installation Install the package with yarn or npm: @@ -16,7 +22,7 @@ yarn install npm install ``` -## Build +## Build Build the package with yarn or npm: @@ -25,37 +31,43 @@ yarn build npm build ``` -## Getting Started +## Getting Started + +The following steps show how to set up the Safe Core SDK, deploy a new Safe, create a Safe transaction, generate the required signatures from its owners and execute the transaction. However, using the Safe Core SDK alone will not allow to collect the signatures of the owners off-chain. To do this and be able to see and confirm the pending transactions showed in the [Gnosis Safe Web App](https://gnosis-safe.io/app/), it is recommended that you follow this other [guide](/packages/guides/integrating-the-safe-core-sdk.md) that covers the use of the Safe Core SDK combined with the Safe Service Client. ### 1. Set up the SDK using `Ethers` or `Web3` -If the app integrating the SDK is using `Ethers` `v5`, create an instance of the `EthersAdapter`. `owner1` is the Ethereum account we are connecting and the one who will sign the transactions. +* **Using ethers.js** -```js -import { ethers } from 'ethers' -import { EthersAdapter } from '@gnosis.pm/safe-core-sdk' + If the app integrating the SDK is using `Ethers` `v5`, create an instance of the `EthersAdapter`. `owner1` is the Ethereum account we are connecting and the one who will sign the transactions. -const web3Provider = // ... -const provider = new ethers.providers.Web3Provider(web3Provider) -const owner1 = provider.getSigner(0) + ```js + import { ethers } from 'ethers' + import { EthersAdapter } from '@gnosis.pm/safe-core-sdk' -const ethAdapterOwner1 = new EthersAdapter({ - ethers, - signer: owner1 -}) -``` + const web3Provider = // ... + const provider = new ethers.providers.Web3Provider(web3Provider) + const owner1 = provider.getSigner(0) -If the app integrating the SDK is using `Web3` `v1`, create an instance of the `Web3Adapter`. + const ethAdapterOwner1 = new EthersAdapter({ + ethers, + signer: owner1 + }) + ``` -```js -import Web3 from 'web3' -import { Web3Adapter } from '@gnosis.pm/safe-core-sdk' +* **Using web3.js** -const ethAdapterOwner1 = new Web3Adapter({ - web3, - signerAddress: await owner1.getAddress() -}) -``` + If the app integrating the SDK is using `Web3` `v1`, create an instance of the `Web3Adapter`. + + ```js + import Web3 from 'web3' + import { Web3Adapter } from '@gnosis.pm/safe-core-sdk' + + const ethAdapterOwner1 = new Web3Adapter({ + web3, + signerAddress: await owner1.getAddress() + }) + ``` ### 2. Deploy a new Safe @@ -77,26 +89,7 @@ const safeAccountConfig: SafeAccountConfig = { const safeSdk: Safe = await safeFactory.deploySafe(safeAccountConfig) ``` -The method `deploySafe` executes a transaction from `owner1` account, deploys a new Safe and returns an instance of the Safe Core SDK connected to the new Safe. - -The `SafeFactory` will deploy the last version of the Safe contracts available by default (currently `v1.3.0`). To deploy an older version of the Safe contracts instantiate the `SafeFactory` adding the property `safeVersion` with the desired version number. - -```js -const safeFactoryV1_1_1 = await SafeFactory.create({ ethAdapter, safeVersion: '1.1.1' }) -``` - -The property `contractNetworks` can also be used to provide the Safe contract addresses in case the SDK is used in a network where the Safe contracts are not deployed. - -```js -const contractNetworks: ContractNetworksConfig = { - [chainId]: { - multiSendAddress: '0x', - safeMasterCopyAddress: '0x', - safeProxyFactoryAddress: '0x' - } -} -const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks }) -``` +The method `deploySafe` executes a transaction from `owner1` account, deploys a new Safe and returns an instance of the Safe Core SDK connected to the new Safe. Check the method `deploySafe` in the [API Reference](#factory-api) for more details on additional configuration parameters. Call the method `getAddress`, for example, to check the address of the newly deployed Safe. @@ -112,19 +105,23 @@ import Safe from '@gnosis.pm/safe-core-sdk' const safeSdk: Safe = await Safe.create({ ethAdapter: ethAdapterOwner1, safeAddress }) ``` +Check the method `create` in the [API Reference](#sdk-api) for more details on additional configuration parameters. + ### 3. Create a Safe transaction ```js import { SafeTransactionDataPartial } from '@gnosis.pm/safe-core-sdk-types' -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { to: '0x
', value: '', data: '0x' -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) ``` +Check the method `createTransaction` in the [API Reference](#sdk-api) for additional details on creating MultiSend transactions. + Before executing this transaction, it must be signed by the owners and this can be done off-chain or on-chain. In this example `owner1` will sign it off-chain, `owner2` will sign it on-chain and `owner3` will execute it (the executor also signs the transaction transparently). ### 3.a. Off-chain signatures @@ -162,28 +159,134 @@ await executeTxResponse.transactionResponse?.wait() All the signatures used to execute the transaction are now available at `safeTransaction.signatures`. -## API Reference +## Safe Factory API Reference ### create -Returns an instance of the Safe Core SDK connected to the `safeAddress`. + +Returns an instance of the Safe Factory. ```js -const safeSdk = await Safe.create({ ethAdapter, safeAddress }) +import { SafeFactory } from '@gnosis.pm/safe-core-sdk' + +const safeFactory = await SafeFactory.create({ ethAdapter }) ``` -The property `contractNetworks` can be added to provide the Safe contract addresses in case the SDK is used in a network where the Safe contracts are not deployed. +* The property `isL1SafeMasterCopy` -```js -const contractNetworks: ContractNetworksConfig = { - [chainId]: { - multiSendAddress: '0x', - safeMasterCopyAddress: '0x', - safeProxyFactoryAddress: '0x' + There are two versions of the Safe contracts: [GnosisSafe.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafe.sol) that does not trigger events in order to save gas and [GnosisSafeL2.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafeL2.sol) that does, which is more appropriate for L2 networks. + + By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the property `isL1SafeMasterCopy` to force the use of the `GnosisSafe.sol` contract. + + ```js + const safeFactory = await SafeFactory.create({ ethAdapter, isL1SafeMasterCopy: true }) + ``` + +* The property `contractNetworks` + + If the Safe contracts are not deployed to your current network, the property `contractNetworks` will be required to point to the addresses of the Safe contracts previously deployed by you. + + ```js + import { ContractNetworksConfig } from '@gnosis.pm/safe-core-sdk' + + const id = await ethAdapter.getChainId() + const contractNetworks: ContractNetworksConfig = { + [id]: { + multiSendAddress: '', + safeMasterCopyAddress: '', + safeProxyFactoryAddress: '' + } } + + const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks }) + ``` + +* The property `safeVersion` + + The `SafeFactory` constructor also accepts the property `safeVersion` to specify the Safe contract version that will deploy. This string can take the values `1.1.1`, `1.2.0` or `1.3.0`. If not specified, the most recent contract version will be used by default. + + ```js + const safeVersion = 'X.Y.Z' + const safeFactory = await SafeFactory.create({ ethAdapter, safeVersion }) + ``` + +### deploySafe + +Deploys a new Safe and returns an instance of the Safe Core SDK connected to the deployed Safe. The address of the Master Copy, Safe contract version and the contract (`GnosisSafe.sol` or `GnosisSafeL2.sol`) of the deployed Safe will depend on the initialization of the `safeFactory` instance. + +```js +const safeAccountConfig: SafeAccountConfig = { + owners, + threshold, + to, // Optional + data, // Optional + fallbackHandler, // Optional + paymentToken, // Optional + payment, // Optional + paymentReceiver // Optional } -const safeSdk = await Safe.create({ ethAdapter, safeAddress, contractNetworks }) + +const safeSdk = await safeFactory.deploySafe(safeAccountConfig) ``` +This method can optionally receive the `safeDeploymentConfig` parameter to define the `saltNonce`. + +```js +const safeAccountConfig: SafeAccountConfig = { + owners, + threshold, + to, // Optional + data, // Optional + fallbackHandler, // Optional + paymentToken, // Optional + payment, // Optional + paymentReceiver // Optional +} +const safeDeploymentConfig: SafeDeploymentConfig = { saltNonce } + +const safeSdk = await safeFactory.deploySafe(safeAccountConfig, safeDeploymentConfig) +``` + +## Safe Core SDK API Reference + +### create + +Returns an instance of the Safe Core SDK connected to the `safeAddress`. + +```js +import Safe from '@gnosis.pm/safe-core-sdk' + +const safeSdk = await Safe.create({ ethAdapter, safeAddress }) +``` + +* The property `isL1SafeMasterCopy` + + There are two versions of the Safe contracts: [GnosisSafe.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafe.sol) that does not trigger events in order to save gas and [GnosisSafeL2.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafeL2.sol) that does, which is more appropriate for L2 networks. + + By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the property `isL1SafeMasterCopy` to force the use of the `GnosisSafe.sol` contract. + + ```js + const safeSdk = await Safe.create({ ethAdapter, safeAddress, isL1SafeMasterCopy: true }) + ``` + +* The property `contractNetworks` + + If the Safe contracts are not deployed to your current network, the property `contractNetworks` will be required to point to the addresses of the Safe contracts previously deployed by you. + + ```js + import { ContractNetworksConfig } from '@gnosis.pm/safe-core-sdk' + + const id = await ethAdapter.getChainId() + const contractNetworks: ContractNetworksConfig = { + [id]: { + multiSendAddress: '', + safeMasterCopyAddress: '', + safeProxyFactoryAddress: '' + } + } + + const safeSdk = await Safe.create({ ethAdapter, safeAddress, contractNetworks }) + ``` + ### connect Returns a new instance of the Safe Core SDK connected to the `safeAddress`. @@ -192,18 +295,49 @@ Returns a new instance of the Safe Core SDK connected to the `safeAddress`. const safeSdk2 = await safeSdk.connect({ ethAdapter, safeAddress }) ``` -The property `contractNetworks` can be added to provide the Safe contract addresses in case the SDK is used in a network where the Safe contracts are not deployed. +* The property `isL1SafeMasterCopy` -```js -const contractNetworks: ContractNetworksConfig = { - [chainId]: { - multiSendAddress: '0x', - safeMasterCopyAddress: '0x', - safeProxyFactoryAddress: '0x' + There are two versions of the Safe contracts: [GnosisSafe.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafe.sol) that does not trigger events in order to save gas and [GnosisSafeL2.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafeL2.sol) that does, which is more appropriate for L2 networks. + + By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the property `isL1SafeMasterCopy` to force the use of the `GnosisSafe.sol` contract. + + ```js + const safeSdk = await Safe.connect({ ethAdapter, safeAddress, isL1SafeMasterCopy: true }) + ``` + +* The property `contractNetworks` + + If the Safe contracts are not deployed to your current network, the property `contractNetworks` will be required to point to the addresses of the Safe contracts previously deployed by you. + + ```js + const contractNetworks: ContractNetworksConfig = { + [chainId]: { + multiSendAddress: '', + safeMasterCopyAddress: '', + safeProxyFactoryAddress: '' + } } -} -const safeSdk = await Safe.connect({ ethAdapter, safeAddress, contractNetworks }) -``` + const safeSdk = await Safe.connect({ ethAdapter, safeAddress, contractNetworks }) + ``` + + + + + + + + + + + + + + + + + + + ### getAddress @@ -287,61 +421,81 @@ const isOwner = await safeSdk.isOwner(address) ### createTransaction -Returns a Safe transaction ready to be signed by the owners and executed. - -```js -const transaction: SafeTransactionDataPartial = { - to, - data, - value, - operation, // Optional - safeTxGas, // Optional - baseGas, // Optional - gasPrice, // Optional - gasToken, // Optional - refundReceiver, // Optional - nonce // Optional -} -const safeTransaction = await safeSdk.createTransaction(transaction) -``` +Returns a Safe transaction ready to be signed by the owners and executed. The Safe Core SDK supports the creation of single Safe transactions but also MultiSend transactions. -Batched transactions are allowed if more than one transaction are passed as an array of transactions. +* **Single transactions** -```js -const transactions: MetaTransactionData[] = [ - { - to, - data, - value, - operation // Optional - }, - // ... -] -const safeTransaction = await safeSdk.createTransaction(transactions) -``` + This method can take an object of type `SafeTransactionDataPartial` that represents the transaction we want to execute (once the signatures are collected). It accepts some optional properties as follows. -This method can also receive the `options` parameter to set the optional properties in the MultiSend transaction: + ```js + import { SafeTransactionDataPartial } from '@gnosis.pm/safe-core-sdk-types' -```js -const transactions: MetaTransactionData[] = [ - { + const transaction: SafeTransactionDataPartial = { to, data, value, - operation - }, - // ... -] -const options: SafeTransactionOptionalProps = { - safeTxGas, // Optional - baseGas, // Optional - gasPrice, // Optional - gasToken, // Optional - refundReceiver, // Optional - nonce // Optional -} -const safeTransaction = await safeSdk.createTransaction(transactions, options) -``` + operation, // Optional + safeTxGas, // Optional + baseGas, // Optional + gasPrice, // Optional + gasToken, // Optional + refundReceiver, // Optional + nonce // Optional + } + const safeTransaction = await safeSdk.createTransaction(transaction) + ``` + +* **MultiSend transactions** + + This method can take an array of `MetaTransactionData` objects that represent the multiple transactions we want to include in our MultiSend transaction. If we want to specify some of the optional properties in our MultiSend transaction, we can pass a second argument to the method `createTransaction` with the `SafeTransactionOptionalProps` object. + + ```js + const transactions: MetaTransactionData[] = [ + { + to, + data, + value, + operation // Optional + }, + { + to, + data, + value, + operation // Optional + }, + // ... + ] + const safeTransaction = await safeSdk.createTransaction(transactions) + ``` + + This method can also receive the `options` parameter to set the optional properties in the MultiSend transaction: + + ```js + const transactions: MetaTransactionData[] = [ + { + to, + data, + value, + operation // Optional + }, + { + to, + data, + value, + operation // Optional + }, + // ... + ] + const options: SafeTransactionOptionalProps = { + safeTxGas, // Optional + baseGas, // Optional + gasPrice, // Optional + gasToken, // Optional + refundReceiver, // Optional + nonce // Optional + } + const safeTransaction = await safeSdk.createTransaction(transactions, options) + ``` If the optional properties are not manually set, the Safe transaction returned will have the default value for each one: @@ -360,10 +514,10 @@ Read more about the [Safe transaction properties](https://docs.gnosis.io/safe/do Returns a Safe transaction ready to be signed by the owners that invalidates the pending Safe transaction/s with a specific nonce. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) const rejectionTransaction = await safeSdk.createRejectionTransaction(safeTransaction.data.nonce) ``` @@ -372,10 +526,10 @@ const rejectionTransaction = await safeSdk.createRejectionTransaction(safeTransa Returns the transaction hash of a Safe transaction. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) const txHash = await safeSdk.getTransactionHash(safeTransaction) ``` @@ -384,10 +538,10 @@ const txHash = await safeSdk.getTransactionHash(safeTransaction) Signs a hash using the current owner account. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) const txHash = await safeSdk.getTransactionHash(safeTransaction) const signature = await safeSdk.signTransactionHash(txHash) ``` @@ -397,10 +551,10 @@ const signature = await safeSdk.signTransactionHash(txHash) Adds the signature of the current owner to the Safe transaction object. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) await safeSdk.signTransaction(safeTransaction) ``` @@ -409,10 +563,10 @@ await safeSdk.signTransaction(safeTransaction) Approves a hash on-chain using the current owner account. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) const txHash = await safeSdk.getTransactionHash(safeTransaction) const txResponse = await safeSdk.approveTransactionHash(txHash) await txResponse.transactionResponse?.wait() @@ -423,10 +577,10 @@ await txResponse.transactionResponse?.wait() Returns a list of owners who have approved a specific Safe transaction. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) const txHash = await safeSdk.getTransactionHash(safeTransaction) const owners = await safeSdk.getOwnersWhoApprovedTx(txHash) ``` @@ -557,10 +711,10 @@ const safeTransaction = await safeSdk.getChangeThresholdTx(newThreshold, options Executes a Safe transaction. ```js -const transactions: SafeTransactionDataPartial[] = [{ +const transaction: SafeTransactionDataPartial = { // ... -}] -const safeTransaction = await safeSdk.createTransaction(...transactions) +} +const safeTransaction = await safeSdk.createTransaction(transaction) const txResponse = await safeSdk.executeTransaction(safeTransaction) await txResponse.transactionResponse?.wait() ``` From f88f8b9e76ab65416f62acae618fbd1dfabc49d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Nov 2021 17:47:45 +0100 Subject: [PATCH 3/5] Update Safe Ethers Adapter README file --- packages/safe-ethers-adapters/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/safe-ethers-adapters/README.md b/packages/safe-ethers-adapters/README.md index 82a2a972b..aa59fe913 100644 --- a/packages/safe-ethers-adapters/README.md +++ b/packages/safe-ethers-adapters/README.md @@ -2,7 +2,6 @@ [![NPM Version](https://badge.fury.io/js/%40gnosis.pm%2Fsafe-ethers-adapters.svg)](https://badge.fury.io/js/%40gnosis.pm%2Fsafe-ethers-adapters) [![GitHub](https://img.shields.io/github/license/gnosis/safe-core-sdk)](https://github.com/gnosis/safe-core-sdk/blob/main/LICENSE.md) -[![Coverage Status](https://coveralls.io/repos/github/gnosis/safe-core-sdk/badge.svg?branch=main)](https://coveralls.io/github/gnosis/safe-core-sdk?branch=main) [Ethers](https://docs.ethers.io/v5/single-page/) adapter that facilitates the interaction with the [Gnosis Safe Services](https://github.com/gnosis/safe-transaction-service) From 5093bef3ea0fbab963a1c52b860d0ccc3c4752a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Nov 2021 17:55:24 +0100 Subject: [PATCH 4/5] Update Safe Service Client doc and README file --- packages/safe-service-client/README.md | 44 ++++++++++++++----- .../src/SafeServiceClient.ts | 14 ++---- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/safe-service-client/README.md b/packages/safe-service-client/README.md index ff731a7d0..ce65a2f62 100644 --- a/packages/safe-service-client/README.md +++ b/packages/safe-service-client/README.md @@ -3,7 +3,6 @@ [![NPM Version](https://badge.fury.io/js/%40gnosis.pm%2Fsafe-service-client.svg)](https://badge.fury.io/js/%40gnosis.pm%2Fsafe-service-client) [![GitHub Release](https://img.shields.io/github/release/gnosis/safe-service-client.svg?style=flat)](https://github.com/gnosis/safe-service-client/releases) [![GitHub](https://img.shields.io/github/license/gnosis/safe-core-sdk)](https://github.com/gnosis/safe-core-sdk/blob/main/LICENSE.md) -[![Coverage Status](https://coveralls.io/repos/github/gnosis/safe-core-sdk/badge.svg?branch=main)](https://coveralls.io/github/gnosis/safe-core-sdk?branch=main) Software development kit that facilitates the interaction with the [Safe Transaction Service API](https://github.com/gnosis/safe-transaction-service). @@ -109,26 +108,37 @@ const delegates: SafeDelegateListResponse = await safeService.getSafeDelegates(s ### addSafeDelegate -Adds a new delegate for a given Safe address. The signature is calculated by signing this hash: `keccak(address + str(int(current_epoch / 3600)))`. +Adds a new delegate for a given Safe address. ```js -await safeService.addSafeDelegate(safeAddress, delegate) +const delegateConfig: SafeDelegateConfig = { + safe, + delegate, + label, + signer +} +await safeService.addSafeDelegate(delegateConfig) ``` ### removeAllSafeDelegates -Removes all delegates for a given Safe address. The signature is calculated by signing this hash: `keccak(address + str(int(current_epoch / 3600)))`. +Removes all delegates for a given Safe address. ```js -await safeService.removeAllSafeDelegates(safeAddress) +await safeService.removeAllSafeDelegates(safeAddress, signer) ``` ### removeSafeDelegate -Removes a delegate for a given Safe address. The signature is calculated by signing this hash: `keccak(address + str(int(current_epoch / 3600)))`. +Removes a delegate for a given Safe address. ```js -await safeService.removeSafeDelegate(safeAddress, delegate) +const delegateConfig: SafeDelegateDeleteConfig = { + safe, + delegate, + signer +} +await safeService.removeSafeDelegate(delegateConfig) ``` ### getSafeCreationInfo @@ -144,7 +154,10 @@ const safeCreationInfo: SafeCreationInfoResponse = await safeService.getSafeCrea Estimates the safeTxGas for a given Safe multi-signature transaction. ```js -const estimateTx: SafeMultisigTransactionEstimateResponse = await safeService.estimateSafeTransaction(safeAddress, safeTransaction) +const estimateTx: SafeMultisigTransactionEstimateResponse = await safeService.estimateSafeTransaction( + safeAddress, + safeTransaction +) ``` ### proposeTransaction @@ -152,7 +165,13 @@ const estimateTx: SafeMultisigTransactionEstimateResponse = await safeService.es Creates a new multi-signature transaction and stores it in the Safe Transaction Service. ```js -await safeService.proposeTransaction({ safeAddress, safeTransaction, safeTxHash, senderAddress }) +const transactionConfig: ProposeTransactionProps = { + safeAddress, + safeTransaction, + safeTxHash, + senderAddress +} +await safeService.proposeTransaction(transactionConfig) ``` ### getIncomingTransactions @@ -188,7 +207,10 @@ const pendingTxs: SafeMultisigTransactionListResponse = await safeService.getPen ``` ```js -const pendingTxs: SafeMultisigTransactionListResponse = await safeService.getPendingTransactions(safeAddress, currentNonce) +const pendingTxs: SafeMultisigTransactionListResponse = await safeService.getPendingTransactions( + safeAddress, + currentNonce +) ``` ### getNextNonce @@ -196,7 +218,7 @@ const pendingTxs: SafeMultisigTransactionListResponse = await safeService.getPen Returns the right nonce to propose a new transaction right after the last pending transaction. ```js -const nextNonce = await getNextNonce(safeAddress) +const nextNonce = await safeService.getNextNonce(safeAddress) ``` ### getBalances diff --git a/packages/safe-service-client/src/SafeServiceClient.ts b/packages/safe-service-client/src/SafeServiceClient.ts index 248ac8ce1..c22e62c83 100644 --- a/packages/safe-service-client/src/SafeServiceClient.ts +++ b/packages/safe-service-client/src/SafeServiceClient.ts @@ -203,7 +203,6 @@ class SafeServiceClient implements SafeTransactionService { /** * Adds a new delegate for a given Safe address. * - * @param safeAddress - The Safe address * @param delegateConfig - The configuration of the new delegate * @returns * @throws "Invalid Safe address" @@ -213,8 +212,7 @@ class SafeServiceClient implements SafeTransactionService { * @throws "Safe= does not exist or it's still not indexed" * @throws "Signing owner is not an owner of the Safe" */ - async addSafeDelegate(delegateConfig: SafeDelegateConfig): Promise { - const { safe, delegate, label, signer } = delegateConfig + async addSafeDelegate({ safe, delegate, label, signer }: SafeDelegateConfig): Promise { if (safe === '') { throw new Error('Invalid Safe address') } @@ -241,6 +239,7 @@ class SafeServiceClient implements SafeTransactionService { * Removes all delegates for a given Safe address. * * @param safeAddress - The Safe address + * @param signer - A Signer who is an owner of the Safe * @returns * @throws "Invalid Safe address" * @throws "Checksum address validation failed" @@ -264,7 +263,6 @@ class SafeServiceClient implements SafeTransactionService { /** * Removes a delegate for a given Safe address. * - * @param safeAddress - The Safe address * @param delegateConfig - The configuration for the delegate that will be removed * @returns * @throws "Invalid Safe address" @@ -273,8 +271,7 @@ class SafeServiceClient implements SafeTransactionService { * @throws "Signing owner is not an owner of the Safe" * @throws "Not found" */ - async removeSafeDelegate(delegateConfig: SafeDelegateDeleteConfig): Promise { - const { safe, delegate, signer } = delegateConfig + async removeSafeDelegate({ safe, delegate, signer }: SafeDelegateDeleteConfig): Promise { if (safe === '') { throw new Error('Invalid Safe address') } @@ -343,10 +340,7 @@ class SafeServiceClient implements SafeTransactionService { /** * Creates a new multi-signature transaction with its confirmations and stores it in the Safe Transaction Service. * - * @param safeAddress - The address of the Safe proposing the transaction - * @param transaction - The transaction that is proposed - * @param safeTxHash - The hash of the Safe transaction - * @param signature - The signature of an owner or delegate of the specified Safe + * @param proposeTransactionConfig - The configuration of the proposed transaction * @returns The hash of the Safe transaction proposed * @throws "Invalid Safe address" * @throws "Invalid safeTxHash" From 2fea9b11fbe600442e569970bf9bd974f187788e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Mon, 22 Nov 2021 13:22:53 +0100 Subject: [PATCH 5/5] Fix docs --- packages/guides/README.md | 3 +- .../guides/integrating-the-safe-core-sdk.md | 18 +++---- packages/safe-core-sdk/README.md | 52 +++++++++---------- .../src/SafeServiceClient.ts | 2 +- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/packages/guides/README.md b/packages/guides/README.md index a871da3c5..30c44c635 100644 --- a/packages/guides/README.md +++ b/packages/guides/README.md @@ -8,8 +8,7 @@ Read about the basics of Gnosis Safe and how it compares to other solutions [her The [Safe Core SDK](https://github.com/gnosis/safe-core-sdk) is a monorepo that contains software developer tools that allows interaction with the [Safe contracts](https://github.com/gnosis/safe-contracts) and the [Safe Transaction Service](https://github.com/gnosis/safe-transaction-service). -In this guide we will be using the following packages to deploy new Safes, create transactions, collect the signatures off-chain and execute those transactions: - +In this guide we will use the following packages to deploy new Safes, create transactions, collect off-chain signatures and execute transactions: * **safe-core-sdk-types** Contains the types that are shared among the different packages inside the monorepo. diff --git a/packages/guides/integrating-the-safe-core-sdk.md b/packages/guides/integrating-the-safe-core-sdk.md index 9399e2a0a..10fc2cfd2 100644 --- a/packages/guides/integrating-the-safe-core-sdk.md +++ b/packages/guides/integrating-the-safe-core-sdk.md @@ -12,7 +12,7 @@ 8. [Execute the transaction](#execute-transaction) 9. [Interface checks](#interface-checks) -## 1. Install the dependencies +## 1. Install the dependencies To integrate the [Safe Core SDK](https://github.com/gnosis/safe-core-sdk) into your Dapp or script you will need to install these dependencies: @@ -22,7 +22,7 @@ To integrate the [Safe Core SDK](https://github.com/gnosis/safe-core-sdk) into y @gnosis.pm/safe-service-client ``` -## 2. Initialize the SDK’s +## 2. Initialize the SDK’s ### Initialize the Safe Service Client @@ -117,7 +117,7 @@ const safeVersion = 'X.Y.Z' const safeFactory = await SafeFactory.create({ ethAdapter, safeVersion }) ``` -## 3. Deploy a new Safe +## 3. Deploy a new Safe The Safe Core SDK library allows the deployment of new Safes using the `safeFactory` instance we just created. @@ -136,7 +136,7 @@ const safeSdk = await safeFactory.deploySafe(safeAccountConfig) Calling the method `deploySafe` will deploy the desired Safe and return a Safe Core SDK initialized instance ready to be used. -## 4. Create a transaction +## 4. Create a transaction The Safe Core SDK supports the execution of single Safe transactions but also MultiSend transactions. We can create a transaction object by calling the method `createTransaction` in our `Safe` instance. @@ -206,7 +206,7 @@ We can specify the `nonce` of our Safe transaction as long as it is not lower th const nonce = await safeService.getNextNonce(safeAddress) ``` -## 5. Propose the transaction to the service +## 5. Propose the transaction to the service Once we have the Safe transaction object we can share it with the other owners of the Safe so they can sign it. To send the transaction to the Safe Transaction Service we need to call the method `proposeTransaction` from the Safe Service Client instance and pass an object with the properties: - `safeAddress`: The Safe address. @@ -225,7 +225,7 @@ await safeService.proposeTransaction({ }) ``` -## 6. Get the transaction from the service +## 6. Get the transaction from the service The transaction is then available on the Safe Transaction Service and the owners can retrieve it by finding it in the pending transaction list, or by getting its Safe transaction hash. @@ -286,7 +286,7 @@ type SafeMultisigTransactionResponse = { } ``` -## 7. Confirm/reject the transaction +## 7. Confirm/reject the transaction The owners of the Safe can now sign the transaction obtained from the Safe Transaction Service by calling the method `signTransactionHash` from the Safe Core SDK to generate the signature and by calling the method `confirmTransaction` from the Safe Service Client to add the signature to the service. @@ -298,7 +298,7 @@ let signature = await safeSdk.signTransactionHash(hash) await safeService.confirmTransaction(hash, signature.data) ``` -## 8. Execute the transaction +## 8. Execute the transaction Once there are enough confirmations in the service the transaction is ready to be executed. The account that will execute the transaction needs to retrieve it from the service with all the required signatures and call the method `executeTransaction` from the Safe Core SDK. @@ -331,7 +331,7 @@ const executeTxResponse = await safeSdk.executeTransaction(safeTransaction) const receipt = executeTxResponse.transactionResponse && (await executeTxResponse.transactionResponse.wait()) ``` -## 9. Interface checks +## 9. Interface checks During the process of collecting the signatures/executing transactions, some useful checks can be made in the interface to display or hide a button to confirm or execute the transaction depending on the current number of confirmations, the address of accounts that confirmed the transaction and the Safe threshold: diff --git a/packages/safe-core-sdk/README.md b/packages/safe-core-sdk/README.md index 1e731fd25..ccf08ab73 100644 --- a/packages/safe-core-sdk/README.md +++ b/packages/safe-core-sdk/README.md @@ -13,7 +13,7 @@ Software development kit that facilitates the interaction with the [Gnosis Safe * [Safe Factory API Reference](#factory-api) * [Safe Core SDK API Reference](#sdk-api) -## Installation +## Installation Install the package with yarn or npm: @@ -22,7 +22,7 @@ yarn install npm install ``` -## Build +## Build Build the package with yarn or npm: @@ -31,9 +31,9 @@ yarn build npm build ``` -## Getting Started +## Getting Started -The following steps show how to set up the Safe Core SDK, deploy a new Safe, create a Safe transaction, generate the required signatures from its owners and execute the transaction. However, using the Safe Core SDK alone will not allow to collect the signatures of the owners off-chain. To do this and be able to see and confirm the pending transactions showed in the [Gnosis Safe Web App](https://gnosis-safe.io/app/), it is recommended that you follow this other [guide](/packages/guides/integrating-the-safe-core-sdk.md) that covers the use of the Safe Core SDK combined with the Safe Service Client. +The following steps show how to set up the Safe Core SDK, deploy a new Safe, create a Safe transaction, generate the required signatures from owners and execute the transaction. However, using the Safe Core SDK alone will not allow for the collection of owner signatures off-chain. To do this and be able to see and confirm the pending transactions shown in the [Gnosis Safe Web App](https://gnosis-safe.io/app/), it is recommended that you follow this other [guide](/packages/guides/integrating-the-safe-core-sdk.md) that covers the use of the Safe Core SDK, combined with the Safe Service Client. ### 1. Set up the SDK using `Ethers` or `Web3` @@ -71,7 +71,7 @@ The following steps show how to set up the Safe Core SDK, deploy a new Safe, cre ### 2. Deploy a new Safe -To deploy a new Safe account instantiate the `SafeFactory` class and call the method `deploySafe` with the right params to configure the new Safe. This includes defining the list of owners and the threshold of the Safe. A Safe account with three owners and threshold equal three will be used as the starting point for this example but any Safe configuration is valid. +To deploy a new Safe account instantiate the `SafeFactory` class and call the `deploySafe` method with the right params to configure the new Safe. This includes defining the list of owners and the threshold of the Safe. A Safe account with three owners and threshold equal three will be used as the starting point for this example but any Safe configuration is valid. ```js import { Safe, SafeFactory, SafeAccountConfig } from '@gnosis.pm/safe-core-sdk' @@ -89,9 +89,9 @@ const safeAccountConfig: SafeAccountConfig = { const safeSdk: Safe = await safeFactory.deploySafe(safeAccountConfig) ``` -The method `deploySafe` executes a transaction from `owner1` account, deploys a new Safe and returns an instance of the Safe Core SDK connected to the new Safe. Check the method `deploySafe` in the [API Reference](#factory-api) for more details on additional configuration parameters. +The `deploySafe` method executes a transaction from the `owner1` account, deploys a new Safe and returns an instance of the Safe Core SDK connected to the new Safe. Check the `deploySafe` method in the [API Reference](#factory-api) for more details on additional configuration parameters. -Call the method `getAddress`, for example, to check the address of the newly deployed Safe. +Call the `getAddress` method, for example, to check the address of the newly deployed Safe. ```js const newSafeAddress = safeSdk.getAddress() @@ -105,7 +105,7 @@ import Safe from '@gnosis.pm/safe-core-sdk' const safeSdk: Safe = await Safe.create({ ethAdapter: ethAdapterOwner1, safeAddress }) ``` -Check the method `create` in the [API Reference](#sdk-api) for more details on additional configuration parameters. +Check the `create` method in the [API Reference](#sdk-api) for more details on additional configuration parameters. ### 3. Create a Safe transaction @@ -120,7 +120,7 @@ const transaction: SafeTransactionDataPartial = { const safeTransaction = await safeSdk.createTransaction(transaction) ``` -Check the method `createTransaction` in the [API Reference](#sdk-api) for additional details on creating MultiSend transactions. +Check the `createTransaction` method in the [API Reference](#sdk-api) for additional details on creating MultiSend transactions. Before executing this transaction, it must be signed by the owners and this can be done off-chain or on-chain. In this example `owner1` will sign it off-chain, `owner2` will sign it on-chain and `owner3` will execute it (the executor also signs the transaction transparently). @@ -159,7 +159,7 @@ await executeTxResponse.transactionResponse?.wait() All the signatures used to execute the transaction are now available at `safeTransaction.signatures`. -## Safe Factory API Reference +## Safe Factory API Reference ### create @@ -171,19 +171,19 @@ import { SafeFactory } from '@gnosis.pm/safe-core-sdk' const safeFactory = await SafeFactory.create({ ethAdapter }) ``` -* The property `isL1SafeMasterCopy` +* The `isL1SafeMasterCopy` flag There are two versions of the Safe contracts: [GnosisSafe.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafe.sol) that does not trigger events in order to save gas and [GnosisSafeL2.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafeL2.sol) that does, which is more appropriate for L2 networks. - By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the property `isL1SafeMasterCopy` to force the use of the `GnosisSafe.sol` contract. + By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the `isL1SafeMasterCopy` flag to force the use of the `GnosisSafe.sol` contract. ```js const safeFactory = await SafeFactory.create({ ethAdapter, isL1SafeMasterCopy: true }) ``` -* The property `contractNetworks` +* The `contractNetworks` property - If the Safe contracts are not deployed to your current network, the property `contractNetworks` will be required to point to the addresses of the Safe contracts previously deployed by you. + If the Safe contracts are not deployed to your current network, the `contractNetworks` property will be required to point to the addresses of the Safe contracts previously deployed by you. ```js import { ContractNetworksConfig } from '@gnosis.pm/safe-core-sdk' @@ -200,9 +200,9 @@ const safeFactory = await SafeFactory.create({ ethAdapter }) const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks }) ``` -* The property `safeVersion` +* The `safeVersion` property - The `SafeFactory` constructor also accepts the property `safeVersion` to specify the Safe contract version that will deploy. This string can take the values `1.1.1`, `1.2.0` or `1.3.0`. If not specified, the most recent contract version will be used by default. + The `SafeFactory` constructor also accepts the `safeVersion` property to specify the Safe contract version that will be deployed. This string can take the values `1.1.1`, `1.2.0` or `1.3.0`. If not specified, the most recent contract version will be used by default. ```js const safeVersion = 'X.Y.Z' @@ -246,7 +246,7 @@ const safeDeploymentConfig: SafeDeploymentConfig = { saltNonce } const safeSdk = await safeFactory.deploySafe(safeAccountConfig, safeDeploymentConfig) ``` -## Safe Core SDK API Reference +## Safe Core SDK API Reference ### create @@ -258,19 +258,19 @@ import Safe from '@gnosis.pm/safe-core-sdk' const safeSdk = await Safe.create({ ethAdapter, safeAddress }) ``` -* The property `isL1SafeMasterCopy` +* The `isL1SafeMasterCopy` flag There are two versions of the Safe contracts: [GnosisSafe.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafe.sol) that does not trigger events in order to save gas and [GnosisSafeL2.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafeL2.sol) that does, which is more appropriate for L2 networks. - By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the property `isL1SafeMasterCopy` to force the use of the `GnosisSafe.sol` contract. + By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the `isL1SafeMasterCopy` flag to force the use of the `GnosisSafe.sol` contract. ```js const safeSdk = await Safe.create({ ethAdapter, safeAddress, isL1SafeMasterCopy: true }) ``` -* The property `contractNetworks` +* The `contractNetworks` property - If the Safe contracts are not deployed to your current network, the property `contractNetworks` will be required to point to the addresses of the Safe contracts previously deployed by you. + If the Safe contracts are not deployed to your current network, the `contractNetworks` property will be required to point to the addresses of the Safe contracts previously deployed by you. ```js import { ContractNetworksConfig } from '@gnosis.pm/safe-core-sdk' @@ -295,19 +295,19 @@ Returns a new instance of the Safe Core SDK connected to the `safeAddress`. const safeSdk2 = await safeSdk.connect({ ethAdapter, safeAddress }) ``` -* The property `isL1SafeMasterCopy` +* The `isL1SafeMasterCopy` flag There are two versions of the Safe contracts: [GnosisSafe.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafe.sol) that does not trigger events in order to save gas and [GnosisSafeL2.sol](https://github.com/gnosis/safe-contracts/blob/v1.3.0/contracts/GnosisSafeL2.sol) that does, which is more appropriate for L2 networks. - By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the property `isL1SafeMasterCopy` to force the use of the `GnosisSafe.sol` contract. + By default `GnosisSafe.sol` will be only used on Ethereum Mainnet. For the rest of the networks where the Safe contracts are already deployed, the `GnosisSafeL2.sol` contract will be used unless you add the `isL1SafeMasterCopy` flag to force the use of the `GnosisSafe.sol` contract. ```js const safeSdk = await Safe.connect({ ethAdapter, safeAddress, isL1SafeMasterCopy: true }) ``` -* The property `contractNetworks` +* The `contractNetworks` property - If the Safe contracts are not deployed to your current network, the property `contractNetworks` will be required to point to the addresses of the Safe contracts previously deployed by you. + If the Safe contracts are not deployed to your current network, the `contractNetworks` property will be required to point to the addresses of the Safe contracts previously deployed by you. ```js const contractNetworks: ContractNetworksConfig = { @@ -447,7 +447,7 @@ Returns a Safe transaction ready to be signed by the owners and executed. The Sa * **MultiSend transactions** - This method can take an array of `MetaTransactionData` objects that represent the multiple transactions we want to include in our MultiSend transaction. If we want to specify some of the optional properties in our MultiSend transaction, we can pass a second argument to the method `createTransaction` with the `SafeTransactionOptionalProps` object. + This method can take an array of `MetaTransactionData` objects that represent the multiple transactions we want to include in our MultiSend transaction. If we want to specify some of the optional properties in our MultiSend transaction, we can pass a second argument to the `createTransaction` method with the `SafeTransactionOptionalProps` object. ```js const transactions: MetaTransactionData[] = [ diff --git a/packages/safe-service-client/src/SafeServiceClient.ts b/packages/safe-service-client/src/SafeServiceClient.ts index c22e62c83..26867bdc9 100644 --- a/packages/safe-service-client/src/SafeServiceClient.ts +++ b/packages/safe-service-client/src/SafeServiceClient.ts @@ -239,7 +239,7 @@ class SafeServiceClient implements SafeTransactionService { * Removes all delegates for a given Safe address. * * @param safeAddress - The Safe address - * @param signer - A Signer who is an owner of the Safe + * @param signer - A Signer that owns the Safe * @returns * @throws "Invalid Safe address" * @throws "Checksum address validation failed"