Swift Wallet Connect V1 遷移 V2

wallet connect 是設(shè)計給Dapp 與 Wallet App 交互的協(xié)議。

本文主要介紹集成v1 版本與v2 版本的一些差異,還有https://github.com/WalletConnect/WalletConnectSwiftV2遷移v2 的方案。

概要

接入swift wallet connect 主要是與Wallet App 進行連接,簽名交易。

下圖是使用Wallet connect 協(xié)議調(diào)用錢包App 進行簽名交易的交互流程。

交互流程.png

Config

在接入SDK 進入項目之后,我們要理解使用 wallet connect swift 協(xié)議通過錢包簽名交易的流程是會通過 WalletConnect 的Relay Server。意味著我們不是直接與錢包App 建立連接,而且是通過中繼服務(wù)器建立連接。

所以在使用SDK 的第一步就是如何建立與Relay Server的連接,這需要我們在連接前完成配置。v1 與 v2 版本的配置方式有不同的重點,下面我們各自介紹一下不同與相似之處。

- v1 Config

v1 版本配置會簡單一些,主要包括用于 展示的 MetaInfo。這個信息會被錢包App 接受并使用。這通常作為在錢包App連接頁面展示的所需要的信息,所以下面代碼中的config 就需要對應(yīng)自己的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連接與發(fā)送請求需要通過中繼服務(wù)器轉(zhuǎn)發(fā)。而在v1 版本中,配置Relay Server 是在建立連接前進行配置。

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

其中bridgeURL參數(shù)配置的url,就是Relay Server 的地址。

- v2 Config

v2 的 wallet connect swift 將Relay Server 細節(jié)隱藏了起來,使用 Networking類代替了v1中WCURL的配置,同時,v2 要求一個projectId 這需要去他們的網(wǎng)站上申請。

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

其中 socketFactory是配置網(wǎng)絡(luò)中具體使用的 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 協(xié)議與錢包App 建立連接,連接一旦建立,意味著Dapp與Wallet App 擁有會話關(guān)系,其中的Session 是我們需要特別關(guān)注的東西。

- v1

連接過程是先創(chuàng)建好WCURL, 然后向中轉(zhuǎn)層發(fā)起連接的請求,最后通過Deeplink或者Universal Links的方式去把WCURL 攜帶在跳轉(zhuǎn)Link的參數(shù)里面跳轉(zhuǎn)到目標的 Wallet App。也可以把 WCURL生成QRCode 然后通過Wallet App 去掃碼連接。

        // 創(chuàng)建連接
        let wcUrl = WCURL(topic: UUID().uuidString,
                           bridgeURL: bridgeURL,
                           key: randomKey)

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

        //跳轉(zhuǎn)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 的過程與上面相似,功能會更加復(fù)雜和強大,但是我們現(xiàn)在還不需要關(guān)心這些。

        // namespaces 定義了連接需要的信息,這些是v2協(xié)議的特性,可以支持多鏈多賬號
        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()
                // 創(chuàng)建連接
                let uri = try await Pair.instance.create()
                // 發(fā)起connect
                try await Sign.instance.connect(
                    requiredNamespaces: namespaces,
                    optionalNamespaces: [:],
                    sessionProperties: sessionProperties,
                    topic: uri.topic
                )
                callback?(.succes(uri.absoluteString))
            } catch {
                callback?(.failed(error))
            }
        }

       //跳轉(zhuǎn)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)

發(fā)送Request

到了我們使用Wallet Connect 的重頭戲了。我們連接之后就可以向 Wallet App 發(fā)送簽名請求進行交易了。

這里主要展示 ethSendTransaction的使用,signMessage 方法我們也會經(jīng)常用到,但是是類似的,就不再體現(xiàn)了。

我們需要認識到,在Dapp端發(fā)送了交易是向中繼層發(fā)送消息,所以在收到中繼層收到的回復(fù)后,我們還需要主動打開Wallet App 這樣,建立過連接的Wallet App會主動向中繼層拉取消息,拉取成功后就會展示我們發(fā)起的簽名請求,等待用戶的確定。

- v1

在v1,我們發(fā)消息的要攜帶的標識是 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))
        }
        
    }

請求成功后會收到回調(diào),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的請求回調(diào)會通過Sign.instance.sessionResponsePublisher通知返回,在返回里面我們request id 去處理對應(yīng)的回調(diào)。

總結(jié)

上面我們從 配置 - 連接 - 請求 三步流程,簡單得概述 v1 向 v2遷移的處理方案,這里面也有很多很重要的點沒討論到。同時v2協(xié)議的設(shè)計理念更加先進,但是很多Wallet App 對 v2 協(xié)議支持情況實現(xiàn)程度都不同,所以其中還是有很多坑點可以討論的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容