目录
[3.1 二维码生成](#3.1 二维码生成)
[3.1.1 初始化SignClient](#3.1.1 初始化SignClient)
[3.1.2 创建会话空间获取WC协议uri](#3.1.2 创建会话空间获取WC协议uri)
[3.1.3 生成二维码供用户扫描](#3.1.3 生成二维码供用户扫描)
[3.1.4 等待扫描](#3.1.4 等待扫描)
[3.2 发送交易事务](#3.2 发送交易事务)
[3.2.1 创建交易事务](#3.2.1 创建交易事务)
[3.2.2 向用户发送交易事务](#3.2.2 向用户发送交易事务)
[3.3 签名事务](#3.3 签名事务)
[3.3.1 接收签名事务并验证](#3.3.1 接收签名事务并验证)
[3.3.2 发送签名并发送到链上](#3.3.2 发送签名并发送到链上)
[3.4 签名任意数据并验证](#3.4 签名任意数据并验证)
Welcome to Code Block's blog
本篇文章主要介绍了
[walletconnect二维码及交互 ]
❤博主广交技术好友,喜欢文章的可以关注一下❤
1.编写目的
最近在使用walletconnect协议和typescript语言实现相关交互功能,在此对从walletconnet协议二维码生成 、连接后发送交易事务 、签名事务 、签名任意信息的处理进行记录,加深对walletconnect的理解,熟悉对其组件的使用,同时希望帮助到有实现相关功能的朋友。
2.实现功能
- 二维码生成:生成wc:协议二维码供用户扫码连接.
- 发送交易事务:向用户发送Transaction以供用户签名.
- 签名事务:用户签名后将transaction提交的链上.
- 签名任意信息:用户对任意信息签名,同时可以完成对签名信息的验证.
3.功能详解
依赖组件
|----------------------------|--------|--------------------------------|
| 名称 | 版本 | 作用 |
| @solana/web3.js | 1.95.2 | 链上相关操作:生成交易事务,提交事务到链上 |
| @walletconnect/sign-client | 2.14.0 | walletconnect协议相关操作:生成二维码、发送事务 |
| solana/spl-token | 0.4.8 | SPL代币事务操作:生成SPL代币事务 |
| qrcode | 1.5.3 | 生成二维码 |
注:这里的链是SOL链,其它链用法类似.SPL代币即除主要代币之外的代币.
3.1 二维码生成
3.1.1 初始化SignClient
SignClient作为与用户wallet交互的主要实现类,在开始时要进行初始化,初始化要使用参数分别是metadata****和 **projectId,**metadata是项目相关信息,这些信息会在连接时进行展示.projectId是在walletconnect官网申请的项目ID.代码如下:
TypeScript
const metadata={
//项目名称
name: 'BoggyGame',
//项目解释
description: 'BoggyGame Bot',
//项目官网
url: 'https://www.boggycoin.com',
//项目图片
icons: [
"https://i.postimg.cc/sftPCk3M/photo-2024-07-12-14-12-43.jpg"
]
}
//项目ID
const projectId="0176e783e7c5b0713450333ff866c2d6"
然后就可以对SignClient进行初始化,为保证性能,这里SignClient使用单例,代码如下:
TypeScript
async function getSignClient() {
if (!signClient) {
signClient = await SignClient.init({
projectId: projectId, // 替换为你的项目ID
metadata: metadata
});
}
return signClient;
}
3.1.2 创建会话空间获取WC协议uri
使用signClient进行和中继器的对等连接配对(实际为websocket链接),并获取订阅的主题(topic),然后将会话空间数据上传到对应的主题,即可获取uri和等待授权方法,实现代码如下:
TypeScript
export async function initWalletConnect(onApproval: (approval: any) => void):Promise<String|undefined> {
const signClient = await getSignClient()
// 创建对等连接获取主题
const {topic} = await signClient.core.pairing.create()
// 发送命名空间,获取uri和等待授权的函数
const { uri,approval } = await signClient.connect({
pairingTopic: topic,
//空间方法
requiredNamespaces: {
solana: {
methods: [
"solana_signTransaction",
"solana_signMessage",
],
chains: [
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
],
events: []
}
}
})
// 调用回调函数处理 approval
if (approval) {
onApproval(approval);
}
return uri;
}
这里存在两个方法solana_signTransaction和solana_signMessage方法,分别是签名交易事务和签名消息,空间内未定义的方法将无法调用.**onApproval: (approval: any)**用于接收外部传输的监听方法,方便在外部获取approval.
3.1.3 生成二维码供用户扫描
生成二维码可以使用qrcode库,直接将uri的连接字符串生成为二维码,代码如下:
TypeScript
import QRCode from 'qrcode';
export async function generateQR(data: string): Promise<any> {
const qrBuffer = await QRCode.toBuffer(data, {
width: 300, //宽度和高度
margin: 4 //边框距离
});
return qrBuffer;
}
3.1.4 等待扫描
可以使用await approval()方法实现等待扫描授权,授权完成会获得当前连接session,同时可以获得当前连接的account,实现代码如下:
TypeScript
//等待扫码后授权
const session=await approval();
//获取链接的账号
const account=session.namespaces.solana.accounts[0].split(':')[2];
//打印账号
console.log(account)
3.2 发送交易事务
3.2.1 创建交易事务
创建交易事务时需要用到@solana/web3.js库,这里我们创建一个转移SPL代币事务,我们需要两个地址(即发送方和接收方),同时因为是Solana链,所以需要获取这两个账户实际的AssociatedToken地址(即实际存储SPL代币的账户地址),同时需要代币的Mint地址和合约地址以及发送的数量,同时为加快交易的速度,需要设置UnitPrice 和**UnitLimit(**即增加一些交易费用来保证用户交易速度),实现代码如下:
TypeScript
export async function getTransaction(
senderPublicKey: string,
drawPublicKey: string,
tokenAmount: number):Promise<Transaction> {
//发送方公钥
const senderPubkey = new PublicKey(senderPublicKey);
//接收方公钥
const drawPubkey = new PublicKey(drawPublicKey);
//代币MINT地址
const tokenMintAddress = BOGGY_TOKEN_MINT;
//获取发送方AssociatedToken账户
const sourceTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,senderPubkey); //获取ACT账户
//获取接收方AssociatedToke账户
const destTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,drawPubkey);
//创建转移数据
const transferInstruction = createTransferInstruction(
sourceTokenAccount,
destTokenAccount,
senderPubkey,
tokenAmount * 1e9,
[],
TOKEN_PROGRAM_ID
);
// 创建 compute unit price 指令,提高交易速度
const computeUnitPriceInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 7500,
});
const computeUnitLimitInstruction=ComputeBudgetProgram.setComputeUnitLimit({
units:200000
})
//创建事务并添加上面的三个交易数据信息
const transaction=new Transaction().add(computeUnitPriceInstruction,computeUnitLimitInstruction,transferInstruction)
//设置最新的区块hash
transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
//设置交易费用由发送方支出
transaction.feePayer=senderPubkey;
return transaction;
}
3.2.2 向用户发送交易事务
通过SignClient.request方法可以向用户发送(通过中继器转发)交易事务,并等待用户的签名,实现代码如下:
TypeScript
const transaction=await getTransaction(
发送方地址,接收方地址,代币数量
)
const result = await signClient!.request<{ signature: string }>({
chainId:'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
topic: session!.topic,
request: {
method: "solana_signTransaction",
params: {
//付款方地址
feePayer: transaction.feePayer!.toBase58(),
//最近区块链hash
recentBlockhash: transaction.recentBlockhash,
//transaction中数据遍历封装
instructions: transaction.instructions.map((i) => ({
//合约ID
programId: i.programId.toBase58(),
//数据
data: Array.from(i.data),
//发送方和接收方
keys: i.keys.map((k) => ({
isSigner: k.isSigner,
isWritable: k.isWritable,
pubkey: k.pubkey.toBase58(),
})),
})),
},
},
});
这里因为transaction没有直接转换为walletconnect通信格式的方法,所以需要将transaction中的数据取出重新封装,当然也可以直接封装为walletconnet通信数据格式使用.
3.3 签名事务
3.3.1 接收签名事务并验证
用户签名数据后,即可获得签名后的Signature值,这里需要对返回后的Signature验证是否有效,然后添加到签名到transaction中,实现代码如下:
TypeScript
//添加签名
transaction.addSignature(
//签名方即发送方
transaction.feePayer,
//获取到的签名信息
Buffer.from(bs58.decode(result.signature))
);
//验证签名是否有效
const valid = transaction.verifySignatures();
3.3.2 发送签名并发送到链上
直接使用sendRawTransaction方法,将数据发送到链上,返回的txId值应该和用户的签名值相同,实现代码如下:
TypeScript
export async function sendTransaction(transaction:Transaction){
const txId =await connection.sendRawTransaction(transaction.serialize())
return txId;
}
3.4 签名任意数据并验证
可以使用签名完成任意数据的认证,这种认证主要用于用户登录的确认,如让用户签名一段随机信息,签名有效则可以认为用户完成登录,从而完成用户wallet网站登录,实现代码如下:
TypeScript
const response=await signClient.request({
topic:session.topic,
//链ID
chainId:'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
request:{
method: 'solana_signMessage',
params: {
//随机字符串
message:"37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7",
pubkey: 链接后用户地址
}
}
})
签名后可以使用公钥对签名后的数据进行验证,代码如下:
TypeScript
async function verifyMessageSignature(message: string, signature: string, userPublicKeyBase58: string) {
try {
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
// 将 Base58 格式的用户公钥转换为 PublicKey 对象
const userPublicKey = new PublicKey(userPublicKeyBase58);
// 将签名数据从 Base58 格式解码为 Uint8Array
const signatureBytes = bs58.decode(signature);
// 将消息字符串转换为 Uint8Array
const messageBytes = new TextEncoder().encode(message);
// 使用 PublicKey 对象和消息数据来验证签名
const isSignatureValid = await connection.verifySignature(
messageBytes,
signatureBytes,
userPublicKey
);
return isSignatureValid;
} catch (error) {
console.error('Error verifying signature:', error);
return false;
}
}
对区块链 内容感兴趣可以查看我的专栏:小试牛刀-区块链
感谢您的关注和收藏!!!!!!