useEffect(() => {constf=async () => {constc=awaitClient.init({ logger:'debug', relayUrl:'wss://relay.walletconnect.com', projectId: 'yourProjectID', // register at WalletConnect and create one for yourself - https://cloud.walletconnect.com/
// you need to have a valid ID or the app will not start metadata: { name:"My Stacks WalletConnect App", description:"Awesome application", url:"https://your_app_url.com/", icons: ["https://avatars.githubusercontent.com/u/37784886"], }, });setClient(c); }if (client ===undefined) {f(); }}, [client]);
Establish a Wallet Connect Session
We have the Client ready, now we need to connect our app to our wallet - establish a Session.
We do that by providing selected Network's chainID to Wallet Connect client.
ChainID is in a form of a string containing network name and a number, ex. foo:123. The Stacks chain ID is defined on ChainAgnostic.
In case of Stacks, the chain IDs you're interested in are:
Handle connection, edit the handleConnect function:
Reset any previously set chains with:
setChain(undefined);
Prior to connecting our app with the wallet, we need to know a couple of things:
what are the supported methods?
what are the supported events?
what are the supported chains?
Chains are simple - it's either Stacks or Bip122 for Bitcoin, so we simply pass the selected chain (full chainID). Events and methods are wallet dependent.
Our dummy wallet doesn't support events and supports only 4 example methods for Stacks and 1 method for Bitcoin:
stacks_signMessage - for signing a message with wallet users private key
stacks_stxTransfer - simple STX transfer
stacks_contractCall - contract call (with post conditions)
At this point, when you click connect - you should be presented with Wallet Connect QR Code modal.
If you scan the QR code with Xverse wallet, you will see a prompt to approve the connection request.
If you scan the QR code with the Xverse wallet, it'll show the list of methods we've passed. Approve it and you'll see the connect buttons go away - indicating there's nothing to connect to anymore.
Show available methods depending on the selected chain, add this to src/App.js render method:
We'll cover each handler individually building up complexity.
You can read more about available Stacks transactions here.
A little theory:
Wallet Connect acts as RPC layer between our App and the Wallet.
The Wallet defines RPC protocol (methods, events, etc.) and performs all of the operations.
The App is just a client and simply sends some requests.
So - everything we do is RPC-like request that goes through "RPC client" like this:
constrequest= { method:'stacks_signMessage',//here you provide the method you want to use params: { //and here you pass the arguments pubkey: address, message,//this is our custom payload },};awaitclient.request({ chainId: chain,//ex. stacks:1 topic:session.topic,//taken from session object request,});
The request itself is heavily dependent on what Wallet requires from us!
We'll also need an address and we can get it from the session like this:
Another thing is Wallet Connect as a communication layer has some limitations we have to deal with - it uses JSON serialization internally. Because of this, ClarityValues that utilize bigint will not work out of the box.
For that reason we need a hack to allow bigint to be serialized as JSON. Add this to your src/App.js (after imports):
/* global BigInt */BigInt.prototype.toJSON=function() { returnthis.toString() }
The API strives to be as similar to the native Stacks API as possible. So looking into Stacks Connect should give you a good idea on how to create any other calls yourself.
Structured Message Signing:
In order to sign ClarityValue message we use signMessageHashRsv method from '@stacks/transactions' in the Xverse wallet
Edit handleStructuredMessage and set it's content to:
Calling a contract method is slightly more elaborate, but it's only due to the fact we want to provide Stacks PostConditions.
Luckily, thanks to the BigInt defined hacks above - we can use Stacks libraries to build the transaction as we normally would.
In our case we want to transfer some dummy ExampleCoin tokens.
First we figure out contract related details, like the contract's name and account address. We also need the token's name - it's hidden in the contract's code (you can see it through explorer).
Then we build the PostConditions array - we want it to transfer exactly 1000 tokens (our "order amount").
If you want the transaction to be sponsored, you can pass a sponsored flag in the parameters.
The last thing is to simply combine it all and pass as request.