In this article
Step 089 covered wallets from the holder's side. This is the builder's side: a mini app that wants to know which wallet somebody has, and sometimes to ask them to send something. The mechanism is a connection between two pieces of software that do not trust each other, and that mistrust is the whole design.
One mistake accounts for most of what goes wrong. An address your page hands to your server is a claim, not a fact. Anybody can type anybody's address, including a well-known one.
What you will be able to do after this step
- check_circle Tell a claimed address from a proved one.
- check_circle Separate connecting, signing and settling — three different events.
- check_circle Write a transaction the confirmation sheet can describe honestly.
- check_circle Handle the outcome that is neither success nor failure.
hub What a connection actually is
The wallet is separate software — another application on the phone, or something on a different device entirely. Your page cannot reach into it, and it cannot reach into your page, so something in between carries messages back and forth.
That intermediary is not trusted, and does not need to be. Everything that matters is either encrypted between the two ends or signed in a way your server can check independently. Which is why the design works, and why skipping the checking removes the entire point of it.
From the user's side the flow is short: your app asks, their wallet shows what is being asked, they approve or they do not. From your side it is three separate events that people run together and should not.
Connecting means you now know an address and can ask for things. Signing means they approved one specific request. Settling means the network accepted it, which is later and can fail after the wallet has already returned.
Your front end sends your server a wallet address. Can you treat that person as its owner?
Your page runs on somebody else's device and anything it sends you can be altered before it arrives. An address is public information, so claiming a famous one costs nothing and is the first thing anybody tries against an app that grants anything on the basis of a balance. The fix is standard and cheap: your server produces a one-time value, the wallet signs it, and you verify that signature against the address. Only then have you learned anything. If your application ever gives a benefit for holding something — access, a role, a reward — and skips this step, it is giving that benefit to whoever types the right string.
A user connects their wallet to your application. What have they authorised?
Connecting is introduction, not permission. What you gain is the ability to see a public address and to put requests in front of the wallet, each of which the person approves or refuses on its own. Two things follow. As a builder, do not design a flow that assumes a connected user can be charged silently — every action needs its own approval and its own handling for refusal. As a user, connecting is the low-risk step and approving is the one that matters, which is the distinction from Step 089 seen from the other direction. What connecting does reveal is your address and therefore its public history, which is a privacy consideration rather than a financial one.
The wallet returns successfully after the user approves a payment. Do you mark the order paid?
A successful return means the user approved and the transaction left the wallet. It can still fail — insufficient balance for the fee, an expired validity window, a rejected message — and it takes time to settle regardless. Treating the wallet's response as payment is how applications deliver goods for transactions that never landed. And the third option is worse than the first: there is no reversal, as Step 089 says, so an optimistic credit is a gift. The correct shape is a pending state, a check that the transaction actually appeared and carried the expected amount to the expected address, and fulfilment only then — with the same protection against handling it twice that Step 094 requires of any payment.
fact_check The confirmation sheet is the only honest surface
Everything the user saw before that moment was drawn by you. The sheet the wallet renders is the one thing you did not write, and it is where a careful person actually reads what is about to happen.
So write transactions that sheet can describe well. A destination they can recognise, an amount that matches what your page said, and a human-readable note attached to the transfer rather than an opaque blob. An application whose confirmation screen shows an unfamiliar address and no explanation is asking people to approve on faith, and the careful ones will not.
The user-facing half of this is the rule from Step 089: read the amount and the destination on the sheet rather than on the page that produced it. Building so that both halves agree is not only good manners, it is the difference between an approval rate that reflects intent and one that reflects trust in your design.
alt_route Design for the outcome that is neither
Most wallet integrations handle success and failure and fall apart on the third case, which is the common one: nothing came back.
The person switched to the wallet and got distracted. They closed it. They read the sheet and decided to think about it. The phone locked, a call arrived, the connection dropped. From your application's point of view the request was sent and no answer exists, possibly forever.
Three things follow. Never leave a permanent spinner — time out into a state that explains itself and offers to try again. Never treat a timeout as a refusal, because the transaction may still be in flight and a retry can produce two. And make the recovery path reconciliation rather than repetition: check whether the expected transaction arrived before offering to send another one.
The same discipline covers the mini app surface itself. Step 088's warning applies directly here — a panel can be swiped away mid-flow, so anything a user has invested effort in should survive being closed and reopened.
touch_app Try it now — five minutes
Four steps. The second one is the test that separates a working integration from a demonstration.
- Check whether your app verifies possession Find the place where an address becomes a logged-in user. If there is no signature check between the two, that is today's work.
- Approve a transaction and then kill the wallet app Watch what your interface does. If it waits forever or declares failure, both are wrong.
- Read your own confirmation sheet as a stranger Is the destination recognisable? Does the amount match your page? Is there a note explaining what this is?
- Try the flow with a wallet holding tokens but no network currency This is the most common real-world stuck state, and most applications say nothing useful about it.
visibility_off What you are joining together
One consequence of building this deserves its own paragraph, because it is easy to create without noticing.
A mini app already knows the Telegram account that opened it (Step 088). Add a wallet connection and you now hold a link between a person's account and an address whose entire history is public. Anybody with your records can look up everything that address has ever done, and anybody with the address can do the same in the other direction.
That may be exactly what your application requires. It is still a record that did not exist before you made it, held by you, and valuable to somebody else. The ordinary discipline from Step 093 applies with more force than usual: store the minimum, keep it for as long as you need it and not longer, and be deliberate about who on your side can read it.
And tell people. An application that says plainly what it records is doing nothing unusual; one that quietly builds the mapping and mentions it nowhere has made a decision on the user's behalf that they would probably have been fine with, if asked.
help Common questions at this stage
Do I need my own server, or can this be entirely in the page?
You need a server for anything that grants something. The verification in the first quiz item has to happen somewhere the user cannot modify, and a page running on their device is not that place. A purely front-end integration is fine for displaying a balance or letting somebody send a transfer to an address they chose — the moment your application decides that this person is entitled to something, that decision belongs on your side.
How long should a login challenge be valid?
Long enough for a person to open their wallet, read, and approve — a few minutes rather than a few seconds — and short enough that an intercepted one is useless later. It must also be single-use and stored on your side, because the purpose is to prove that this signature was made now for you, and a value the client supplies proves nothing at all. If you find yourself extending it because users keep missing the window, the problem is usually a confusing flow rather than the timeout.
Should I support more than one wallet application?
The standard connection mechanisms are designed so that you support an interface rather than a product, which is the point — you list the wallets that implement it and the user picks. What differs in practice is how each one renders your confirmation, so test with more than one before deciding your transaction is clearly described. The one that shows your note nicely may not be the one most of your users have.
My users complain their transfer failed for no reason.
Check whether they hold any of the network's own currency. Moving anything costs a small fee paid in it, so a wallet containing only tokens cannot send those tokens — a genuinely confusing state that looks like a bug and is not. A good application detects this before the attempt and says so in plain words. A great one says how much is needed, since "top up your wallet" is not actionable advice to somebody who does not know how much.
Can I test without spending real money?
Yes — there is a test network, wallets can be pointed at it, and you should do all of your development there. What matters more is which paths you test: the success path works within an hour, and the ones that cost you customers are refusal, timeout, insufficient fee, an expired request and a duplicate submission. Write those five down and try each one deliberately, because none of them will happen by accident while you are building.
Is any of this appropriate for a general audience?
Be honest about who your users are. Everything in Step 089 applies to every person who connects a wallet to your application: irreversibility, custody of a recovery phrase, and a category of scam that specifically targets people doing exactly what you are asking them to do. Building a flow that requires a wallet excludes most people and exposes the rest. If the value of your application does not genuinely depend on it, the in-app payments from Step 094 are simpler for you and safer for them.
At a glance
- 1Claimed vs proved
Ask the wallet to sign a challenge before trusting an address.
- 2Three events
Connecting, signing and settling happen at different moments.
- 3Neither yes nor no
Plan for no answer: no endless spinner, no automatic retry.
Updated