mirror of
https://github.com/lil-org/tokenary.git
synced 2025-01-07 14:10:28 +03:00
commit
4294e6b65a
@ -6,12 +6,36 @@ extension SafariRequest {
|
||||
|
||||
struct Solana: SafariRequestBody {
|
||||
|
||||
enum Method: String, Decodable, CaseIterable {
|
||||
case connect, signMessage, signTransaction, signAllTransactions, signAndSendTransaction
|
||||
}
|
||||
|
||||
let method: Method
|
||||
let publicKey: String
|
||||
let message: String?
|
||||
let messages: [String]?
|
||||
let displayHex: Bool
|
||||
let sendOptions: [String: Any]?
|
||||
|
||||
init?(name: String, json: [String: Any]) {
|
||||
|
||||
guard let method = Method(rawValue: name),
|
||||
let publicKey = json["publicKey"] as? String else { return nil }
|
||||
self.method = method
|
||||
self.publicKey = publicKey
|
||||
let parameters = (json["object"] as? [String: Any])?["params"] as? [String: Any]
|
||||
self.message = parameters?["message"] as? String
|
||||
self.messages = parameters?["messages"] as? [String]
|
||||
self.displayHex = (parameters?["display"] as? String) == "hex"
|
||||
self.sendOptions = parameters?["options"] as? [String: Any]
|
||||
}
|
||||
|
||||
var responseUpdatesStoredConfiguration: Bool {
|
||||
return false
|
||||
switch method {
|
||||
case .connect:
|
||||
return true
|
||||
case .signMessage, .signTransaction, .signAllTransactions, .signAndSendTransaction:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,16 @@ extension ResponseToExtension {
|
||||
|
||||
struct Solana: Codable {
|
||||
|
||||
let publicKey: String?
|
||||
let result: String?
|
||||
let results: [String]?
|
||||
|
||||
init(publicKey: String? = nil, result: String? = nil, results: [String]? = nil) {
|
||||
self.publicKey = publicKey
|
||||
self.result = result
|
||||
self.results = results
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -13,14 +13,20 @@ window.tokenary.postMessage = (name, id, body, provider) => {
|
||||
};
|
||||
|
||||
window.ethereum = new TokenaryEthereum();
|
||||
const handler = {
|
||||
const ethereumHandler = {
|
||||
get(target, property) {
|
||||
return window.ethereum;
|
||||
}
|
||||
}
|
||||
window.web3 = new Proxy(window.ethereum, handler);
|
||||
window.web3 = new Proxy(window.ethereum, ethereumHandler);
|
||||
|
||||
window.solana = new TokenarySolana();
|
||||
const solanaHandler = {
|
||||
get(target, property) {
|
||||
return window.solana;
|
||||
}
|
||||
}
|
||||
window.phantom = new Proxy(window.solana, solanaHandler);
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
if (event.source == window && event.data && event.data.direction == "from-content-script") {
|
||||
|
@ -15,6 +15,7 @@
|
||||
"author": "Tokenary <support@tokenary.io>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bs58": "^5.0.0",
|
||||
"buffer": "^5.6.0",
|
||||
"events": "^3.2.0",
|
||||
"isutf8": "^3.1.1"
|
||||
|
@ -2,19 +2,234 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
import RPCServer from "./rpc";
|
||||
import ProviderRpcError from "./error";
|
||||
import Utils from "./utils";
|
||||
import IdMapping from "./id_mapping";
|
||||
import { EventEmitter } from "events";
|
||||
import isUtf8 from "isutf8";
|
||||
import { PublicKey, Transaction } from "@solana/web3.js";
|
||||
import bs58 from "bs58";
|
||||
|
||||
class TokenarySolana extends EventEmitter {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
processTokenaryResponse(id, response) {
|
||||
this.idMapping = new IdMapping();
|
||||
this.callbacks = new Map();
|
||||
|
||||
this.respondWithBuffer = new Map();
|
||||
this.transactionsPendingSignature = new Map();
|
||||
|
||||
this.isPhantom = true;
|
||||
this.publicKey = null;
|
||||
this.isConnected = false;
|
||||
this.isTokenary = true;
|
||||
|
||||
this.didGetLatestConfiguration = false;
|
||||
this.pendingPayloads = [];
|
||||
}
|
||||
|
||||
connect() {
|
||||
return this.request({method: "connect"});
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
// TODO: implement
|
||||
// support also via request "disconnect" method
|
||||
}
|
||||
|
||||
// provider.on("accountChanged", (publicKey: PublicKey | null));
|
||||
// TODO: support emitting accountChanged (on switch account event)
|
||||
|
||||
signTransaction(transaction) {
|
||||
const params = {message: bs58.encode(transaction.serializeMessage())};
|
||||
const payload = {method: "signTransaction", params: params, id: Utils.genId()};
|
||||
this.transactionsPendingSignature.set(payload.id, [transaction]);
|
||||
return this.request(payload);
|
||||
}
|
||||
|
||||
signAllTransactions(transactions) {
|
||||
const messages = transactions.map(transaction => {
|
||||
return bs58.encode(transaction.serializeMessage());
|
||||
});
|
||||
const params = {messages: messages};
|
||||
const payload = {method: "signAllTransactions", params: params, id: Utils.genId()};
|
||||
this.transactionsPendingSignature.set(payload.id, transactions);
|
||||
return this.request(payload);
|
||||
}
|
||||
|
||||
signAndSendTransaction(transaction, options) {
|
||||
var params = {message: bs58.encode(transaction.serializeMessage())};
|
||||
if (typeof options !== "undefined") {
|
||||
params.options = options;
|
||||
}
|
||||
return this.request({method: "signAndSendTransaction", params: params});
|
||||
}
|
||||
|
||||
signMessage(encodedMessage, display) {
|
||||
var params = {message: encodedMessage};
|
||||
if (typeof display !== "undefined") {
|
||||
params.display = display;
|
||||
}
|
||||
const payload = {method: "signMessage", params: params, id: Utils.genId()};
|
||||
this.respondWithBuffer.set(payload.id, true);
|
||||
return this.request(payload);
|
||||
}
|
||||
|
||||
request(payload) {
|
||||
this.idMapping.tryIntifyId(payload);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!payload.id) {
|
||||
payload.id = Utils.genId();
|
||||
}
|
||||
this.callbacks.set(payload.id, (error, data) => {
|
||||
// Some dapps do not get responses sent without a delay.
|
||||
// e.g., nftx.io does not start with a latest account if response is sent without a delay.
|
||||
setTimeout( function() {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
switch (payload.method) {
|
||||
case "connect":
|
||||
case "signMessage":
|
||||
case "signTransaction":
|
||||
case "signAllTransactions":
|
||||
case "signAndSendTransaction":
|
||||
return this.processPayload(payload);
|
||||
default:
|
||||
this.callbacks.delete(payload.id);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
processPayload(payload) {
|
||||
if (!this.didGetLatestConfiguration) {
|
||||
this.pendingPayloads.push(payload);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (payload.method) {
|
||||
case "connect":
|
||||
if (!this.publicKey) {
|
||||
return this.postMessage("connect", payload.id, {});
|
||||
} else {
|
||||
this.isConnected = true;
|
||||
this.emitConnect(this.publicKey);
|
||||
return this.sendResponse(payload.id, {publicKey: this.publicKey});
|
||||
}
|
||||
case "signMessage":
|
||||
if (typeof payload.params.message !== "string") {
|
||||
payload.params.message = Utils.bufferToHex(payload.params.message);
|
||||
}
|
||||
return this.postMessage("signMessage", payload.id, payload);
|
||||
case "signTransaction":
|
||||
return this.postMessage("signTransaction", payload.id, payload);
|
||||
case "signAllTransactions":
|
||||
return this.postMessage("signAllTransactions", payload.id, payload);
|
||||
case "signAndSendTransaction":
|
||||
return this.postMessage("signAndSendTransaction", payload.id, payload);
|
||||
}
|
||||
}
|
||||
|
||||
emitConnect(publicKey) {
|
||||
this.emit("connect", publicKey);
|
||||
}
|
||||
|
||||
processTokenaryResponse(id, response) {
|
||||
if (response.name == "didLoadLatestConfiguration") {
|
||||
this.didGetLatestConfiguration = true;
|
||||
if ("publicKey" in response) {
|
||||
const publicKey = new PublicKey(response.publicKey);
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
for(let payload of this.pendingPayloads) {
|
||||
this.processPayload(payload);
|
||||
}
|
||||
|
||||
this.pendingPayloads = [];
|
||||
} else if ("publicKey" in response) {
|
||||
this.isConnected = true;
|
||||
const publicKey = new PublicKey(response.publicKey);
|
||||
this.publicKey = publicKey;
|
||||
this.sendResponse(id, {publicKey: publicKey});
|
||||
this.emitConnect(publicKey);
|
||||
} else if ("result" in response) {
|
||||
if (response.name == "signTransaction" || response.name == "signAndSendTransaction") {
|
||||
if (this.transactionsPendingSignature.has(id)) {
|
||||
const pending = this.transactionsPendingSignature.get(id);
|
||||
this.transactionsPendingSignature.delete(id);
|
||||
const buffer = Utils.messageToBuffer(bs58.decode(response.result));
|
||||
const transaction = pending[0];
|
||||
transaction.addSignature(this.publicKey, buffer);
|
||||
this.sendResponse(id, transaction);
|
||||
} else {
|
||||
this.sendResponse(id, {signature: response.result, publicKey: this.publicKey.toString()});
|
||||
}
|
||||
} else {
|
||||
if (this.respondWithBuffer.get(id) === true) {
|
||||
this.respondWithBuffer.delete(id);
|
||||
const buffer = Utils.messageToBuffer(bs58.decode(response.result));
|
||||
this.sendResponse(id, {signature: buffer, publicKey: this.publicKey});
|
||||
} else {
|
||||
this.sendResponse(id, {signature: response.result, publicKey: this.publicKey.toString()});
|
||||
}
|
||||
}
|
||||
} else if ("results" in response) {
|
||||
if (this.transactionsPendingSignature.has(id)) {
|
||||
const transactions = this.transactionsPendingSignature.get(id);
|
||||
this.transactionsPendingSignature.delete(id);
|
||||
response.results.forEach( (signature, index) => {
|
||||
const buffer = Utils.messageToBuffer(bs58.decode(signature));
|
||||
transactions[index].addSignature(this.publicKey, buffer);
|
||||
});
|
||||
this.sendResponse(id, transactions);
|
||||
} else {
|
||||
this.sendResponse(id, {signatures: response.results, publicKey: this.publicKey.toString()});
|
||||
}
|
||||
} else if ("error" in response) {
|
||||
this.respondWithBuffer.delete(id);
|
||||
this.transactionsPendingSignature.delete(id);
|
||||
this.sendError(id, response.error);
|
||||
}
|
||||
}
|
||||
|
||||
postMessage(handler, id, data) {
|
||||
var publicKey = "";
|
||||
if (this.publicKey) {
|
||||
publicKey = this.publicKey.toString();
|
||||
}
|
||||
|
||||
let object = {
|
||||
object: data,
|
||||
publicKey: publicKey,
|
||||
};
|
||||
window.tokenary.postMessage(handler, id, object, "solana");
|
||||
}
|
||||
|
||||
sendResponse(id, result) {
|
||||
let originId = this.idMapping.tryPopId(id) || id;
|
||||
let callback = this.callbacks.get(id);
|
||||
if (callback) {
|
||||
callback(null, result);
|
||||
this.callbacks.delete(id);
|
||||
} else {
|
||||
console.log(`callback id: ${id} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
sendError(id, error) {
|
||||
console.log(`<== ${id} sendError ${error}`);
|
||||
let callback = this.callbacks.get(id);
|
||||
if (callback) {
|
||||
callback(error instanceof Error ? error : new Error(error), null);
|
||||
this.callbacks.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenarySolana;
|
||||
|
@ -1771,6 +1771,11 @@ base-x@^3.0.2:
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
base-x@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a"
|
||||
integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
@ -1959,6 +1964,13 @@ bs58@^4.0.0:
|
||||
dependencies:
|
||||
base-x "^3.0.2"
|
||||
|
||||
bs58@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279"
|
||||
integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==
|
||||
dependencies:
|
||||
base-x "^4.0.0"
|
||||
|
||||
bs58check@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
|
||||
|
@ -4,6 +4,7 @@ enum ApprovalSubject {
|
||||
case signMessage
|
||||
case signPersonalMessage
|
||||
case signTypedData
|
||||
case approveTransaction
|
||||
|
||||
var asAuthenticationReason: AuthenticationReason {
|
||||
switch self {
|
||||
@ -13,6 +14,8 @@ enum ApprovalSubject {
|
||||
return .signPersonalMessage
|
||||
case .signTypedData:
|
||||
return .signTypedData
|
||||
case .approveTransaction:
|
||||
return .approveTransaction
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +27,8 @@ enum ApprovalSubject {
|
||||
return Strings.signPersonalMessage
|
||||
case .signTypedData:
|
||||
return Strings.signTypedData
|
||||
case .approveTransaction:
|
||||
return Strings.approveTransaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ enum AuthenticationReason {
|
||||
case signMessage
|
||||
case signPersonalMessage
|
||||
case signTypedData
|
||||
case approveTransaction
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
@ -28,6 +29,8 @@ enum AuthenticationReason {
|
||||
return Strings.signPersonalMessage
|
||||
case .signTypedData:
|
||||
return Strings.signTypedData
|
||||
case .approveTransaction:
|
||||
return Strings.approveTransaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ struct DappRequestProcessor {
|
||||
|
||||
private static let walletsManager = WalletsManager.shared
|
||||
private static let ethereum = Ethereum.shared
|
||||
private static let solana = Solana.shared
|
||||
|
||||
static func processSafariRequest(_ request: SafariRequest, completion: @escaping () -> Void) -> DappRequestAction {
|
||||
guard ExtensionBridge.hasRequest(id: request.id) else {
|
||||
@ -16,6 +17,11 @@ struct DappRequestProcessor {
|
||||
switch request.body {
|
||||
case let .ethereum(body):
|
||||
return process(request: request, ethereumRequest: body, completion: completion)
|
||||
case let .solana(body):
|
||||
return process(request: request, solanaRequest: body, completion: completion)
|
||||
case .tezos:
|
||||
respond(to: request, error: "Tezos is not supported yet", completion: completion)
|
||||
return .none
|
||||
case let .unknown(body):
|
||||
switch body.method {
|
||||
case .justShowApp:
|
||||
@ -34,12 +40,80 @@ struct DappRequestProcessor {
|
||||
}
|
||||
return .selectAccount(action)
|
||||
}
|
||||
case .solana:
|
||||
respond(to: request, error: "Solana is not supported yet", completion: completion)
|
||||
case .tezos:
|
||||
respond(to: request, error: "Tezos is not supported yet", completion: completion)
|
||||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
private static func process(request: SafariRequest, solanaRequest body: SafariRequest.Solana, completion: @escaping () -> Void) -> DappRequestAction {
|
||||
let peerMeta = PeerMeta(title: request.host, iconURLString: request.favicon)
|
||||
switch body.method {
|
||||
case .connect:
|
||||
// TODO: pick an address from the list
|
||||
let responseBody = ResponseToExtension.Solana(publicKey: "")
|
||||
respond(to: request, body: .solana(responseBody), completion: completion)
|
||||
return .none // TODO: replace with .selectAccount
|
||||
case .signAllTransactions:
|
||||
guard let messages = body.messages else {
|
||||
respond(to: request, error: Strings.somethingWentWrong, completion: completion)
|
||||
return .none
|
||||
}
|
||||
let displayMessage = messages.joined(separator: "\n\n")
|
||||
let action = SignMessageAction(provider: request.provider, subject: .approveTransaction, address: body.publicKey, meta: displayMessage, peerMeta: peerMeta) { approved in
|
||||
if approved {
|
||||
var results = [String]()
|
||||
for message in messages {
|
||||
guard let signed = solana.sign(message: message, asHex: false) else {
|
||||
respond(to: request, error: Strings.failedToSign, completion: completion)
|
||||
return
|
||||
}
|
||||
results.append(signed)
|
||||
}
|
||||
let responseBody = ResponseToExtension.Solana(results: results)
|
||||
respond(to: request, body: .solana(responseBody), completion: completion)
|
||||
} else {
|
||||
respond(to: request, error: Strings.failedToSign, completion: completion)
|
||||
}
|
||||
}
|
||||
return .approveMessage(action)
|
||||
case .signMessage, .signTransaction, .signAndSendTransaction:
|
||||
guard let message = body.message else {
|
||||
respond(to: request, error: Strings.somethingWentWrong, completion: completion)
|
||||
return .none
|
||||
}
|
||||
let displayMessage: String
|
||||
let subject: ApprovalSubject
|
||||
switch body.method {
|
||||
case .signMessage:
|
||||
displayMessage = body.displayHex ? message : (String(data: Data(hex: message), encoding: .utf8) ?? message)
|
||||
subject = .signMessage
|
||||
default:
|
||||
displayMessage = message
|
||||
subject = .approveTransaction
|
||||
}
|
||||
let action = SignMessageAction(provider: request.provider, subject: subject, address: body.publicKey, meta: displayMessage, peerMeta: peerMeta) { approved in
|
||||
guard approved else {
|
||||
respond(to: request, error: Strings.failedToSign, completion: completion)
|
||||
return
|
||||
}
|
||||
|
||||
if body.method == .signAndSendTransaction {
|
||||
solana.signAndSendTransaction(message: message, options: body.sendOptions) { result in
|
||||
switch result {
|
||||
case let .success(signature):
|
||||
let responseBody = ResponseToExtension.Solana(result: signature)
|
||||
respond(to: request, body: .solana(responseBody), completion: completion)
|
||||
case .failure:
|
||||
respond(to: request, error: Strings.failedToSend, completion: completion)
|
||||
}
|
||||
}
|
||||
} else if let signed = solana.sign(message: message, asHex: body.method == .signMessage) {
|
||||
let responseBody = ResponseToExtension.Solana(result: signed)
|
||||
respond(to: request, body: .solana(responseBody), completion: completion)
|
||||
} else {
|
||||
respond(to: request, error: Strings.failedToSign, completion: completion)
|
||||
}
|
||||
}
|
||||
return .approveMessage(action)
|
||||
}
|
||||
}
|
||||
|
||||
private static func process(request: SafariRequest, ethereumRequest: SafariRequest.Ethereum, completion: @escaping () -> Void) -> DappRequestAction {
|
||||
|
290
Shared/Services/Solana.swift
Normal file
290
Shared/Services/Solana.swift
Normal file
@ -0,0 +1,290 @@
|
||||
// Copyright © 2022 Tokenary. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import WalletCore
|
||||
|
||||
class Solana {
|
||||
|
||||
enum SendTransactionError: Error {
|
||||
case blockhashNotFound, unknown
|
||||
}
|
||||
|
||||
private enum Method: String {
|
||||
case sendTransaction, simulateTransaction, getLatestBlockhash
|
||||
}
|
||||
|
||||
private struct SendTransactionResponse: Codable {
|
||||
let result: String?
|
||||
private let error: Error?
|
||||
|
||||
var blockhashNotFound: Bool {
|
||||
return error?.data.err == "BlockhashNotFound"
|
||||
}
|
||||
|
||||
private struct Error: Codable {
|
||||
|
||||
let data: Data
|
||||
|
||||
struct Data: Codable {
|
||||
let err: String
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private struct LatestBlockhashResponse: Codable {
|
||||
|
||||
let result: Result
|
||||
|
||||
struct Result: Codable {
|
||||
let value: Value
|
||||
|
||||
struct Value: Codable {
|
||||
let blockhash: String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static let shared = Solana()
|
||||
private let urlSession = URLSession(configuration: .default)
|
||||
private let rpcURL = URL(string: "https://api.mainnet-beta.solana.com")!
|
||||
|
||||
private init() {}
|
||||
|
||||
func sign(message: String, asHex: Bool) -> String? {
|
||||
let digest = asHex ? Data(hex: message) : Base58.decodeNoCheck(string: message)
|
||||
guard let digest = digest else { return nil }
|
||||
|
||||
let words = ""
|
||||
let password = ""
|
||||
guard let key = StoredKey.importHDWallet(mnemonic: words, name: "", password: Data(password.utf8), coin: .solana),
|
||||
let phantomPrivateKey = key.wallet(password: Data(password.utf8))?.getKey(coin: .solana, derivationPath: "m/44'/501'/0'/0'") else { return nil }
|
||||
|
||||
if let data = phantomPrivateKey.sign(digest: digest, curve: CoinType.solana.curve) {
|
||||
return Base58.encodeNoCheck(data: data)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func signAndSendTransaction(retryCount: Int = 0, message: String, options: [String: Any]?, completion: @escaping (Result<String, SendTransactionError>) -> Void) {
|
||||
guard retryCount < 3,
|
||||
let signed = sign(message: message, asHex: false),
|
||||
let raw = compileTransactionData(message: message, signature: signed, simulation: false) else {
|
||||
completion(.failure(.unknown))
|
||||
return
|
||||
}
|
||||
|
||||
sendTransaction(signed: raw, options: options) { [weak self] result in
|
||||
switch result {
|
||||
case let .success(result):
|
||||
completion(.success(result))
|
||||
case let .failure(sendTransactionError):
|
||||
switch sendTransactionError {
|
||||
case .unknown:
|
||||
completion(.failure(.unknown))
|
||||
case .blockhashNotFound:
|
||||
self?.updateBlockhash(message: message) { updatedMessage in
|
||||
if let updatedMessage = updatedMessage {
|
||||
self?.signAndSendTransaction(retryCount: retryCount + 1, message: updatedMessage, options: options, completion: completion)
|
||||
} else {
|
||||
completion(.failure(.unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func createRequest(method: Method, parameters: [Any]? = nil) -> URLRequest {
|
||||
var request = URLRequest(url: rpcURL)
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.httpMethod = "POST"
|
||||
|
||||
var dict: [String: Any] = [
|
||||
"method": method.rawValue,
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0"
|
||||
]
|
||||
if let parameters = parameters {
|
||||
dict["params"] = parameters
|
||||
}
|
||||
|
||||
request.httpBody = try? JSONSerialization.data(withJSONObject: dict, options: .fragmentsAllowed)
|
||||
return request
|
||||
}
|
||||
|
||||
private func getLatestBlockhash(completion: @escaping (String?) -> Void) {
|
||||
let request = createRequest(method: .getLatestBlockhash)
|
||||
let dataTask = urlSession.dataTask(with: request) { data, _, _ in
|
||||
DispatchQueue.main.async {
|
||||
if let data = data,
|
||||
let response = try? JSONDecoder().decode(LatestBlockhashResponse.self, from: data) {
|
||||
completion(response.result.value.blockhash)
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
private func sendTransaction(signed: String, options: [String: Any]?, completion: @escaping (Result<String, SendTransactionError>) -> Void) {
|
||||
var parameters: [Any] = [signed]
|
||||
if let options = options {
|
||||
parameters.append(options)
|
||||
}
|
||||
let request = createRequest(method: .sendTransaction, parameters: parameters)
|
||||
let dataTask = urlSession.dataTask(with: request) { data, _, _ in
|
||||
DispatchQueue.main.async {
|
||||
if let data = data,
|
||||
let response = try? JSONDecoder().decode(SendTransactionResponse.self, from: data) {
|
||||
if let result = response.result {
|
||||
completion(.success(result))
|
||||
} else {
|
||||
completion(.failure(response.blockhashNotFound ? .blockhashNotFound : .unknown))
|
||||
}
|
||||
} else {
|
||||
completion(.failure(.unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
private func simulateTransaction(serializedMessage: String, signature: String, publicKey: String, completion: @escaping (Any?) -> Void) {
|
||||
guard let message = compileTransactionData(message: serializedMessage, signature: signature, simulation: true) else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
let configuration: [String: Any] = ["accounts": ["encoding": "jsonParsed", "addresses": [publicKey]]]
|
||||
let request = createRequest(method: .simulateTransaction, parameters: [message, configuration])
|
||||
|
||||
let dataTask = urlSession.dataTask(with: request) { data, _, _ in
|
||||
DispatchQueue.main.async {
|
||||
if let data = data {
|
||||
let raw = try? JSONSerialization.jsonObject(with: data)
|
||||
completion(raw)
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
private func compileTransactionData(message: String, signature: String, simulation: Bool) -> String? {
|
||||
guard let messageData = Base58.decodeNoCheck(string: message),
|
||||
let signatureData = Base58.decodeNoCheck(string: signature) else { return nil }
|
||||
let numberOfSignatures = messageData.requiredSignaturesCount
|
||||
let placeholderSignature = Data(repeating: 0, count: 64)
|
||||
|
||||
var result = Data()
|
||||
let encodedSignatureLength = Data.encodeLength(numberOfSignatures)
|
||||
result = encodedSignatureLength + (simulation ? placeholderSignature : signatureData)
|
||||
|
||||
for _ in 0..<(numberOfSignatures - 1) {
|
||||
result += placeholderSignature
|
||||
}
|
||||
|
||||
result += messageData
|
||||
return Base58.encodeNoCheck(data: result)
|
||||
}
|
||||
|
||||
private func updateBlockhash(message: String, completion: @escaping (String?) -> Void) {
|
||||
guard var data = Base58.decodeNoCheck(string: message) else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
getLatestBlockhash { blockhash in
|
||||
guard let blockhash = blockhash,
|
||||
let numRequiredSignatures = data.popFirst(),
|
||||
let numReadonlySignedAccounts = data.popFirst(),
|
||||
let numReadonlyUnsignedAccounts = data.popFirst(),
|
||||
let blockhashData = Base58.decodeNoCheck(string: blockhash) else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
let numberOfBytes = 32
|
||||
let accountCount = data.decodeLength()
|
||||
let blockhashStartIndex = data.index(data.startIndex, offsetBy: numberOfBytes * accountCount)
|
||||
let blockhashEndIndex = data.index(blockhashStartIndex, offsetBy: numberOfBytes)
|
||||
data.replaceSubrange(blockhashStartIndex..<blockhashEndIndex, with: blockhashData)
|
||||
|
||||
data = Data.encodeLength(accountCount) + data
|
||||
data = Data([numRequiredSignatures, numReadonlySignedAccounts, numReadonlyUnsignedAccounts]) + data
|
||||
completion(Base58.encodeNoCheck(data: data))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://github.com/p2p-org/solana-swift/blob/main/Sources/SolanaSwift/Extensions/Data%2BExtensions.swift
|
||||
private extension Data {
|
||||
|
||||
var requiredSignaturesCount: Int {
|
||||
if let first = first {
|
||||
return Int(first)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
var decodedLength: Int {
|
||||
var len = 0
|
||||
var size = 0
|
||||
var bytes = self
|
||||
while true {
|
||||
guard let elem = bytes.first else { break }
|
||||
bytes = bytes.dropFirst()
|
||||
len = len | ((Int(elem) & 0x7f) << (size * 7))
|
||||
size += 1
|
||||
if Int16(elem) & 0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
mutating func decodeLength() -> Int {
|
||||
var len = 0
|
||||
var size = 0
|
||||
while true {
|
||||
guard let elem = bytes.first else { break }
|
||||
_ = popFirst()
|
||||
len = len | ((Int(elem) & 0x7f) << (size * 7))
|
||||
size += 1
|
||||
if Int16(elem) & 0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
static func encodeLength(_ len: Int) -> Data {
|
||||
encodeLength(UInt(len))
|
||||
}
|
||||
|
||||
private static func encodeLength(_ len: UInt) -> Data {
|
||||
var remLen = len
|
||||
var bytes = Data()
|
||||
while true {
|
||||
var elem = remLen & 0x7f
|
||||
remLen = remLen >> 7
|
||||
if remLen == 0 {
|
||||
bytes.append(UInt8(elem))
|
||||
break
|
||||
} else {
|
||||
elem = elem | 0x80
|
||||
bytes.append(UInt8(elem))
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
}
|
@ -76,5 +76,6 @@ struct Strings {
|
||||
static let calculating = "Calculating…"
|
||||
static let howToEnableSafariExtension = "How to enable Safari extension?"
|
||||
static let shareInvite = "Share an invite"
|
||||
static let approveTransaction = "Approve Transaction"
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0A35995B64EBF9C1B79FFADF /* Pods_Tokenary_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE9E1A64A4B68CD2CC762C5C /* Pods_Tokenary_iOS.framework */; };
|
||||
0D059AD226C2796200EE3023 /* ApprovalSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D059AD126C2796200EE3023 /* ApprovalSubject.swift */; };
|
||||
0D82ED97B38BA6F113762E82 /* Pods_Tokenary_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF0BA09057BCB2C4011D0C5E /* Pods_Tokenary_iOS.framework */; };
|
||||
0DC850E726B73A5900809E82 /* AuthenticationReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC850E626B73A5900809E82 /* AuthenticationReason.swift */; };
|
||||
2C03D1D2269B407900EF10EA /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C03D1D1269B407900EF10EA /* NetworkMonitor.swift */; };
|
||||
2C03D1D5269B428C00EF10EA /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C03D1D4269B428C00EF10EA /* Notification.swift */; };
|
||||
@ -64,6 +64,8 @@
|
||||
2C264BEB27B6B50700234393 /* DappRequestProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */; };
|
||||
2C264BEC27B6B50700234393 /* DappRequestProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */; };
|
||||
2C3B7F022756A08600931264 /* Identifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3B7F012756A08600931264 /* Identifiers.swift */; };
|
||||
2C40379428199110004C7263 /* Solana.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C40379328199110004C7263 /* Solana.swift */; };
|
||||
2C40379528199110004C7263 /* Solana.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C40379328199110004C7263 /* Solana.swift */; };
|
||||
2C40709027667A6600AB3D55 /* MultilineLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C40708E27667A6600AB3D55 /* MultilineLabelTableViewCell.swift */; };
|
||||
2C40709127667A6600AB3D55 /* MultilineLabelTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C40708F27667A6600AB3D55 /* MultilineLabelTableViewCell.xib */; };
|
||||
2C40709427667A8600AB3D55 /* ImageWithLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C40709227667A8600AB3D55 /* ImageWithLabelTableViewCell.swift */; };
|
||||
@ -188,7 +190,7 @@
|
||||
2CF255BA275A749300AE54B9 /* ApproveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF255B9275A749300AE54B9 /* ApproveViewController.swift */; };
|
||||
2CFDDF4C2765416F00F89019 /* macos-specific-content.js in Resources */ = {isa = PBXBuildFile; fileRef = 2CFDDF4B2765416F00F89019 /* macos-specific-content.js */; };
|
||||
2CFDDF4E2765417E00F89019 /* ios-specific-content.js in Resources */ = {isa = PBXBuildFile; fileRef = 2CFDDF4D2765417D00F89019 /* ios-specific-content.js */; };
|
||||
9CF0E61C7E25270AFD7B66BC /* Pods_Tokenary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 748E4E402F96890CD86451CD /* Pods_Tokenary.framework */; };
|
||||
E1F659197E90BA26C3A3A6EE /* Pods_Tokenary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80697A23D681CBF87CF830B1 /* Pods_Tokenary.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -267,6 +269,7 @@
|
||||
2C264BE527B5AC6800234393 /* TezosResponseToExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TezosResponseToExtension.swift; sourceTree = "<group>"; };
|
||||
2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DappRequestProcessor.swift; sourceTree = "<group>"; };
|
||||
2C3B7F012756A08600931264 /* Identifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifiers.swift; sourceTree = "<group>"; };
|
||||
2C40379328199110004C7263 /* Solana.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Solana.swift; sourceTree = "<group>"; };
|
||||
2C40708E27667A6600AB3D55 /* MultilineLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineLabelTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2C40708F27667A6600AB3D55 /* MultilineLabelTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MultilineLabelTableViewCell.xib; sourceTree = "<group>"; };
|
||||
2C40709227667A8600AB3D55 /* ImageWithLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageWithLabelTableViewCell.swift; sourceTree = "<group>"; };
|
||||
@ -365,12 +368,12 @@
|
||||
2CF255B9275A749300AE54B9 /* ApproveViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApproveViewController.swift; sourceTree = "<group>"; };
|
||||
2CFDDF4B2765416F00F89019 /* macos-specific-content.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "macos-specific-content.js"; sourceTree = "<group>"; };
|
||||
2CFDDF4D2765417D00F89019 /* ios-specific-content.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "ios-specific-content.js"; sourceTree = "<group>"; };
|
||||
3A44A6AD75AD00A8147D51C9 /* Pods-Tokenary iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary iOS.release.xcconfig"; path = "Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS.release.xcconfig"; sourceTree = "<group>"; };
|
||||
748E4E402F96890CD86451CD /* Pods_Tokenary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tokenary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
79196256009C6E698EB7AA59 /* Pods-Tokenary.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary.release.xcconfig"; path = "Target Support Files/Pods-Tokenary/Pods-Tokenary.release.xcconfig"; sourceTree = "<group>"; };
|
||||
79E83B47DE0EB9BC8AC9B236 /* Pods-Tokenary iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary iOS.debug.xcconfig"; path = "Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9FB2D4E7F11F60183689311C /* Pods-Tokenary.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary.debug.xcconfig"; path = "Target Support Files/Pods-Tokenary/Pods-Tokenary.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
BE9E1A64A4B68CD2CC762C5C /* Pods_Tokenary_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tokenary_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3238B55CC74B94A606D08A98 /* Pods-Tokenary iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary iOS.debug.xcconfig"; path = "Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3819EB5B48C6943F202EA8A0 /* Pods-Tokenary.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary.release.xcconfig"; path = "Target Support Files/Pods-Tokenary/Pods-Tokenary.release.xcconfig"; sourceTree = "<group>"; };
|
||||
599AA1113E7986A6414E0CD9 /* Pods-Tokenary iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary iOS.release.xcconfig"; path = "Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS.release.xcconfig"; sourceTree = "<group>"; };
|
||||
6FB8D9A5870F897C5CA4D898 /* Pods-Tokenary.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tokenary.debug.xcconfig"; path = "Target Support Files/Pods-Tokenary/Pods-Tokenary.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
80697A23D681CBF87CF830B1 /* Pods_Tokenary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tokenary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FF0BA09057BCB2C4011D0C5E /* Pods_Tokenary_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tokenary_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -385,7 +388,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9CF0E61C7E25270AFD7B66BC /* Pods_Tokenary.framework in Frameworks */,
|
||||
E1F659197E90BA26C3A3A6EE /* Pods_Tokenary.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -393,7 +396,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0A35995B64EBF9C1B79FFADF /* Pods_Tokenary_iOS.framework in Frameworks */,
|
||||
0D82ED97B38BA6F113762E82 /* Pods_Tokenary_iOS.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -422,6 +425,15 @@
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2A7484A18D772B4233D171DA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
80697A23D681CBF87CF830B1 /* Pods_Tokenary.framework */,
|
||||
FF0BA09057BCB2C4011D0C5E /* Pods_Tokenary_iOS.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2C09CBA0273979C1009AD39B /* Safari macOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -454,7 +466,7 @@
|
||||
2C5FF98A26C8567D00B32ACC /* Screensaver */,
|
||||
2C19953D2674C4B900A8E370 /* Products */,
|
||||
FB5786212D81829B0FADBD25 /* Pods */,
|
||||
3C02E73226D93B28BA7E14DD /* Frameworks */,
|
||||
2A7484A18D772B4233D171DA /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -781,6 +793,7 @@
|
||||
children = (
|
||||
2C03D1D1269B407900EF10EA /* NetworkMonitor.swift */,
|
||||
2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */,
|
||||
2C40379328199110004C7263 /* Solana.swift */,
|
||||
2CAA412426C7CD93009F3535 /* ReviewRequester.swift */,
|
||||
2C901C4C268A033100D0926A /* GasService.swift */,
|
||||
2CC0CDBD2692027E0072922A /* PriceService.swift */,
|
||||
@ -809,22 +822,13 @@
|
||||
path = Screens;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3C02E73226D93B28BA7E14DD /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
748E4E402F96890CD86451CD /* Pods_Tokenary.framework */,
|
||||
BE9E1A64A4B68CD2CC762C5C /* Pods_Tokenary_iOS.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB5786212D81829B0FADBD25 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9FB2D4E7F11F60183689311C /* Pods-Tokenary.debug.xcconfig */,
|
||||
79196256009C6E698EB7AA59 /* Pods-Tokenary.release.xcconfig */,
|
||||
79E83B47DE0EB9BC8AC9B236 /* Pods-Tokenary iOS.debug.xcconfig */,
|
||||
3A44A6AD75AD00A8147D51C9 /* Pods-Tokenary iOS.release.xcconfig */,
|
||||
6FB8D9A5870F897C5CA4D898 /* Pods-Tokenary.debug.xcconfig */,
|
||||
3819EB5B48C6943F202EA8A0 /* Pods-Tokenary.release.xcconfig */,
|
||||
3238B55CC74B94A606D08A98 /* Pods-Tokenary iOS.debug.xcconfig */,
|
||||
599AA1113E7986A6414E0CD9 /* Pods-Tokenary iOS.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
@ -865,13 +869,13 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2C19954C2674C4BA00A8E370 /* Build configuration list for PBXNativeTarget "Tokenary" */;
|
||||
buildPhases = (
|
||||
032A6C803438BB662CB91D5C /* [CP] Check Pods Manifest.lock */,
|
||||
5ED94A042948552AFC0B9216 /* [CP] Check Pods Manifest.lock */,
|
||||
2C1995382674C4B900A8E370 /* Sources */,
|
||||
2C1995392674C4B900A8E370 /* Frameworks */,
|
||||
2C19953A2674C4B900A8E370 /* Resources */,
|
||||
2C1995512674C6A100A8E370 /* ShellScript */,
|
||||
2C09CBB8273979C1009AD39B /* Embed App Extensions */,
|
||||
E992E558A2E6E14778576867 /* [CP] Embed Pods Frameworks */,
|
||||
2B67825D537A2599798098F3 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -887,13 +891,13 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2C5FF98026C84F7C00B32ACC /* Build configuration list for PBXNativeTarget "Tokenary iOS" */;
|
||||
buildPhases = (
|
||||
9283FF1ED099C56D5CD4C47F /* [CP] Check Pods Manifest.lock */,
|
||||
111F85DD9343C4B33301D00C /* [CP] Check Pods Manifest.lock */,
|
||||
2C5FF96B26C84F7B00B32ACC /* Sources */,
|
||||
2C5FF96C26C84F7B00B32ACC /* Frameworks */,
|
||||
2C5FF96D26C84F7B00B32ACC /* Resources */,
|
||||
2CC8C59D27678D4B0083FB1B /* ShellScript */,
|
||||
2CCEB84627594E2A00768473 /* Embed App Extensions */,
|
||||
3D6DA0588EFAC21D933260AA /* [CP] Embed Pods Frameworks */,
|
||||
68842E1F5932B8E151C67B04 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -1055,7 +1059,7 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
032A6C803438BB662CB91D5C /* [CP] Check Pods Manifest.lock */ = {
|
||||
111F85DD9343C4B33301D00C /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -1070,13 +1074,30 @@
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Tokenary-checkManifestLockResult.txt",
|
||||
"$(DERIVED_FILE_DIR)/Pods-Tokenary iOS-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2B67825D537A2599798098F3 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary/Pods-Tokenary-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary/Pods-Tokenary-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tokenary/Pods-Tokenary-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2C1995512674C6A100A8E370 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -1147,24 +1168,7 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\n\nif which swiftlint; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||
};
|
||||
3D6DA0588EFAC21D933260AA /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9283FF1ED099C56D5CD4C47F /* [CP] Check Pods Manifest.lock */ = {
|
||||
5ED94A042948552AFC0B9216 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -1179,28 +1183,28 @@
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Tokenary iOS-checkManifestLockResult.txt",
|
||||
"$(DERIVED_FILE_DIR)/Pods-Tokenary-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E992E558A2E6E14778576867 /* [CP] Embed Pods Frameworks */ = {
|
||||
68842E1F5932B8E151C67B04 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary/Pods-Tokenary-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary/Pods-Tokenary-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tokenary/Pods-Tokenary-frameworks.sh\"\n";
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tokenary iOS/Pods-Tokenary iOS-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@ -1288,6 +1292,7 @@
|
||||
2C264BDC27B5AC5400234393 /* EthereumResponseToExtension.swift in Sources */,
|
||||
2C03D1D5269B428C00EF10EA /* Notification.swift in Sources */,
|
||||
2C1995562674D0F300A8E370 /* Ethereum.swift in Sources */,
|
||||
2C40379428199110004C7263 /* Solana.swift in Sources */,
|
||||
2C8A09DF267579EA00993638 /* AccountsListViewController.swift in Sources */,
|
||||
2C917429267D2A6E00049075 /* Keychain.swift in Sources */,
|
||||
);
|
||||
@ -1341,6 +1346,7 @@
|
||||
2CF2559C275A477F00AE54B9 /* ApprovalSubject.swift in Sources */,
|
||||
2C264BE727B5AC6800234393 /* TezosResponseToExtension.swift in Sources */,
|
||||
2C264BD627B5806200234393 /* Web3Provider.swift in Sources */,
|
||||
2C40379528199110004C7263 /* Solana.swift in Sources */,
|
||||
2CE0594427640EB40042D844 /* ExtensionBridge.swift in Sources */,
|
||||
2CF255B1275A4A1800AE54B9 /* ResponseToExtension.swift in Sources */,
|
||||
2CF2559B275A46E700AE54B9 /* AuthenticationReason.swift in Sources */,
|
||||
@ -1619,7 +1625,7 @@
|
||||
};
|
||||
2C19954D2674C4BA00A8E370 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9FB2D4E7F11F60183689311C /* Pods-Tokenary.debug.xcconfig */;
|
||||
baseConfigurationReference = 6FB8D9A5870F897C5CA4D898 /* Pods-Tokenary.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@ -1647,7 +1653,7 @@
|
||||
};
|
||||
2C19954E2674C4BA00A8E370 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 79196256009C6E698EB7AA59 /* Pods-Tokenary.release.xcconfig */;
|
||||
baseConfigurationReference = 3819EB5B48C6943F202EA8A0 /* Pods-Tokenary.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@ -1675,7 +1681,7 @@
|
||||
};
|
||||
2C5FF98126C84F7C00B32ACC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 79E83B47DE0EB9BC8AC9B236 /* Pods-Tokenary iOS.debug.xcconfig */;
|
||||
baseConfigurationReference = 3238B55CC74B94A606D08A98 /* Pods-Tokenary iOS.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@ -1701,7 +1707,7 @@
|
||||
};
|
||||
2C5FF98226C84F7C00B32ACC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 3A44A6AD75AD00A8147D51C9 /* Pods-Tokenary iOS.release.xcconfig */;
|
||||
baseConfigurationReference = 599AA1113E7986A6414E0CD9 /* Pods-Tokenary iOS.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
|
Loading…
Reference in New Issue
Block a user