{"id":21820170,"url":"https://github.com/5afe/safe-contracts-wrapper","last_synced_at":"2026-05-17T18:32:37.818Z","repository":{"id":62487589,"uuid":"529162261","full_name":"5afe/safe-contracts-wrapper","owner":"5afe","description":null,"archived":false,"fork":false,"pushed_at":"2023-08-21T14:48:35.000Z","size":1528,"stargazers_count":0,"open_issues_count":3,"forks_count":2,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-01-26T07:23:44.430Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/5afe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-26T07:38:17.000Z","updated_at":"2023-02-01T20:33:11.000Z","dependencies_parsed_at":"2025-01-26T07:33:32.566Z","dependency_job_id":null,"html_url":"https://github.com/5afe/safe-contracts-wrapper","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/5afe%2Fsafe-contracts-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/5afe%2Fsafe-contracts-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/5afe%2Fsafe-contracts-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/5afe%2Fsafe-contracts-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/5afe","download_url":"https://codeload.github.com/5afe/safe-contracts-wrapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244782486,"owners_count":20509824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-27T16:29:00.550Z","updated_at":"2026-05-17T18:32:32.800Z","avatar_url":"https://github.com/5afe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Polywrap Gnosis Safe Wrapper\n\n## Table of contents\n* [Installation](#installation)\n* [Getting Started](#getting-started)\n* [Safe Factory Wrapper Reference](#safe-factory-wrapper)\n* [Safe Wrapper Reference](#safe-wrapper)\n## \u003ca name=\"installation\"\u003eInstallation\u003c/a\u003e\n\nInstall the package with yarn or npm:\n\n```bash\nyarn add polywrap\nnpm install polywrap\n```\n\n## \u003ca name=\"getting-started\"\u003eGetting Started\u003c/a\u003e\n\nThe following steps show how to set up the Polywrap Client, deploy a new Safe, create a Safe transaction, generate the required signatures from owners and execute the transaction. However, using the Polywrap Client 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 [Safe Web App](https://gnosis-safe.io/app/), it is recommended that you follow this other [guide](/guides/integrating-the-safe-core-sdk.md) that covers the use of the Safe Core SDK, combined with the Safe Service Client.\n\n### 1. Instantiate a Polywrap Client\n\nFirst of all, we need to create a `Polywrap Client`, 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.io/v5/) Ethereum libraries.\n\nUsage#\nUse an import or require statement, depending on which your environment supports.\n\n```js\nimport { PolywrapClient } from \"@polywrap/client-js\";\n```\n\nThen, you will be able to use the PolywrapClient like so:\n```js\n// Simply instantiate the PolywrapClient.\nconst client = new PolywrapClient();\n```\n[Polywrap client docs](https://docs.polywrap.io/reference/clients/js/client-js)\n\n### 2. Deploy a new Safe\n\nTo deploy a new Safe account invoke `deploySafe` method of `safe-factory-wrapper` 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.\n\n```js\nconst result = await client.invoke({\n  uri: 'ens/safe.factory.eth',\n  method: \"deploySafe\",\n  args: {\n    safeAccountConfig: {\n      owners: [\u003cowner1\u003e, \u003cowner2\u003e, \u003cowner3\u003e ],\n      threshold: 1,\n    }\n  }\n});\n```\n\nThe `deploySafe` method executes a transaction from the your current ethereum account, deploys a new Safe and returns a Safe Address. Make sure to save this address as you will need it to interact with `safe-wrapper`\n\n### 3. Create a Safe transaction\n\nTo create a transaction you can invoke `createTransaction` method of `safe-wrapper`. Make sure you provided environment param `safeAddress` to your call.\n\n```js\n\nconst safeTransactionData = {\n  to: '0x\u003caddress\u003e',\n  value: '\u003ceth_value_in_wei\u003e',\n  data: '0x\u003cdata\u003e'\n}\n\nconst safeTransaction = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: \"createTransaction\",\n  args: {\n      tx: safeTransactionData,\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  }\n});\n```\n\nCheck the `createTransaction` method in the [Wrapper Reference](#wrapper-api) for additional details on creating MultiSend transactions.\n\nBefore 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).\n\n\n### 3.a. Off-chain signatures\n\nThe `owner1` account signs the transaction off-chain.\n\n```js\nconst signedSafeTransaction = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: \"addSignature\",\n  args: {\n      tx: safeTransactionData,\n    },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n});\n```\n\nBecause the signature is off-chain, there is no interaction with the contract and the signature becomes available at `signedSafeTransaction.signatures`.\n\n### 3.b. On-chain signatures\n\nTo sign transaction on-chain `owner2` should instantiate new PolywrapClient connected to ethereum ([Ethereum-plugin-config](#ethereum-plugin-config)). After `owner2` account is connected to the ethereum-plugin as a signer the transaction hash will be approved on-chain.\n\n```js\n// Get transaction hash\nconst txHash = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: \"getTransactionHash\",\n  args: {\n      tx: signedSafeTransaction.data,\n    },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n});\n\n// Approve\nawait client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: \"approveTransactionHash\",\n  args: {\n      hash: txHash,\n    },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n});\n```\n\n### 4. Transaction execution\n\nLastly, `owner3` account is connected to the client as a signer and executor of the Safe transaction to execute it.\n\n```js\nconst executeTxResponse = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: \"executeTransaction\",\n  args: {\n      tx: signedTransaction,\n    },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n});\n```\n\n\n\n## \u003ca name=\"safe-factory-wrapper\"\u003eSafe Factory Wrapper Reference\u003c/a\u003e\n\n### deploySafe\n\nDeploys 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.\n\n```js\nconst safeAccountConfig = {\n  owners,\n  threshold,\n  to, // Optional\n  data, // Optional\n  fallbackHandler, // Optional\n  paymentToken, // Optional\n  payment, // Optional\n  paymentReceiver // Optional\n}\n\nconst safeSdk = await safeFactory.deploySafe({ safeAccountConfig })\n```\n\nThis method can optionally receive the `safeDeploymentConfig` parameter to define the `saltNonce`.\n\n```js\nconst safeAccountConfig = {\n  owners,\n  threshold,\n  to, // Optional\n  data, // Optional\n  fallbackHandler, // Optional\n  paymentToken, // Optional\n  payment, // Optional\n  paymentReceiver // Optional\n}\n\nconst safeDeploymentConfig = {\n  saltNonce,\n  isL1Safe, // Optional\n  version, // Optional\n}\n\nconst safeDeploymentResponse = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'deploySafe',\n  args: { \n    safeAccountConfig, \n    safeDeploymentConfig\n    },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n## \u003ca name=\"safe-wrapper\"\u003eSafe Wrapper Reference\u003c/a\u003e\n\n\n### getAddress\n\nReturns the address of the current SafeProxy contract.\n\n```js\nconst address = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getAddress',,\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getContractVersion\n\nReturns the Safe Master Copy contract version.\n\n```js\nconst version = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getContractVersion',,\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getOwners\n\nReturns the list of Safe owner accounts.\n\n```js\nconst owners = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getOwners',\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getNonce\n\nReturns the Safe nonce.\n\n```js\nconst nonce = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getNonce',\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getThreshold\n\nReturns the Safe threshold.\n\n```js\nconst threshold = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getThreshold',\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getChainId\n\nReturns the chainId of the connected network.\n\n```js\nconst chainId = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getChainId',\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getBalance\n\nReturns the ETH balance of the Safe.\n\n```js\nconst balance = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getBalance',\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### getModules\n\nReturns the list of addresses of all the enabled Safe modules.\n\n```js\nconst moduleAddresses = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'getModules',\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### isModuleEnabled\n\nChecks if a specific Safe module is enabled for the current Safe.\n\n```js\nconst isEnabled = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'isModuleEnabled',\n  args: {\n    moduleAddress: \u003caddress\u003e\n  },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### isOwner\n\nChecks if a specific address is an owner of the current Safe.\n\n```js\nconst isOwner = await client.invoke({\n  uri: 'ens/safe.wrapper.eth',\n  method: 'isOwner',\n  args: {\n    ownerAddress: \u003caddress\u003e\n  },\n  env: {\n    safeAddress: \u003cSAFE_ADDRESS\u003e\n  }\n})\n```\n\n### createTransaction\n\nReturns a Safe transaction ready to be signed by the owners and executed. The Safe Wrapper supports the creation of single Safe transactions but also MultiSend transactions.\n\n* **Single transactions**\n\n  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.\n\n  ```js\n\n  const safeTransactionData = {\n    to,\n    data,\n    value,\n    operation, // Optional\n    safeTxGas, // Optional\n    baseGas, // Optional\n    gasPrice, // Optional\n    gasToken, // Optional\n    refundReceiver, // Optional\n    nonce // Optional\n  }\n  \n  const safeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'createTransaction',\n    args: {\n      tx: safeTransactionData\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n  ```\n\n* **MultiSend transactions**\n\n  This method can take an array of `SafeTransactionDataPartial` 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 `createMultiSendTransaction` method with the `SafeTransactionOptionalProps` object.\n\n  ```js\n  const safeTransactionsData = [\n    {\n      to,\n      data,\n      value,\n      operation // Optional\n    },\n    {\n      to,\n      data,\n      value,\n      operation // Optional\n    },\n    // ...\n  ]\n  const safeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'createMultiSendTransaction',\n    args: {\n      txs: safeTransactionsData\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n    })\n  ```\n\n  This method can also receive the `options` parameter to set the optional properties in the MultiSend transaction:\n\n  ```js\n  const safeTransactionsData = [\n    {\n      to,\n      data,\n      value,\n      operation // Optional\n    },\n    {\n      to,\n      data,\n      value,\n      operation // Optional\n    },\n    // ...\n  ]\n  const options = {\n    safeTxGas, // Optional\n    baseGas, // Optional\n    gasPrice, // Optional\n    gasToken, // Optional\n    refundReceiver, // Optional\n    nonce // Optional\n  }\n    const safeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'createMultiSendTransaction',\n    args: {\n      txs: safeTransactionsData,\n      options\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n  ```\n\n  In addition, the optional `onlyCalls` parameter, which is `false` by default, allows to force the use of the `MultiSendCallOnly` instead of the `MultiSend` contract when sending a batch transaction:\n\n  ```js\n  const onlyCalls = true\n  const safeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'createMultiSendTransaction',\n    args: {\n      txs: safeTransactionsData,\n      options,\n      onlyCalls: onlyCalls\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n  ```\n\nIf the optional properties are not manually set, the Safe transaction returned will have the default value for each one:\n\n* `operation`: `OperationType.Call` (0) is the default value.\n* `safeTxGas`: The right gas estimation is the default value.\n* `baseGas`: 0 is the default value.\n* `gasPrice`: 0 is the default value.\n* `gasToken`: 0x address is the default value.\n* `refundReceiver`: 0x address is the default value.\n* `nonce`: The current Safe nonce is the default value.\n\nRead more about the [Safe transaction properties](https://docs.gnosis-safe.io/tutorials/tutorial_tx_service_initiate_sign).\n\n### getTransactionHash\n\nReturns the transaction hash of a Safe transaction.\n\n```js\nconst safeTransactionData: SafeTransactionDataPartial = {\n  // ...\n}\nconst safeTransaction =  await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'createTransaction',\n    args: {\n      tx: safeTransactionData\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n  \nconst txHash = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'getTransactionHash',\n    args: {\n      tx: safeTransaction\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n### signTransactionHash\n\nSigns a hash using the current owner account.\n\n```js\nconst signature = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'signTransactionHash',\n    args: {\n      hash: txHash\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n### signTypedData\n\nSigns a transaction according to the EIP-712 using the current signer account.\n\n```js\nconst signature = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'signTypedData',\n    args: {\n      tx: safeTransaction\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n### signTransaction\n\nReturns a new `SafeTransaction` object that includes the signature of the current owner. `eth_sign` will be used by default to generate the signature.\n\n```js\nconst signedSafeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'addSignature',\n    args: {\n      tx: safeTransaction\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\nOptionally, an additional parameter can be passed to specify a different way of signing:\n\n```js\nconst signedSafeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'addSignature',\n    args: {\n      tx: safeTransaction\n      signingMethod: 'eth_signTypedData'\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n```js\nconst signedSafeTransaction = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'addSignature',\n    args: {\n      tx: safeTransaction\n      signingMethod: 'eth_sign' // default option.\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n### approveTransactionHash\n\nApproves a hash on-chain using the current owner account.\n\n```js\nconst txResponse = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'approveTransactionHash',\n    args: {\n      hash: txHash\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\nOptionally, some properties can be passed as execution options:\n\n```js\nconst options = {\n  from, // Optional\n  gasLimit, // Optional\n  gasPrice, // Optional\n  maxFeePerGas, // Optional\n  maxPriorityFeePerGas // Optional\n  nonce // Optional\n}\n```\n```js\nconst txResponse = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'approveTransactionHash',\n    args: {\n      hash: txHash,\n      options: options\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n### getOwnersWhoApprovedTx\n\nReturns a list of owners who have approved a specific Safe transaction.\n\n```js\nconst ownerAddresses = await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'getOwnersWhoApprovedTx',\n    args: {\n      hash: txHash\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n### executeTransaction\n\nExecutes a Safe transaction.\n\n```js\nconst txResponse  await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'executeTransaction',\n    args: {\n      tx: signedSafeTransaction\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\nOptionally, some properties can be passed as execution options:\n\n```js\nconst options = {\n  from, // Optional\n  gasLimit, // Optional\n  gasPrice, // Optional\n  maxFeePerGas, // Optional\n  maxPriorityFeePerGas // Optional\n  nonce // Optional\n}\n```\n```js\nconst txResponse  await client.invoke({\n    uri: 'ens/safe.wrapper.eth',\n    method: 'executeTransaction',\n    args: {\n      tx: signedSafeTransaction,\n      options: options,\n    },\n    env: {\n      safeAddress: \u003cSAFE_ADDRESS\u003e\n    }\n  })\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F5afe%2Fsafe-contracts-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F5afe%2Fsafe-contracts-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F5afe%2Fsafe-contracts-wrapper/lists"}