Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: Safe.create is not a function #419

Closed
ipatka opened this issue Apr 26, 2023 · 10 comments
Closed

TypeError: Safe.create is not a function #419

ipatka opened this issue Apr 26, 2023 · 10 comments
Assignees

Comments

@ipatka
Copy link

ipatka commented Apr 26, 2023

Description

When importing the Safe class to connect with an existing Safe, it does not import the right module. Instead importing the rest of the module.

Environment

  • Safe Core SDK version: latest
  • Safe contract version:
  • Environment:
    • non-browser

Steps to reproduce

import 'dotenv/config'
import { Wallet, ethers } from 'ethers'

import Safe from '@safe-global/protocol-kit'
import { EthersAdapter } from '@safe-global/protocol-kit'
import { SafeTransactionDataPartial } from '@safe-global/safe-core-sdk-types'

const safeAddress = 'REDACTED'

const send = async () => {
    const pk = process.env.PRIVATE_KEY_1
    if (!pk) throw new Error('could not load key')

    const web3Provider = new ethers.providers.InfuraProvider('goerli', `REDACTED`)
    const signer = new Wallet(pk, web3Provider)

    const ethAdapter = new EthersAdapter({
        ethers,
        signerOrProvider: signer,
    })
    console.log({Safe})
    const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress})
    const safeTransactionData: SafeTransactionDataPartial = {
        to: '0x<address>',
        value: ethers.utils.formatUnits('0.2', 'ether'),
        data: '0x',
    }
    const safeTransaction = await safeSdk.createTransaction({ safeTransactionData })
    console.log({ safeTransaction })

}

send()

Expected result

Safe connection works

Additional context

Maybe there's a name collision in src/index.ts? export default Safe doesn't seem to work. The exports on lines 57-103 are getting imported.

Possibly related
#411

@ipatka
Copy link
Author

ipatka commented Apr 26, 2023

Current workaround:

import SafeProtocol from '@safe-global/protocol-kit'

    const Safe = SafeProtocol.default
    const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress})

typescript is not happy but it runs 🤷‍♂️

@royalaid
Copy link

Thanks for posting, no idea what is going on internally to require this hack but it worked for me

@dasanra
Copy link
Collaborator

dasanra commented Aug 28, 2023

Seems related to this
#243

@yagopv yagopv self-assigned this Aug 28, 2023
@yagopv
Copy link
Member

yagopv commented Aug 28, 2023

Hey @ipatka. I have some questions:

  1. Are you running this in a node environment (no browser) ?
  2. Could you provide your typescript compilation settings and package.json ?
  3. Are you compiling to esm instead cjs ?

Thanks!

@mmv08
Copy link
Member

mmv08 commented Aug 28, 2023

Hey @ipatka. I have some questions:

  1. Are you running this in a node environment (no browser) ?
  2. Could you provide your typescript compilation settings and package.json ?
  3. Are you compiling to esm instead cjs ?

Thanks!

It can be reproduced in node when using ESM

@yagopv
Copy link
Member

yagopv commented Aug 28, 2023

We are compiling to commonjs the safe-core-sdk project so guess the problem is here (node esm project outside the browser trying to import cjs). We probably need to do a dual compilation setup to cjs and esm detecting the usage of the type module.

But when creating a simple node project to test i have an error even before (Safe is not a constructor). For this reason i want to know the exact configuration @ipatka is using with TS

@yagopv
Copy link
Member

yagopv commented Aug 29, 2023

For now when using safe-core-sdk outside the browser (node) with ESM Modules the proposed hack works. We opened an issue for changing the way we are building the safe-core-sdk and avoid this kind of hacks

@yagopv yagopv closed this as completed Aug 29, 2023
@ipatka
Copy link
Author

ipatka commented Aug 29, 2023

We are compiling to commonjs the safe-core-sdk project so guess the problem is here (node esm project outside the browser trying to import cjs). We probably need to do a dual compilation setup to cjs and esm detecting the usage of the type module.

But when creating a simple node project to test i have an error even before (Safe is not a constructor). For this reason i want to know the exact configuration @ipatka is using with TS

Sounds good thanks for addressing! Here's my setup if it helps:

tsconfig.json

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": false,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "outDir": "dist",
    "rootDir": "."
  },
  "include": [
    "./scripts"
  ]
}

selected package.json

  "devDependencies": {
    "@types/node": "^18.13.0",
    "bob-ts": "^4.0.0",
    "bob-tsm": "^1.1.1",
    "ts-node": "^10.8.1",
    "typescript": "^4.7.4"
  },
  "dependencies": {
    "@safe-global/api-kit": "^1.0.1",
    "@safe-global/protocol-kit": "^0.1.1",
    "@safe-global/safe-core-sdk-types": "^1.9.1",
    ...
  }

Example script

import 'dotenv/config'
import { Wallet, ethers } from 'ethers'

import SafeProtocol from '@safe-global/protocol-kit'
import SafeProtocolApiKit from '@safe-global/api-kit'
import { EthersAdapter } from '@safe-global/protocol-kit'
import { SafeTransactionDataPartial } from '@safe-global/safe-core-sdk-types'

const send = async () => {
   const pk = process.env.PRIVATE_KEY_1
   if (!pk) throw new Error('could not load key')

   const web3Provider = new ethers.providers.InfuraProvider('goerli', `KEY`)
   const signer = new Wallet(pk, web3Provider)

   const ethAdapter = new EthersAdapter({
       ethers,
       signerOrProvider: signer,
   })
   const Safe = SafeProtocol.default
   const SafeApiKit = SafeProtocolApiKit.default

   const safeSdk: SafeProtocol = await Safe.create({ ethAdapter, safeAddress })

   const safeService = new SafeApiKit({
       txServiceUrl: 'https://safe-transaction-goerli.safe.global',
       ethAdapter,
   })
   const safeTransactionData: SafeTransactionDataPartial = {
       to: ethAdapterOwner1,
       value: ethers.utils.formatUnits(ethers.utils.parseEther('0.2'), 'wei'),
       data: '0x',
   }
   const safeTransaction = await safeSdk.createTransaction({ safeTransactionData })
   const safeTxHash = await safeSdk.getTransactionHash(safeTransaction)
   const signature = await safeSdk.signTransactionHash(safeTxHash)
   const signedTransaction = await safeSdk.signTransaction(safeTransaction)
   const senderAddress = await signer.getAddress()

   console.log({ safeTransaction, signedTransaction })

   await safeService.proposeTransaction({
       safeAddress,
       safeTransactionData: safeTransaction.data,
       safeTxHash,
       senderAddress,
       senderSignature: signature.data,
   })
}

send()

@yagopv
Copy link
Member

yagopv commented Aug 29, 2023

Thanks!

@jmrossy
Copy link

jmrossy commented Apr 18, 2024

Seeing the same problem when my TSConfig is using module and/or moduleResolution node16 or nodenext

Thanks @ipatka for the workaround ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants