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

I can't connect wallet on chrome/safari mobile phones(IOS/Android) #3090

Closed
Temitope3665 opened this issue Jun 4, 2024 · 28 comments
Closed

Comments

@Temitope3665
Copy link

Describe the bug
I can connect the wallet to my project on my web but after mobile responsiveness was updated on the project, I could not connect the wallet on mobile phones.

To Reproduce
Steps to reproduce the behavior:

  1. Connect Wallet on the web version, it works well
  2. Connect Wallet on the smart phone is not connecting
  3. Perform an action that requires the user to connect to that app.
  4. See error

Expected behavior
I should be able to connect to my wallet on my smartphones exactly how it worked well on the web.

Screenshots
Attached screenshot

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

@Liubov-crypto
Copy link
Collaborator

Hello!
I checked your issue and did not find any connection problems.

  1. I was able to connect to DEX using native app on iphone on in-app browser
  2. I was able to connect to DEX opened in Safari using native app on iphone
  3. I was able to connect to DEX using web wallet on iphone, both were opened in Safari

Device: iphone 13
OS: ios 17.5.1
browser: Safari

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jun 18, 2024

Hello, @Temitope3665.
Can you give us some more details of your problem?

  1. Which version of aepp-sdk your project is using?
  2. Are you trying to connect to a wallet via deeplink?
  3. Are you having trouble connecting to the native app or the web version on mobile devices?

If it is possible maybe share the link to your project.

@Temitope3665
Copy link
Author

  1. "@aeternity/aepp-sdk": "^13.2.2"
    
  2. I tried using deeplink but whenever I want to make a transaction, I keep getting I'm not connected to the wallet.

  3. Could you share the code that works generally instead here? or
    Below is the function that I'm using to connect wallet, if you can help update it with the correct, I would be happy.

export const connectWallet = async ({
setConnectingToWallet,
setEnableIFrameWallet,
setUser,
address,
setConnectionError,
setOpenModal,
isHome,
walletObj = { info: { name: '', type: '' } },
aeSdk,
}: ConnectWalletParams) => {
setConnectingToWallet(true);
let addressDeepLink: any;

if ((IS_MOBILE || isSafariBrowser()) && !IN_FRAME) {
if (address) {
setConnectingToWallet(false);
return;
}
if (isHome) {
const domainName =
typeof window !== 'undefined' && window.location.origin;
const dashboardURL = ${domainName}/${DASHBOARD_URL}/;
addressDeepLink = createDeepLinkUrl({
type: 'address',
'x-success': ${ dashboardURL.split('?')[0] }?address={address}&networkId={networkId},
'x-cancel': dashboardURL.split('?')[0],
});
} else {
addressDeepLink = createDeepLinkUrl({
type: 'address',
'x-success': ${ window.location.href.split('?')[0] }?address={address}&networkId={networkId},
'x-cancel': window.location.href.split('?')[0],
});
}
if (typeof window !== 'undefined') {
window.location.replace(addressDeepLink);
}
} else {
try {
await resolveWithTimeout(30000, async () => {
const webWalletTimeout = IS_MOBILE
? 0
: setTimeout(() => setEnableIFrameWallet(true), 15000);

    let resolve: any = null;
    let rejected = (e: any) => {
      throw e;
    };
    let stopScan: any = null;

    const connectWallet = async (wallet: any) => {
      try {
        const { networkId } = await aeSdk.connectToWallet(
          wallet.getConnection()
        );
        const ret = await aeSdk.subscribeAddress('subscribe', 'connected');
        const {
          address: { current },
        } = ret;
        const currentAccountAddress = Object.keys(current)[0];
        if (!currentAccountAddress) return;
        stopScan?.();
        const user = { address: currentAccountAddress, isConnected: true };
        setUser(user);
        aeSdks = aeSdk;
        // localStorage.setItem('user', JSON.stringify(user));
        resolve?.(currentAccountAddress);
        setOpenModal(false);
      } catch (e: any) {
        if (!(e instanceof RpcRejectedByUserError)) {
          alert('error occured');
        }
        if (e.message === 'Operation rejected by user') {
          toast.error(e.message);
          resolve = null;
          rejected = (e: any) => {
            throw e;
          };
          stopScan = null;
        }
        console.log(e.message);
        rejected(e);
      }
    };
    if (walletObj.getConnection) {
      await connectWallet(walletObj);
    } else {
      const handleWallet = async ({ wallets }: any) => {
        const detectedWalletObject = Object.values(wallets).find(
          (wallet: any) => wallet.info.name === walletObj.info.name
        );
        if (!detectedWalletObject) return;
        clearInterval(webWalletTimeout);
        await connectWallet(detectedWalletObject);
      };
      const scannerConnection = new BrowserWindowMessageConnection();
      stopScan = walletDetector(scannerConnection, handleWallet);

      await new Promise((_resolve: any, _rejected: any) => {
        resolve = _resolve;
        rejected = _rejected;
      });
    }
  });
} catch (error) {
  if (walletObj.info.name === 'Superhero') {
    setConnectionError({
      type: 'denied',
      message:
        'Login with your wallet has failed. Please make sure that you are logged into your wallet.',
    });
  } else {
    setConnectionError({
      type: 'timeout',
      message: `Connection to ${walletObj.info.name} has been timeout, please try again later.`,
    });
  }
}

}
};

@Temitope3665
Copy link
Author

`export const connectWallet = async ({
setConnectingToWallet,
setEnableIFrameWallet,
setUser,
address,
setConnectionError,
setOpenModal,
isHome,
walletObj = { info: { name: '', type: '' } },
aeSdk,
}: ConnectWalletParams) => {
setConnectingToWallet(true);
let addressDeepLink: any;

if ((IS_MOBILE || isSafariBrowser()) && !IN_FRAME) {
if (address) {
setConnectingToWallet(false);
return;
}
if (isHome) {
const domainName =
typeof window !== 'undefined' && window.location.origin;
const dashboardURL = ${domainName}/${DASHBOARD_URL}/;
addressDeepLink = createDeepLinkUrl({
type: 'address',
'x-success': ${ dashboardURL.split('?')[0] }?address={address}&networkId={networkId},
'x-cancel': dashboardURL.split('?')[0],
});
} else {
addressDeepLink = createDeepLinkUrl({
type: 'address',
'x-success': ${ window.location.href.split('?')[0] }?address={address}&networkId={networkId},
'x-cancel': window.location.href.split('?')[0],
});
}
if (typeof window !== 'undefined') {
window.location.replace(addressDeepLink);
}
} else {
try {
await resolveWithTimeout(30000, async () => {
const webWalletTimeout = IS_MOBILE
? 0
: setTimeout(() => setEnableIFrameWallet(true), 15000);

    let resolve: any = null;
    let rejected = (e: any) => {
      throw e;
    };
    let stopScan: any = null;

    const connectWallet = async (wallet: any) => {
      try {
        const { networkId } = await aeSdk.connectToWallet(
          wallet.getConnection()
        );
        const ret = await aeSdk.subscribeAddress('subscribe', 'connected');
        const {
          address: { current },
        } = ret;
        const currentAccountAddress = Object.keys(current)[0];
        if (!currentAccountAddress) return;
        stopScan?.();
        const user = { address: currentAccountAddress, isConnected: true };
        setUser(user);
        aeSdks = aeSdk;
        // localStorage.setItem('user', JSON.stringify(user));
        resolve?.(currentAccountAddress);
        setOpenModal(false);
      } catch (e: any) {
        if (!(e instanceof RpcRejectedByUserError)) {
          alert('error occured');
        }
        if (e.message === 'Operation rejected by user') {
          toast.error(e.message);
          resolve = null;
          rejected = (e: any) => {
            throw e;
          };
          stopScan = null;
        }
        console.log(e.message);
        rejected(e);
      }
    };
    if (walletObj.getConnection) {
      await connectWallet(walletObj);
    } else {
      const handleWallet = async ({ wallets }: any) => {
        const detectedWalletObject = Object.values(wallets).find(
          (wallet: any) => wallet.info.name === walletObj.info.name
        );
        if (!detectedWalletObject) return;
        clearInterval(webWalletTimeout);
        await connectWallet(detectedWalletObject);
      };
      const scannerConnection = new BrowserWindowMessageConnection();
      stopScan = walletDetector(scannerConnection, handleWallet);

      await new Promise((_resolve: any, _rejected: any) => {
        resolve = _resolve;
        rejected = _rejected;
      });
    }
  });
} catch (error) {
  if (walletObj.info.name === 'Superhero') {
    setConnectionError({
      type: 'denied',
      message:
        'Login with your wallet has failed. Please make sure that you are logged into your wallet.',
    });
  } else {
    setConnectionError({
      type: 'timeout',
      message: `Connection to ${walletObj.info.name} has been timeout, please try again later.`,
    });
  }
}

}
};`

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jun 18, 2024

I think the problem might be in the sdk version. Version 13.3.1 containing an invisible breaking change. Since the version of the protocol for mainnet node was raised to 7.0.0 and the previous sdk is not supporting it.
https://github.com/aeternity/aepp-sdk-js/releases/tag/v13.3.1

@Temitope3665
Copy link
Author

What fix would you suggest?

@CedrikNikita
Copy link
Collaborator

The potential fixes are listed in the aepp-sdk release description. Our wallet currently using version version 13.3.1.

@Temitope3665
Copy link
Author

Temitope3665 commented Jun 18, 2024

Alright, I will check out the release description.

@CedrikNikita
Copy link
Collaborator

Hi, @Temitope3665. Were you able to resolve your issue?

@AroyewonTemitope
Copy link

No. It has not been resolved yet

@CedrikNikita
Copy link
Collaborator

Hi, @Temitope3665. I have few more clarification questions:

  1. Are you redirected back to your site after deeplink is approved in superhero wallet?
    • If no, can you share the deeplink your code is generating?
    • if yes, does your code process the received address?

@AroyewonTemitope
Copy link

Yes, it's connecting successfully, and here is the deep link it's generating below
Deeplink URL result

Attached below is the response during the connection.
PHOTO-2024-07-04-07-54-57

But the issue here is that, whenever we want to make a transaction or initiate an activity with the wallet, it keeps saying, wallet not connected. Here is a sample of how the error
IMG_3669

@CedrikNikita
Copy link
Collaborator

In order to sign/send transaction on mobile, you are to create a new deeplink with the transaction details as such.
#2679 (comment)
Currently we don't have any docs regarding deeplink creation. It seems that we need to create such (judging from this issue).

@AroyewonTemitope
Copy link

Alright. I will check the thread.
That should help with the issue.

@AroyewonTemitope
Copy link

Could you help with how best to go about it especially when interacting with contract?
We're building a DAO system, by interacting with the contract, I mean creating a dao, creating a proposal, etc...

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jul 12, 2024

This is a small example.

const createDeepLinkUrl = ({ type, callbackUrl, ...params }) => {
  const url = new URL(`https;//wallet.superhero.com/${type}`);
  if (callbackUrl) {
    url.searchParams.set('x-success', callbackUrl);
    url.searchParams.set('x-cancel', callbackUrl);
  }
  Object.entries(params)
    .filter(([, value]) => ![undefined, null].includes(value))
    .forEach(([name, value]) => url.searchParams.set(name, value));
  return url;
};

const contract = await aeSdk.initializeContract(
  {
    aci,
    address,
  },
);
const result = await contract[method](
  ...methodArgs,
  {
    callStatic: true,
    // Instead of `onAccount` property you can use `replace-caller` flag in the deep link
    onAccount: createOnAccountObject(address),
  },
);

const encodedTx = result.rawTx;

const currentUrl = new URL(window.location.href);
// reset url
currentUrl.searchParams.delete('transaction');
currentUrl.searchParams.delete('transaction-status');

// append transaction parameter for success case
const successUrl = new URL(currentUrl.href);
successUrl.searchParams.set('transaction', '{transaction}');

// append transaction parameter for failed case
const cancelUrl = new URL(currentUrl.href);
cancelUrl.searchParams.set('transaction-status', 'cancelled');

return createDeepLinkUrl({
  type: 'sign-transaction',
  transaction: encodedTx,
  networkId,
  // decode these urls because they will be encoded again
  'x-success': decodeURI(successUrl.href),
  'x-cancel': decodeURI(cancelUrl.href),
});

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jul 16, 2024

@AroyewonTemitope
Copy link

Thank you Cedrink. I was able to do it just like you suggested. Here is the code below. But I keep getting an error relating to pubkey in my console. Could you help me if I'm doing anything wrong or requires an update?

Screenshot 2024-07-16 at 07 24 05

export const createDeepLinkUrl = ({ type, callbackUrl, ...params }: DeepLinkParams): URL => { const url = new URL(${process.env.NEXT_PUBLIC_WALLET_URL}/${type}`);

if (callbackUrl) {
url.searchParams.set('x-success', callbackUrl);
url.searchParams.set('x-cancel', callbackUrl);
}

Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null) // Filter out undefined and null values
.forEach(([name, value]) => url.searchParams.set(name, value as string)); // Assert value as string

return url;
};

const generateKey: any = () => {
const secretKey = CryptoJS.lib.WordArray.random(64).toString();
return secretKey;
};

const node = new Node('https://testnet.aeternity.io'); // ideally host your own node
const account = new MemoryAccount(generateKey());
const newUserAccount = MemoryAccount.generate();

const aeSdk: any = new AeSdk({
nodes: [{ name: 'testnet', instance: node }],
accounts: [account, newUserAccount],
// onCompiler: compiler, // remove if step #2 skipped
});

const createDeepLinkUrl2 = async ({ type, callbackUrl, ...params }: any) => {
const url = new URL(${process.env.NEXT_PUBLIC_WALLET_URL}/${type});
if (callbackUrl) {
url.searchParams.set('x-success', callbackUrl);
url.searchParams.set('x-cancel', callbackUrl);
}

Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null) // Filter out undefined and null values
.forEach(([name, value]) => url.searchParams.set(name, value as string)); // Assert value as string
// Object.entries(params)
// .filter(([, value]) => ![undefined, null].includes(value))
// .forEach(([name, value]) => url.searchParams.set(name, value));
return url;
};

const createOnAccountObject = () => {
const accountObject = aeSdk.addresses()[0];
return accountObject;
};

export const contractInterract = async (payload: any) => {
const contract = await aeSdk.initializeContract({
aci: basicDAOAci,
address: payload.daoContractAddress,
});

const result = await contract['createProposal'](
payload.proposalType,
payload.description,
payload.value,
payload.target,
payload.info,
{
callStatic: true,
onAccount: createOnAccountObject(),
}
);

const encodedTx = result.rawTx;

const currentUrl = new URL(window.location.href);
// reset url
currentUrl.searchParams.delete('transaction');
currentUrl.searchParams.delete('transaction-status');

// append transaction parameter for success case
const successUrl = new URL(currentUrl.href);
successUrl.searchParams.set('transaction', '{transaction}');

// append transaction parameter for failed case
const cancelUrl = new URL(currentUrl.href);
cancelUrl.searchParams.set('transaction-status', 'cancelled');

return createDeepLinkUrl2({
type: 'sign-transaction',
transaction: encodedTx,
networkId: 'ae_uat',
// decode these urls because they will be encoded again
'x-success': decodeURI(successUrl.href),
'x-cancel': decodeURI(cancelUrl.href),
});
};
`

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jul 16, 2024

This is the example of createOnAccountObject function. I think this place might be the reason of the issue. And in general you can take a look at the the dex-ui implementation of the whole connection feature.
https://github.com/aeternity/dex-ui/blob/main/src/lib/utils.js#L80

@AroyewonTemitope
Copy link

Awesome!.
Thank you for this.
But I noticed a few things after I confirmed, which are;

  1. I couldn't find the transaction in my history
  2. The newly created proposal was not added to the expected list of proposals the contract returns when I get all contracts.

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jul 16, 2024

You should also set flag broadcast to true in deep link creation if you want wallet to push it to the node.

@AroyewonTemitope
Copy link

Yeah. That solves the problem.
Thank you.
I have a question again, I noticed when redirecting to the success page, that it disconnected the wallet. Is there a way that can be prevented?

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Jul 16, 2024

You can store user's address in localStorage.

@AroyewonTemitope
Copy link

Yes. That's perfect.
Thank you

@CedrikNikita
Copy link
Collaborator

I think that's problem solved, now that we have a deeplink scheme and an actual example of creating a such in JS. Feel free to open the issue back up if there are any unresolved issues or additional questions.

@Temitope3665
Copy link
Author

@CedrikNikita I want to switch the network from our app, although it's switched successfully on our app but, it does not switch on super hero wallet.
How can we go about it?

@CedrikNikita
Copy link
Collaborator

CedrikNikita commented Sep 9, 2024

Hi. This is not possible currently. If the user is connected to other dapps in case wallet will change the network for one of them it will change the network for each. It can be frustrated to the users.
The only supported wallet api methods are:
https://github.com/aeternity/aepp-sdk-js/blob/c8fbffebab588389f088f9a836de457d4d8afa00/src/aepp-wallet-communication/rpc/types.ts#L34-L95
You can explain to the user how to change the network in the wallet manually. If this is not what you need, and it is an important feature for you, please post the use case.
You can also propose to have a such api method in the sdk issues https://github.com/aeternity/aepp-sdk-js/issues.

@AroyewonTemitope
Copy link

Alright. Thank you.
I will do that.

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

4 participants