mirror of
https://github.com/luxfi/safe-ios.git
synced 2026-10-30 15:19:20 +00:00
109 lines
3.4 KiB
Swift
109 lines
3.4 KiB
Swift
//
|
|
// File.swift
|
|
//
|
|
//
|
|
// Created by Dmitry Bespalov on 03.01.22.
|
|
//
|
|
|
|
import Foundation
|
|
import CryptoSwift
|
|
|
|
public protocol SolContractFunction: SolAbiEncodable {
|
|
var selector: Sol.Bytes4 { get set }
|
|
var parameters: Sol.Tuple { get set }
|
|
|
|
associatedtype Returns: SolEncodableTuple
|
|
}
|
|
|
|
extension Sol {
|
|
public struct ContractFunction<R: SolEncodableTuple>: SolContractFunction {
|
|
public var selector: Sol.Bytes4
|
|
public var parameters: Sol.Tuple
|
|
|
|
public var canonicalName: Swift.String {
|
|
let name = selector.storage.map({ Swift.String($0, radix: 16) }).joined()
|
|
let result = name + parameters.canonicalName
|
|
return result
|
|
}
|
|
|
|
public typealias Returns = R
|
|
|
|
public init() { parameters = .init(); selector = .init() }
|
|
|
|
public init(selector: Sol.Bytes4, parameters: Sol.Tuple) {
|
|
self.parameters = parameters
|
|
self.selector = selector
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SolContractFunction {
|
|
public func encode() -> Data {
|
|
/*
|
|
All in all, a call to the function f with parameters a_1, ..., a_n is encoded as
|
|
|
|
function_selector(f) enc((a_1, ..., a_n))
|
|
|
|
and the return values v_1, ..., v_k of f are encoded as
|
|
|
|
enc((v_1, ..., v_k))
|
|
|
|
i.e. the values are combined into a tuple and encoded.
|
|
|
|
|
|
The first four bytes of the call data for a function call specifies the function to be called. It is the first (left, high-order in big-endian) four bytes of the Keccak-256 hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype without data location specifier, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used.
|
|
*/
|
|
let selector = selector.encode()[0..<4]
|
|
let result = selector + parameters.encode()
|
|
return result
|
|
}
|
|
|
|
public mutating func decode(from data: Data, offset: inout Int) throws {
|
|
guard offset < data.count - 4 + 1 else {
|
|
throw SolAbiDecodingError.outOfBounds
|
|
}
|
|
let selectorData = data[offset..<offset + 4] + Data(repeating: 0x00, count: 32 - 4)
|
|
var selectorOffset = 0
|
|
self.selector = try Sol.Bytes4(from: selectorData, offset: &selectorOffset)
|
|
offset += 4
|
|
|
|
try self.parameters.decode(from: data, offset: &offset)
|
|
}
|
|
|
|
public func encodePacked() -> Data {
|
|
// Since packed encoding is not used when calling functions, there is no special support for
|
|
// prepending a function selector.
|
|
let result = parameters.encodePacked()
|
|
return result
|
|
}
|
|
}
|
|
|
|
extension SolContractFunction where Self: SolKeyPathTuple {
|
|
public var canonicalName: String {
|
|
String(describing: type(of: self)) + parameters.canonicalName
|
|
}
|
|
|
|
public var parameters: Sol.Tuple {
|
|
get {
|
|
Sol.Tuple(elements: self.elements)
|
|
}
|
|
set {
|
|
self.elements = newValue.elements
|
|
}
|
|
}
|
|
|
|
public var selector: Sol.Bytes4 {
|
|
get {
|
|
// keccak256(canonicalName)[0..<4]
|
|
let preimage = canonicalName
|
|
let hashValue = SHA3(variant: .keccak256).calculate(for: preimage.bytes)
|
|
precondition(hashValue.count == 256 / 8)
|
|
let result = Sol.Bytes4(storage: Data(hashValue[0..<4]))
|
|
return result
|
|
}
|
|
set {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|