-
Notifications
You must be signed in to change notification settings - Fork 25
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
Ledger Live App #655
Ledger Live App #655
Conversation
This hook will check if there is an `isEmbed` query param in the page url. If it is, then it will save that value in local storage. We will use that param in our manifest.json file for our Ledger Live App.
For Ledger Live App we only need tBTC page.
Since we only have tbtc section in our Ledger Live App the Sidebar is not needed, so we hide it if the `isEmbed` flag is set to true.
The `LedgerLiveAppManager` will be a core for all the logic used in the LedgerLive app. It will contain two separate managers for ethereum and bitcoin transactions. For ethereum manager we also have to create a separate LedgerLiveApp signer that implements Signer class from Ethers. It is needed so that we can pass it to our new tbtc-v2 SDK and use it to sign transactions and communicatin with the contracts. This way we won't have to change anything in the SDK just for the LedgerLive implementation and we keep it clean. In this commit only ethereum manager is created. In the future we should also create bitcoinManager and put it here, which allow the user to send bitcoins to a specific bitcoin address.
The Threshold Ledger Live App will show only tbtc flow so it makes sense to put the `ledgerLiveAppManager` inside our `threshold-ts` lib in `TBTC` class. Besides, we will use the signer from that class and pass it to our tbtc-v2 SDK.
As of now it will only allow to use goerli accounts. Left TODOs around the places to not forget to fix that. I've created `useRequestEthereumAccount` which actually works similar to `useRequestAccount` from `@ledgerhq/wallet-api-client-react`, but it uses our ledgerLiveManager under the hood. This way it will save the account in our signer, which then will be used in our tbtc-v2 SDK to interact with contracts. Since we save our connected address in redux store we will now display it when the address is connected instead of getting the `account` property from `useWeb3React` hook.
Minting flow was not displaying for connected users in Ledger Live app. I've fixed it by checking if the address is present in redux store. Since we save it for normal wallets and also for wallets in ledger live app, it will work for both.
Assign eth address from the redux store to the provide data form. It will be an address that will be used to generate deposit address.
We don't want to allow the account to be changed outside of the ledgerLive etherumManager so we are making the `account` property inside `LedgerLiveEthereum` signer private. The account should be changed by using `connectAccount` method in our manager.
This class will manage all bitcoin transactions/message signing inside the Ledger Live app. Besides I've also renamed `EthereumManager` to `LedgerLiveAppEthereumManager` to make it more specific.
`LedgerLiveEthereumSigner` -> `LedgerLiveAppEthereumSigner`
Makes two separate requestAccount hook for bitcoin and for ethereum networks.
We want to connect bitcoin account at step 2 of the minting flow. For this case I've created a separate button that allows the user to connect a bitcoin account inside ledger live app. After that he will be able to send the bitcoins form the account he had choosen to a deposit address that he just generated.
Preview uploaded to https://preview.dashboard.test.threshold.network/ledger-live-app/index.html. |
I've decided to rewamp the whole implementation as it seems that we don't really need to keep everything in ledgerLiveAppManager. The idea now is to keep only LedgerLiveAppEthereumSigner (which in the future will be placed in tbtc-v2 SDK) for integrating with contracts, and create a context for ledger live app where we will keep connected ethereum and bitcoin addresses. The LedgerLiveAppEthereumSigner will use `@ledgerhq/wallet-api-client` package, and for the rest things (connecting ethereum wallet, connecting bitcoin wallet, sending bitcoins to address), we will use `wallet-connect-client-react` lib. The first step is to install the `wallet-connect-client-react`
Adds TransportProvider needed for `wallet-api-client-react`. This will allow us to use the hooks from the libs (like `useRequestAccount` etc.).
This context is where we will store our eth and btc addresses for ledger live app. Why we need it? We have to detect when the address is changed in our LedgerLiveApp and then update our signer inside threshold lib in `ThresholdContext`. For website we could just detect it with our `useWeb3React` hook, but in this case it is not that simple. The only way in the previous implementation was to get the actual address from the redux store, but this required to move `<ReduxProvider>` above the `<ThresholdProvider`, which might broke the app in some places. That's why I've decided to create a separate context foe Ledger Live App and keep all data about connected accounts there. We will put the context above `ThresholdContext` so we can use it there and later we will make sure to save the account there each time we use `useRequestAccount` hook from `wallet-api-client-react`.
This is probably final step (or one of the final steps) in this rewamp. There are few bigger changes in this commit: 1. Remove `LedgerLiveAppManager` completely We won't need that class anymore. The only thing we need is `LedgerLiveAppEthereumSigner` to interact with eth contracts. The rest thigns (like connecting wallets, and sending bitcoins) will be handled with hooks from ledger's wallet-api for react. 2. Store `LedgerLiveAppEthereumSigner` instance in our `TBTC` class in `thershold-ts` lib. We also remove the need to pass anything related to ledger live app through `ThresholdConfig`, like we did with `LedgerLiveAppManager`. The Ledger Live App Signer will be created inside the class if necessary. 3. Refactor `useRequestEthereumAccount` and `useRequestBitcoinAccount` Those hooks will now use `useRequestAccount` hook from `@ledgerht/wallet-api-client-react`, but additionaly the `requestAccount` method will save the bitcoin/ethereum account in the LedgerLiveContext and ethereum account in the LedgerLiveAppEthereumSigner (through TBTC method).
Preview uploaded to https://preview.dashboard.test.threshold.network/ledger-live-app/index.html. |
This hook will work both in website view and inside the Ledger Live App. If `isEmbed` flag is set to false it will return the values based on the `useWeb3React` hook. If it's true the returned values will be based on `LedgerLiveApp` context.
Preview uploaded to https://preview.dashboard.test.threshold.network/ledger-live-app/index.html. |
For this we've added `Continue` button in Step 1 - it is visible only if the app is embed. The `useIsActive` hook is used to get the proper account base on the `isEmbed` feature flag.
For both bitcoin and ethereum hooks we can just save the account address immidietely to the LedgerLiveApp Context - we don't have to check if the address is undefined or not in the if statements like before. Additionally we can also save the eth account for the LedgerLiveAppEthereumSigner using one-liner. I've also remove saving bitcoin address to the LedgerLiveApp signer. It was a mistake. Only eth addresses should be saved there, since it is a signer for ethereum chain.
I've decided to store Account object (from ledger's wallet-api) in Ledger Live App Context, instead of just account address. This will be helpful when sending a bitcoin transaction, because it need the account id, that is stored in Account object.
Creates `useSendBitcoinTransaction` hook that use `useSignAndBroadcast` hook from `ledgerhq/wallet-api-client-react` under the hood. This will send a specific amount of bitcoins to a specific address.
Preview uploaded to https://preview.dashboard.test.threshold.network/ledger-live-app/index.html. |
Unfortunately we need `bignumber.js` library in our dApp to work with ledger's wallet-api. Unfortunately, because we already use `BigNumber` lib from `ethers` for our calcuulations. The commit changes only `package.json` because `yarn.lock` already contains `bignumber.js` registry, since it's a dependency of wallet-api.
Extending `LedgerLiveAppEthereumSigner` from `Signer` class from `ethers` lib did not work as expected when passing it to tbtc-v2 SDK. The SDK did not recognize that it was an instance of Signer. What works is changing the way we import `Signer` class - instead of doing it from `ethers` we now do it from `@ethersproject/abstract-signer`.
I believe this is how our dApp works right now on the website too. When you refresh the page you are not automatically connected to the wallet again. I don't remember why we've changed it, but I think there were some bugs that were happening and we implemented it as a workaround 🤔 I think this could be a separate issue - to implement an automatic wallet connection with the previously used account once the page is refreshed @lukasz-zimnoch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall it looks good to me. ✨Only small non-blocking comments.
However, it would be good if @r-czajkowski also took a look at this solution to make sure that this PR doesn't break anything. He knows the app better. Then I think we will be ready for the merge.
const _providerOrSigner = | ||
providerOrSigner instanceof LedgerLiveEthereumSigner | ||
? providerOrSigner | ||
: (getProviderOrSigner(providerOrSigner as any, account) as any) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking comment
This logic is duplicated in
https://github.com/threshold-network/token-dashboard/blob/ledger-live-app/src/web3/hooks/useGetBlock.ts#L14-L19
I would suggest wrapping it in one method.
To this point we detected if the application is embed in Ledger Live App by checking if it had `?embed=true` query parameter in the url and then saving it it local storage. It was problematic because the query parameter would have to be in the url only once to apply that change, so refreshing the page would not reset that state (which is good for ledger live app, but bad is someone pass that query parameter on the website - he would have to remove it manually from the local storage). To resolve this I've created `useDetectIfEmbed` hook that detects if application is Embed based on the `?embed=true` query parameter and saves that information to local storage (under `isEmbed` property). Besides that it will also set `?embed=true` query parameter for every url if the `isEmbed` property is set to true in local storage. This will persist the embed flag when the user refreshes the page unless he removes the `?embed=true` query parameter from the url. This will produce the result we desire - it will persist the embed mode for ledger live app and remove it on the website once the query parameter is not provided in the url.
The param is needed only in "url".
Preview uploaded to https://preview.dashboard.test.threshold.network/ledger-live-app/index.html. |
Let's keep it the same as it is on `main` branch.
Extracts `SendBitcoinsToDepositAddressForm` to a separate file to make the `MakeDeposit` file cleaner.
Preview uploaded to https://preview.dashboard.test.threshold.network/ledger-live-app/index.html. |
…fect-bug Fix Ledger Live use effect bug Fix bug that was introduced in #655. The `useRequestEthereumAccount` hook that supposed to be used only in embed mode, was firing it's `useEffect` in the website where it set the empty account in the redux state. This prevented some functionality in the dApp to work properly. For example the Operator Mapping Card was not displayed when user connected the account on the Staking page. The fix is adding `isEmbed` check for all `useEffects` related to ledger live app.
Ledger Live App
Closes: #649
Blocked by: #654This PR allows to run our dApp as Live App withing Ledger Live. The Live Apps are displayed in the Discover section of Ledger Live on Desktop (Windows, Mac, Linux) and mobile (Android and iOS).
The main purpose of it would be to complete the whole Mint & Unmint flow, without the need to leave the Ledger Live application and do a bitcoin transaction to generated deposit address. All transactions are done within the application.
Overall Description
When running as Ledger Live App, our Token Dashboard is embedded into it and displayet differently than in the website. We are checking that with our
?embed=true
query parameter, that I've put in the manifest file. Only tbtc section is needed for this, so that's why onli this section is displayed and the rest are hidden.The user can connect his ethereum account from Ledger to communicate with eth contracts. He can also choose which of his bitcoin addresses he wants to use to send the bitcoins from.
Technical Details
Overview
The code was written based on the Ledger Live App documentations. As you can see there are two sections in the documentation: DApp and Non-DApp - both describe two different ways of embedding an application into the Ledger Live Discover section. A first natural choice in our case would be the
DApp
section, since our website is a Dapp. Unfortunately, that is not the case, because from my experience and research it looks like it was not possible to do a bitcoin transaction there. This is why we choose the second option, which allows to use Wallet-API. With the help of this API we are able to do bitcoin and eth transactions, and also interact with eth contracts.The Wallet-API also has two sections in the docs: Core-API and React-API, that uses Core-API under the hood. In our case we actually use both: React-API for connecting the eth/btc accounts and sending bitcoin transactions from one account to another (in our case to deposit address) and Core-Api to interact with eth contracts. Why?
The answer is that using only React-API would require us to reorganize tBTC v2 SDK just for the Ledger Live App functionality. The API for reacts needs raw data format of the ethereum transaction when we interact with the contract, and that can be obtained using populateTransaction method from
ethers
lib, but we are not returning it in such form in our SDK. This is why we've decided to create a separate signer for this purpose - to avoid doing any changes in the SDK just for that feature and to not unnecessarily extend SDK responsibility.Ledger Live Ethereum Signer (wallet-api-core)
TBTC v2 SDK allows us to pass signer when initiating it. The signer must extend the
Signer
class fromethers
lib and this is exactly what our Ledger Live Ethereum Signer do. It useswallet-api-core
lib under the hood. The signer was placed in tbtc-v2 repoYou can see a more detailed description of that signer, its purpose and explanation of how it works in keep-network/tbtc-v2#743.
In our dApp we are requesting an eth account using
wallet-api-core-react
(see the subsection below) and then pass the account to the signer usingsetAccount
method.Connecting wallets and doing simple transactions (wallet-api-core-react)
The Ledger Live Ethereum Signer is used to integrate with eth contracts, but what about connecting the account to our dApp and sending some tokens from one account to another? This is where we use
wallet-api-core-react
and it's hooks.In our dApp we have three custom hooks that use hooks from
wallet-api-core-react
under the hood:useRequestBitcoinAccount
,useRequestEthereumAccount
,useSendBitcoinTransaction
.The first two are pretty similar to the original ones (from the lib), but I've had to write a wrapper to it so that I can connect and disconnect
walletApiReactTransport
there. This is needed because our Ledger Live Ethereum Signer uses different instance of the transport there, so if we won't disconnect one or another, ano ongoing request
error might occur. Based on the dosc the transport should be disconnected when we are done to ensure the communication is properly closed.The third one,
useSendBitcoinTransaction
, is used to create a bitcoin transaction in a proper format that is required bywallet-api-core-react
. The format for our bitcoin transaction looks like this:Fields:
family
(string): The cryptocurrency family to which the transaction belongs. This could be 'ethereum', 'bitcoin', etc.amount
(BigNumber): The amount of cryptocurrency to be sent in the transaction, represented in the smallest unit of the currency. For instance, in Bitcoin, an amount of 1 represents 0.00000001 BTC.recipient
(string): The address of the recipient of the transaction.nonce
(number, optional): This is the number of transactions sent from the sender's address.data
(Buffer, optional): Input data of the transaction. This is often used for contract interactions.gasPrice
(BigNumber, optional): The price per gas in wei.gasLimit
(BigNumber, optional): The maximum amount of gas provided for the transaction.maxPriorityFeePerGas
(BigNumber, optional): Maximum fee per gas to be paid for a transaction to be included in a block.maxFeePerGas
(BigNumber, optional): Maximum fee per gas willing to be paid for a transaction.Source: https://wallet.api.live.ledger.com/appendix/transaction
In our case, for our bitcoin transaction, we only need
family
,amount
andrecipient
. We only use that to send bitcoins to deposit address, so we will use the deposit address as arecipient
here.Finally, to execute the transaction, we just pass the transaction object and id of the connected bitcoin account to
useSignAndBroadcastTransaction
hook.LedgerLiveAppContext
Connecting account in Ledger Live App is quite different than our actual one in the website. Normally, we use
web3react
for that, but in this case we need to useuseRequestAccount
hook formwallet-api-client-react
. Because of that we need to store those accounts somewhere in our dApp, so I decided to create aLedgerLiveAppContext
for that.The context contain 5 properties:
As you can see we have
ethAccount
andbtcAccount
to store the connected accounts there. We can also set those account usingsetEthAccount
andsetBtcAccount
methods, after we request it using our hook. TheledgerLiveAppEthereumSigner
is an additional property that contains our signer for Ledger Live App. This way we will be able to set the account also in the signer.useIsEmbed
hookLike I said earlier, we use
isEmbed
query parameter to determine if the dApp is used in Ledger Live or not. I've created anuseIsEmbed
hook that saves that query parameter to local storage and the use it to detect if we should use all the functionalities for Ledger Live App or not.useDetectEmbed
hookAdditional to above, I've created
useDetectEmbed
hook, that detects if application is Embed based on the?embed=true
query parameter. Besides that, it also adds?embed=true
query parameter to every url in the page.This is needed because with only
useIsEmbed
hook, the?embed=true
flag was needed only in home page (https://dashboard.threshold.network?embed=true
) which then was saved to local storage and the url was changed tohttps://dashboard.threshold.network/tBTC/mint
. This worked well for Ledger Live, because we still used embed state when we refreshed the page, but was problematic for website, because adding?embed=true
parameter to the url would broke the page and required the user to manually remove theisEmbed
from local storage.With
useDetectEmbed
hook every url will have?embed=true
query parameter, if theisEmbed
flag in local storage is set to true, meaning that adding the query param once to the url will be enough to keep it in every url. After refreshing the page it will be checked if the parameter is in the url, and then we will set the correct value in local storage. This means, that if user use?embed=true
flag in the website, he would have to just remove that query string from the url to fix it. On the Ledger Live it will still persist the embed mode once the user refreshes the page.useIsActive
hookThis is also a new hook here. His main purpose is to determine if, and what account is active. Up to this point we've used
useWeb3React
hook for that purpose, but in this case it won't work. So, under the hook, theuseIsActive
returns similar values touseWeb3React
hook if the app is not embed, but if it is, then we return proper values based on theLedgerLiveAppContext
.How it works with
threshold-ts
libI've actually manage to not do any changes in our
threshold-ts
lib. The way it works now is that when theisEmbed
flag is set to true, we pass the Ledger Live Ethereum Signer as aproviderOrSigner
property.This required me to change
getContract
andgetBlock
method though, so that they return the proper values when ttheproviderOrSigner
is and instance ofLedgerLiveEthereumSigner
.Read More
How To Test
Note: Right now it can't be tested on Ledger Live, because it does not support Sepolia. Please see last point in TODO list at the bottom of this description.
Steps to Run it in as Ledger Live App:
Add a local app
row and clickBrowse
Open
To Do List
Task list:
isEmbed
flag,tbtc-v2.ts
main
into this branch to apply changes from Support deployment on Sepolia #605 (note: Unfortunately as of know Ledger Live App does not support Sepolia)In the future: