|
|
|
@ -27,24 +27,24 @@ https://app.awallet.io/AA9CQFq1tAAAe+6CvdnoZrK9EUeApH8iYcaE4wECAwQFBgcICS+YK4TGN |
|
|
|
|
import Foundation |
|
|
|
|
import BigInt |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class UniversalLinkHandler { |
|
|
|
|
|
|
|
|
|
public let urlPrefix = "https://app.awallet.io/" |
|
|
|
|
public static let paymentServer = "http://stormbird.duckdns.org:8080/api/claimToken" |
|
|
|
|
public static let paymentServer = "https://feemaster.eastasia.cloudapp.azure.com:8080/api/claimToken" //"http://stormbird.duckdns.org:8080/api/claimToken" |
|
|
|
|
|
|
|
|
|
func createUniversalLink(signedOrder: SignedOrder) -> String |
|
|
|
|
{ |
|
|
|
|
let message = MarketQueueHandler.bytesToHexa(signedOrder.message) |
|
|
|
|
let signature = signedOrder.signature.substring(from: 2) |
|
|
|
|
//message is with 32 bytes each of price and expiry and is shortened for link |
|
|
|
|
func createUniversalLink(signedOrder: SignedOrder) -> String { |
|
|
|
|
let message = formatMessageForLink(signedOrder: signedOrder) |
|
|
|
|
let signature = signedOrder.signature |
|
|
|
|
let link = (message + signature).hexa2Bytes |
|
|
|
|
let binaryData = Data(bytes: link) |
|
|
|
|
let base64String = binaryData.base64EncodedString() |
|
|
|
|
|
|
|
|
|
return urlPrefix + base64String |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func parseURL(url: String) -> SignedOrder { |
|
|
|
|
//TODO add exception handling |
|
|
|
|
//link has shortened price and expiry and must be expanded |
|
|
|
|
func parseUniversalLink(url: String) -> SignedOrder { |
|
|
|
|
let linkInfo = url.substring(from: urlPrefix.count) |
|
|
|
|
let linkBytes = Data(base64Encoded: linkInfo)?.array |
|
|
|
|
let price = getPriceFromLinkBytes(linkBytes: linkBytes!) |
|
|
|
@ -52,19 +52,87 @@ public class UniversalLinkHandler { |
|
|
|
|
let contractAddress = getContractAddressFromLinkBytes(linkBytes: linkBytes!) |
|
|
|
|
let ticketIndices = getTicketIndicesFromLinkBytes(linkBytes: linkBytes!) |
|
|
|
|
let (v, r, s) = getVRSFromLinkBytes(linkBytes: linkBytes!) |
|
|
|
|
let message = getMessageFromLinkBytes(linkBytes: linkBytes!) |
|
|
|
|
|
|
|
|
|
let order = Order( |
|
|
|
|
price: price, |
|
|
|
|
indices: ticketIndices, |
|
|
|
|
expiry: expiry, |
|
|
|
|
contractAddress: contractAddress, |
|
|
|
|
start: BigUInt("0")!, |
|
|
|
|
count: ticketIndices.count |
|
|
|
|
price: price, |
|
|
|
|
indices: ticketIndices, |
|
|
|
|
expiry: expiry, |
|
|
|
|
contractAddress: contractAddress, |
|
|
|
|
start: BigUInt("0")!, |
|
|
|
|
count: ticketIndices.count |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
let message = getMessageFromOrder(order: order) |
|
|
|
|
return SignedOrder(order: order, message: message, signature: "0x" + r + s + v) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//we used a special encoding so that one 16 bit number could represent either one ticket or two |
|
|
|
|
//this is for the purpose of keeping universal links as short as possible |
|
|
|
|
func decodeTicketIndices(indices: [UInt16]) -> [UInt8] { |
|
|
|
|
var indicesBytes = [UInt8]() |
|
|
|
|
for i in 0...indices.count - 1 { |
|
|
|
|
let index = indices[i] |
|
|
|
|
if(index < 128) { |
|
|
|
|
let byte = UInt8(index) |
|
|
|
|
indicesBytes.append(byte) |
|
|
|
|
} else { |
|
|
|
|
//Top 7 bits |
|
|
|
|
let firstByteHigh = UInt8(128 + (index >> 8)) |
|
|
|
|
//bottom 8 bits |
|
|
|
|
let secondByteLow = UInt8(index & 255) |
|
|
|
|
indicesBytes.append(firstByteHigh) |
|
|
|
|
indicesBytes.append(secondByteLow) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return indicesBytes |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//shortens price and expiry |
|
|
|
|
func formatMessageForLink(signedOrder: SignedOrder) -> String { |
|
|
|
|
let message = signedOrder.message |
|
|
|
|
let indices = decodeTicketIndices(indices: signedOrder.order.indices) |
|
|
|
|
var messageWithSzabo = [UInt8]() |
|
|
|
|
let price = Array(message[0...31]) |
|
|
|
|
let expiry = Array(message[32...63]) |
|
|
|
|
let priceHex = MarketQueueHandler.bytesToHexa(price) |
|
|
|
|
let expiryHex = MarketQueueHandler.bytesToHexa(expiry) |
|
|
|
|
//removes leading zeros |
|
|
|
|
let priceInt = BigUInt(priceHex, radix: 16)! |
|
|
|
|
let expiryInt = BigUInt(expiryHex, radix: 16)! |
|
|
|
|
//change from wei to szabo |
|
|
|
|
let priceSzabo = priceInt / 1000000000000 |
|
|
|
|
var priceBytes = formatTo4Bytes(priceSzabo.serialize().bytes) |
|
|
|
|
var expiryBytes = formatTo4Bytes(expiryInt.serialize().bytes) |
|
|
|
|
for i in 0...3 { |
|
|
|
|
messageWithSzabo.append(priceBytes[i]) |
|
|
|
|
} |
|
|
|
|
for i in 0...3 { |
|
|
|
|
messageWithSzabo.append(expiryBytes[i]) |
|
|
|
|
} |
|
|
|
|
for i in 64...83 { |
|
|
|
|
messageWithSzabo.append(message[i]) |
|
|
|
|
} |
|
|
|
|
for i in 0...indices.count - 1 { |
|
|
|
|
messageWithSzabo.append(indices[i]) |
|
|
|
|
} |
|
|
|
|
return MarketQueueHandler.bytesToHexa(messageWithSzabo) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func formatTo4Bytes(_ array: [UInt8]) -> [UInt8] { |
|
|
|
|
var formattedArray = [UInt8]() |
|
|
|
|
if array.count == 4 { |
|
|
|
|
return array |
|
|
|
|
} else if array.isEmpty { |
|
|
|
|
for _ in 0...3 { |
|
|
|
|
formattedArray.append(0) |
|
|
|
|
} |
|
|
|
|
return formattedArray |
|
|
|
|
} else { |
|
|
|
|
let missingDigits = 4 - array.count |
|
|
|
|
for _ in 0...missingDigits - 1 { |
|
|
|
|
formattedArray.append(0) |
|
|
|
|
} |
|
|
|
|
return formattedArray |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getPriceFromLinkBytes(linkBytes: [UInt8]) -> BigUInt { |
|
|
|
|
var priceBytes = [UInt8]() |
|
|
|
@ -72,8 +140,9 @@ public class UniversalLinkHandler { |
|
|
|
|
//price in szabo |
|
|
|
|
priceBytes.append(linkBytes[i]) |
|
|
|
|
} |
|
|
|
|
return (BigUInt(MarketQueueHandler.bytesToHexa(priceBytes), |
|
|
|
|
radix: 16)!.multiplied(by: BigUInt("1000000000000")!)) |
|
|
|
|
let priceHex = MarketQueueHandler.bytesToHexa(priceBytes) |
|
|
|
|
let price = BigUInt(priceHex, radix: 16)! |
|
|
|
|
return price * 1000000000000 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getExpiryFromLinkBytes(linkBytes: [UInt8]) -> BigUInt { |
|
|
|
@ -113,25 +182,23 @@ public class UniversalLinkHandler { |
|
|
|
|
else { |
|
|
|
|
ticketIndices.append(UInt16(byte)) |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
break |
|
|
|
|
case 2: |
|
|
|
|
currentIndex += UInt16(byte) |
|
|
|
|
ticketIndices.append(currentIndex) |
|
|
|
|
state = 1 |
|
|
|
|
break; |
|
|
|
|
break |
|
|
|
|
default: |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ticketIndices |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getVRSFromLinkBytes(linkBytes: [UInt8]) -> (String, String, String) { |
|
|
|
|
var signatureStart = linkBytes.count - 65 |
|
|
|
|
var rBytes = [UInt8]() |
|
|
|
|
for i in signatureStart...signatureStart + 31 |
|
|
|
|
{ |
|
|
|
|
for i in signatureStart...signatureStart + 31 { |
|
|
|
|
rBytes.append(linkBytes[i]) |
|
|
|
|
} |
|
|
|
|
let r = MarketQueueHandler.bytesToHexa(rBytes) |
|
|
|
@ -140,20 +207,49 @@ public class UniversalLinkHandler { |
|
|
|
|
for i in signatureStart...signatureStart + 31 { |
|
|
|
|
sBytes.append(linkBytes[i]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let s = MarketQueueHandler.bytesToHexa(sBytes) |
|
|
|
|
let v = String(format:"%2X", linkBytes[linkBytes.count - 1]).trimmed |
|
|
|
|
|
|
|
|
|
return (v, r, s) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getMessageFromLinkBytes(linkBytes: [UInt8]) -> [UInt8] { |
|
|
|
|
let messageLength = linkBytes.count - 65 |
|
|
|
|
|
|
|
|
|
//price and expiry need to be 32 bytes each |
|
|
|
|
func getMessageFromOrder(order: Order) -> [UInt8] { |
|
|
|
|
var message = [UInt8]() |
|
|
|
|
for i in 0...messageLength - 1 { |
|
|
|
|
message.append(linkBytes[i]) |
|
|
|
|
//encode price and expiry first |
|
|
|
|
let priceBytes = padTo32(order.price.serialize().array) |
|
|
|
|
for i in 0...31 { |
|
|
|
|
message.append(priceBytes[i]) |
|
|
|
|
} |
|
|
|
|
let expiryBytes = padTo32(order.expiry.serialize().array) |
|
|
|
|
for i in 0...31 { |
|
|
|
|
message.append(expiryBytes[i]) |
|
|
|
|
} |
|
|
|
|
let contractBytes = order.contractAddress.hexa2Bytes |
|
|
|
|
for i in 0...19 { |
|
|
|
|
message.append(contractBytes[i]) |
|
|
|
|
} |
|
|
|
|
let indices = OrderHandler.uInt16ArrayToUInt8(arrayOfUInt16: order.indices) |
|
|
|
|
for i in 0...indices.count - 1 { |
|
|
|
|
message.append(indices[i]) |
|
|
|
|
} |
|
|
|
|
return message |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func padTo32(_ buffer: [UInt8]) -> [UInt8] { |
|
|
|
|
var padded = [UInt8]() |
|
|
|
|
if buffer.isEmpty { |
|
|
|
|
for _ in 0...31 { |
|
|
|
|
padded.append(0) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for _ in 0...31 - buffer.count - 1 { |
|
|
|
|
padded.append(0) |
|
|
|
|
} |
|
|
|
|
for i in 0...buffer.count - 1 { |
|
|
|
|
padded.append(buffer[i]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return padded |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|