Swift Wallet Connect V1 迁移 V2

wallet connect 是设计给Dapp 与 Wallet App 交互的协议。

本文主要介绍集成v1 版本与v2 版本的一些差异,还有https://github.com/WalletConnect/WalletConnectSwiftV2迁移v2 的方案。

概要

接入swift wallet connect 主要是与Wallet App 进行连接,签名交易。

下图是使用Wallet connect 协议调用钱包App 进行签名交易的交互流程。

交互流程.png

Config

在接入SDK 进入项目之后,我们要理解使用 wallet connect swift 协议通过钱包签名交易的流程是会通过 WalletConnect 的Relay Server。意味着我们不是直接与钱包App 建立连接,而且是通过中继服务器建立连接。

所以在使用SDK 的第一步就是如何建立与Relay Server的连接,这需要我们在连接前完成配置。v1 与 v2 版本的配置方式有不同的重点,下面我们各自介绍一下不同与相似之处。

- v1 Config

v1 版本配置会简单一些,主要包括用于 展示的 MetaInfo。这个信息会被钱包App 接受并使用。这通常作为在钱包App连接页面展示的所需要的信息,所以下面代码中的config 就需要对应自己的Dapp做自定义了。

        let clientMeta = Session.ClientMeta(name: "ExampleDApp",
                                            description: "WalletConnectSwift",
                                            icons: [],
                                            url: URL(string: "you url"))
        let dAppInfo = Session.DAppInfo(peerId: UUID().uuidString, peerMeta: clientMeta)
        client = Client(delegate: self, dAppInfo: dAppInfo)

之前我们有说过Relay Server,这是我们配置的重点,我们使用Wallet Connect连接与发送请求需要通过中继服务器转发。而在v1 版本中,配置Relay Server 是在建立连接前进行配置。

        let wcUrl =  WCURL(topic: UUID().uuidString,
                           bridgeURL: URL(string: "https://safe-walletconnect.gnosis.io/"),
                           key: randomKey())

其中bridgeURL参数配置的url,就是Relay Server 的地址。

- v2 Config

v2 的 wallet connect swift 将Relay Server 细节隐藏了起来,使用 Networking类代替了v1中WCURL的配置,同时,v2 要求一个projectId 这需要去他们的网站上申请。

    Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory())

其中 socketFactory是配置网络中具体使用的 websocket 实例的地方。这里可以使用 Starscream 或者 URLSession

Dapp 的meta info,在v2 是在Pair 这里配置

let metadata = AppMetadata(name: <String>,
                           description: <String>,
                           url: <String>,
                           icons: <[String]>)

Pair.configure(metadata: metadata)

和v1一样,这些信息主要用于在Wallet App 上的展示。

连接

向钱包签名交易前,我们需要通过Wallet Connect 协议与钱包App 建立连接,连接一旦建立,意味着Dapp与Wallet App 拥有会话关系,其中的Session 是我们需要特别关注的东西。

- v1

连接过程是先创建好WCURL, 然后向中转层发起连接的请求,最后通过Deeplink或者Universal Links的方式去把WCURL 携带在跳转Link的参数里面跳转到目标的 Wallet App。也可以把 WCURL生成QRCode 然后通过Wallet App 去扫码连接。

        // 创建连接
        let wcUrl = WCURL(topic: UUID().uuidString,
                           bridgeURL: bridgeURL,
                           key: randomKey)

        //发起 Connect  
        do {
            try client.connect(to: wcUrl)
        } catch {
            callback?(.failed(error))
        }

        //跳转Wallet App
        let deepLink = self.getDeepLink(wallet: type, wcURL: wcUrl)
        guard let url = URL(string: deepLink), UIApplication.shared.canOpenURL(url) else { return }
        UIApplication.shared.open(url, options: [:], completionHandler: nil)

- v2

v2 的过程与上面相似,功能会更加复杂和强大,但是我们现在还不需要关心这些。

        // namespaces 定义了连接需要的信息,这些是v2协议的特性,可以支持多链多账号
        let namespaces: [String: ProposalNamespace] = [
            "eip155": ProposalNamespace(
                chains: [
                    Blockchain(ChainIdV2)!
                ],
                methods: [
                    "eth_sendTransaction",
                    "personal_sign",
                    "eth_estimateGas",
                    "eth_getTransactionReceipt",
                    "eth_call"
                ], events: [WCSessionEvent.chainChanged.rawValue, WCSessionEvent.accountsChanged.rawValue]
            )
        ]
        let date: Int = Int(Date().dateByAdding(day: 7)?.timeIntervalSince1970 ?? 0)
        
        let sessionProperties: [String: String] = [
            "sessionExpiry": String(date)
        ]
        
        Task {
            do {
                disconnectUnusedPairings()
                // 创建连接
                let uri = try await Pair.instance.create()
                // 发起connect
                try await Sign.instance.connect(
                    requiredNamespaces: namespaces,
                    optionalNamespaces: [:],
                    sessionProperties: sessionProperties,
                    topic: uri.topic
                )
                callback?(.succes(uri.absoluteString))
            } catch {
                callback?(.failed(error))
            }
        }

       //跳转Wallet App
       let deepLink = self.getDeepLink(wallet: type, wcURL: wcUrl)
       guard let url = URL(string: deepLink), UIApplication.shared.canOpenURL(url) else { return }
       UIApplication.shared.open(url, options: [:], completionHandler: nil)

发送Request

到了我们使用Wallet Connect 的重头戏了。我们连接之后就可以向 Wallet App 发送签名请求进行交易了。

这里主要展示 ethSendTransaction的使用,signMessage 方法我们也会经常用到,但是是类似的,就不再体现了。

我们需要认识到,在Dapp端发送了交易是向中继层发送消息,所以在收到中继层收到的回复后,我们还需要主动打开Wallet App 这样,建立过连接的Wallet App会主动向中继层拉取消息,拉取成功后就会展示我们发起的签名请求,等待用户的确定。

- v1

在v1,我们发消息的要携带的标识是 WCURL,所以我们需要把建立过连接的 Session 缓存在Dapp中。通过读取Session中的URL信息。

    func ethSendTransaction(_ transaction: Transaction, session: WCSession, callback: ((WCSendRequestResult) -> ())?) {
        
        guard let v1Session = getSession(for: session) else {
            callback?(.failed(WCConnectError.invaildSession))
            return
        }
        
        do {
            try client.eth_sendTransaction(url: v1Session.url, transaction: transaction.convertV1Transaction()) { Response in
                if let error = Response.error {
                    callback?(.failed(error))
                } else {
                    do {
                        let result = try Response.result(as: String.self)
                        callback?(.success(result))
                    } catch {
                        callback?(.failed(error))
                    }
                }
            }
        } catch {
            callback?(.failed(error))
        }
        
    }

请求成功后会收到回调,result 就是这次交易的Hash

- v2

v2 SDK 会自动管理我们的 Session。所以我们只需要保持好当前连接的Topic即可获取 Session去完成交易。

    func ethSendTransaction(_ transaction: DGTransaction, session: WCSession, sendSuccess: (() -> ())?, callback: ((WCSendRequestResult) -> ())?) {
        let method = "eth_sendTransaction"
        let requestParams = AnyCodable([transaction])
        let request = Request(topic: session.topic, method: method, params: requestParams, chainId: Blockchain(session.chainId)!)
        sendRequest(request, sendSuccess: sendSuccess, callback: callback)
    }
     // 通过request id 标识请求
    private func sendRequest(_ request: Request, sendSuccess: (() -> ())?, callback: ((WCSendRequestResult) -> ())?) {
        let id = request.id.string
        if let cb = callback {
            callBackPool[id] = cb
        }
        Task {
            do {
                try await Sign.instance.request(params: request)
                sendSuccess?()
            } catch {
                callBackPool[id]?(.failed(error))
                callBackPool[id] = nil
            }
        }
    }

v2的请求回调会通过Sign.instance.sessionResponsePublisher通知返回,在返回里面我们request id 去处理对应的回调。

总结

上面我们从 配置 - 连接 - 请求 三步流程,简单得概述 v1 向 v2迁移的处理方案,这里面也有很多很重要的点没讨论到。同时v2协议的设计理念更加先进,但是很多Wallet App 对 v2 协议支持情况实现程度都不同,所以其中还是有很多坑点可以讨论的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容