safe-global / safe-apps-sdk

Client-side SDKs to create third-party Safe Apps
https://safe.global
MIT License
259 stars 122 forks source link

Provide a sync way to check if an app is run inside gnosis safe #263

Open mmv08 opened 2 years ago

mmv08 commented 2 years ago

We got a request for easy detection if the app is loaded as a safe app that doesn't require loading the SDK - in the web version, one can use desktop.referrer, but not on the desktop. It could be done with the query params.

mmv08 commented 2 years ago

Suggestion from @taylorjdawson

You could add a data- attribute to the iframe element and then I believe dapps could access this to determine if they are in a gnosis safe context

kleinpetr commented 2 years ago

Since there is a requirement to auto-login with gnosis (if you want to release your app into gnosis apps) I would expect the straight-forward approach how to detect the gnosis provider availability. If I am trying to getInfo() from the safe, it is failing on net::ERR_BLOCKED_BY_CLIENT (probably cors), which you can't handle by try-catch statement. So what is the recommended approach for detecting it? Because I've inspected the React library (which I don't use), where it's used try-catch as well, so probably it has to fail

mmv08 commented 2 years ago

getInfo() uses .postMessage and not http, it cannot fail with a CORS error.

Could you share the code? The problem must be somewhere else

kleinpetr commented 2 years ago

Oh, srry. it is probably trouble with Sentry integration.... :/

kleinpetr commented 2 years ago

Ok, so the error is not related to the gnosis safe info. But when I am trying to get safe info, there is no error, but the app is just stuck.

mmv08 commented 2 years ago

Ok, so the error is not related to the gnosis safe info. But when I am trying to get safe info, there is no error, but the app is just stuck.

could you share the code?

mmv08 commented 2 years ago

I just checked, and other apps are working, so we'd need a reproduction case to check it.

kleinpetr commented 2 years ago

The code is very simple, before I mount the app I am waiting for this try-catch statement. And it looks that the Promise is never resolved.

try {
      const gnosisSdk = new SafeAppsSDK()
      const safeInfo = await gnosisSdk.safe.getInfo() 
      return safeInfo
    } catch (e) {
      console.log('Gnosis Safe is not detected', e)
    }
mmv08 commented 2 years ago

The code is very simple, before I mount the app I am waiting for this try-catch statement. And it looks that the Promise is never resolved.

try {
      const gnosisSdk = new SafeAppsSDK()
      const safeInfo = await gnosisSdk.safe.getInfo() 
      return safeInfo
    } catch (e) {
      console.log('Gnosis Safe is not detected', e)
    }

It's expected to never resolve if you run it outside the Safe because there's no handler for this request. You need to add a timeout. Check the example here:
https://github.com/gnosis/safe-apps-sdk/blob/master/packages/safe-apps-web3modal/src/modal.ts#L45-L54

kleinpetr commented 2 years ago

ok, thanks

gndelia commented 2 years ago

Hi, this feature would be useful for me: I work in an App that uses Gnosis, but 99% of our users don't use a multisig, so this is for very limited users. We also use web3js, but given we install @gnosis.pm/safe-apps-web3-react, it brings the SDK as a dependency, which also brings its own copy of ethers package, which is huge.

I understand that these packages are required, but I'd like to be able to know if I'm in Gnosis context so I can only load them in these scenarios, and not for all users. Thanks!

edit:

currently, to detect if I'm in Gnosis context, I use

const { connector: currentConnector } = useWeb3React()
if (currentConnector instanceof SafeAppConnector) {

but by importing SafeAppConnector I get the whole SDK as mentioned above

mmv08 commented 2 years ago

Hi, this feature would be useful for me: I work in an App that uses Gnosis, but 99% of our users don't use a multisig, so this is for very limited users. We also use web3js, but given we install @gnosis.pm/safe-apps-web3-react, it brings the SDK as a dependency, which also brings its own copy of ethers package, which is huge.

I understand that these packages are required, but I'd like to be able to know if I'm in Gnosis context so I can only load them in these scenarios, and not for all users. Thanks!

edit:

currently, to detect if I'm in Gnosis context, I use

const { connector: currentConnector } = useWeb3React()
if (currentConnector instanceof SafeAppConnector) {

but by importing SafeAppConnector I get the whole SDK as mentioned above

There's an async approach above that may help. Unfortunately, synchronous is not possible because browsers do not have such a mechanism

mmv08 commented 2 years ago

It could be done with the query params.

I've just got reminded that it's possible with query params @dasanra

gndelia commented 2 years ago

how's the query params approach?

The async approach still requires importing SafeAppsSDK, which I think still implies loading the entire SDK even for those users that are not in gnosis, which is the problem that I want to avoid in the first place

mmv08 commented 2 years ago

how's the query params approach?

The async approach still requires importing SafeAppsSDK, which I think still implies loading the entire SDK even for those users that are not in gnosis, which is the problem that I want to avoid in the first place

Query params approach isn't possible at the moment. If you import the SDK only when the app is loaded in an iframe, the chances are high that the iframe is coming from the safe

efstajas commented 1 year ago

Hey all!

Wondering if anyone has found a definitive solution to this since. We're having this exact problem now — we want to support being a Safe App in our frontend, but also have the same app support standalone usage.

The solution linked by @mikhailxyz works, but I really don't like the idea of delaying our app load by 200ms for everyone, especially given we expect the vast majority of sessions to not be within a safe.

I can also imagine making an environment variable that puts the app into "safe mode" and deploying our app twice — once with it on, once off (for the main deployment). But this just seems like a lot of effort.