Skip to content

Commit

Permalink
Merge pull request #143 from argentlabs/develop
Browse files Browse the repository at this point in the history
0.8.0
  • Loading branch information
DarthMike authored Jun 11, 2021
2 parents f728d46 + dd707e3 commit b411430
Show file tree
Hide file tree
Showing 14 changed files with 199 additions and 26 deletions.
2 changes: 1 addition & 1 deletion web3.swift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'web3.swift'
s.version = '0.7.2'
s.version = '0.8.0'
s.license = 'MIT'
s.summary = 'Ethereum API for Swift'
s.homepage = 'https://github.com/argentlabs/web3.swift'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class EthereumAccount_SignTransactionTests: XCTestCase {
let tx = EthereumTransaction(from: nil, to: to, value: value, data: nil, nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, chainId: chainID)

let account = try! EthereumAccount.init(keyStorage: TestEthereumKeyStorage(privateKey: "0x4646464646464646464646464646464646464646464646464646464646464646"))
let signed = try! account.sign(tx)
let signed = try! account.sign(transaction: tx)

let v = signed.v.web3.hexString
let r = signed.r.web3.hexString
Expand Down
68 changes: 67 additions & 1 deletion web3sTests/Contract/ABIDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -309,5 +309,71 @@ class ABIDecoderTests: XCTestCase {
print(error.localizedDescription)
}
}

func test_GivenSimpleTuple_ThenDecodesCorrectly() {
do {
let value = try ABIDecoder.decodeData("0x00000000000000000000000064d0ea4fc60f27e74f1a70aa6f39d403bbe56793000000000000000000000000000000000000000000000000000000000000001e", types: [SimpleTuple.self])

XCTAssertEqual(try value[0].decoded(), SimpleTuple(address: EthereumAddress("0x64d0ea4fc60f27e74f1a70aa6f39d403bbe56793"), amount: BigUInt(30)))
} catch {
print(error.localizedDescription)
XCTFail()
}
}

func test_testGivenDynamicContentTuple_ThenDecodesCorrectly() {
do {
let value = try ABIDecoder.decodeData("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000036162630000000000000000000000000000000000000000000000000000000000", types: [DynamicContentTuple.self])

XCTAssertEqual(try value[0].decoded(), DynamicContentTuple(message: "abc"))
} catch {
print(error.localizedDescription)
XCTFail()
}
}

func test_testGivenLongTuple_ThenDecodesCorrectly() {
do {
let value = try ABIDecoder.decodeData("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001007efef35dcd300eec8819c4ce5cb6b57be685254d583954273c5cc16edee83790ba42a7d804d9eff383efb1864514f5f15c82f1c333a777dd8f76dba1c1977029000000000000000000000000000000000000000000000000000000000000005668747470733a2f2f697066732e666c65656b2e636f2f697066732f62616679626569623774726c746c66353637647171336a766f6b37336b3776706e6b7864713367663665766a326c74657a7a7a7a6871756336656100000000000000000000000000000000000000000000000000000000000000000000000000000000005668747470733a2f2f697066732e666c65656b2e636f2f697066732f62616679626569657a706165676378796c74707733716a6d78746678716961646471627264727a6f79766d62616768716468776875756c6963697900000000000000000000", types: [LongTuple.self])

XCTAssertEqual(try value[0].decoded(), LongTuple(value1: "https://ipfs.fleek.co/ipfs/bafybeib7trltlf567dqq3jvok73k7vpnkxdq3gf6evj2ltezzzzhquc6ea",
value2: "https://ipfs.fleek.co/ipfs/bafybeiezpaegcxyltpw3qjmxtfxqiaddqbrdrzoyvmbaghqdhwhuuliciy",
value3: Data(hex: "0x7efef35dcd300eec8819c4ce5cb6b57be685254d583954273c5cc16edee83790")!,
value4: Data(hex: "0xba42a7d804d9eff383efb1864514f5f15c82f1c333a777dd8f76dba1c1977029")!))
} catch {
print(error.localizedDescription)
XCTFail()
}
}

func test_testGivenArrayOfTuples_ThenDecodesCorrectly() {
do {
let value = try ABIDecoder.decodeData("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000064d0ea4fc60f27e74f1a70aa6f39d403bbe56793000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000003c1bd6b420448cf16a389c8b0115ccb3660bb8540000000000000000000000000000000000000000000000000000000000000078", types: [ABIArray<SimpleTuple>.self])

XCTAssertEqual(try value[0].decodedTupleArray(), [
SimpleTuple(address: EthereumAddress("0x64d0eA4FC60f27E74f1a70Aa6f39D403bBe56793"), amount: 30),
SimpleTuple(address: EthereumAddress("0x3C1Bd6B420448Cf16A389C8b0115CCB3660bB854"), amount: 120)])
} catch {
print(error.localizedDescription)
XCTFail()
}
}

func test_GivenArrayOfLongTuples_WhenHasOneEntry_ThenDecodesCorrectly() {
do {
let value = try ABIDecoder.decodeData("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001007efef35dcd300eec8819c4ce5cb6b57be685254d583954273c5cc16edee83790ba42a7d804d9eff383efb1864514f5f15c82f1c333a777dd8f76dba1c1977029000000000000000000000000000000000000000000000000000000000000005668747470733a2f2f697066732e666c65656b2e636f2f697066732f62616679626569623774726c746c66353637647171336a766f6b37336b3776706e6b7864713367663665766a326c74657a7a7a7a6871756336656100000000000000000000000000000000000000000000000000000000000000000000000000000000005668747470733a2f2f697066732e666c65656b2e636f2f697066732f62616679626569657a706165676378796c74707733716a6d78746678716961646471627264727a6f79766d62616768716468776875756c6963697900000000000000000000", types: [ABIArray<LongTuple>.self])

XCTAssertEqual(try value[0].decodedTupleArray(),
[
LongTuple(value1: "https://ipfs.fleek.co/ipfs/bafybeib7trltlf567dqq3jvok73k7vpnkxdq3gf6evj2ltezzzzhquc6ea",
value2: "https://ipfs.fleek.co/ipfs/bafybeiezpaegcxyltpw3qjmxtfxqiaddqbrdrzoyvmbaghqdhwhuuliciy",
value3: Data(hex: "0x7efef35dcd300eec8819c4ce5cb6b57be685254d583954273c5cc16edee83790")!,
value4: Data(hex: "0xba42a7d804d9eff383efb1864514f5f15c82f1c333a777dd8f76dba1c1977029")!)

])
} catch {
print(error.localizedDescription)
XCTFail()
}
}
}

10 changes: 5 additions & 5 deletions web3sTests/Contract/ABIFunctionEncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class ABIFunctionEncoderTests: XCTestCase {
}
}

fileprivate struct SimpleTuple: ABITuple {
struct SimpleTuple: ABITuple, Equatable {
static var types: [ABIType.Type] { [EthereumAddress.self, BigUInt.self] }

var address: EthereumAddress
Expand All @@ -308,7 +308,7 @@ fileprivate struct SimpleTuple: ABITuple {
var encodableValues: [ABIType] { [address, amount] }
}

fileprivate struct LongTuple: ABITuple {
struct LongTuple: ABITuple, Equatable {
static var types: [ABIType.Type] { [String.self, String.self, Data32.self, Data32.self] }

var value1: String
Expand Down Expand Up @@ -343,7 +343,7 @@ fileprivate struct LongTuple: ABITuple {
var encodableValues: [ABIType] { [value1, value2, value3, value4] }
}

fileprivate struct DynamicContentTuple: ABITuple {
struct DynamicContentTuple: ABITuple, Equatable {
static var types: [ABIType.Type] { [String.self] }

var message: String
Expand Down Expand Up @@ -425,7 +425,7 @@ fileprivate struct RelayerExecute: ABIFunction {
}
}

fileprivate struct NumberTuple: ABITuple {
struct NumberTuple: ABITuple, Equatable {
func encode(to encoder: ABIFunctionEncoder) throws {
try encoder.encode(value)
}
Expand All @@ -445,7 +445,7 @@ fileprivate struct NumberTuple: ABITuple {
var encodableValues: [ABIType] { [value] }
}

fileprivate struct TupleOfTuples: ABITuple {
struct TupleOfTuples: ABITuple {
func encode(to encoder: ABIFunctionEncoder) throws {
try encoder.encode(value1)
try encoder.encode(value2)
Expand Down
23 changes: 21 additions & 2 deletions web3sTests/ERC721/ERC721Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import BigInt
@testable import web3

let tokenOwner = EthereumAddress("0x69F84b91E7107206E841748C2B52294A1176D45e")
let previousOwner = EthereumAddress("0x64d0ea4fc60f27e74f1a70aa6f39d403bbe56793")
let nonOwner = EthereumAddress("0x64d0eA4FC60f27E74f1a70Aa6f39D403bBe56792")
let nftImageURL = URL(string: "https://ipfs.io/ipfs/QmUDJMmiJEsueLbr6jxh7vhSSFAvjfYTLC64hgkQm1vH2C/graph.svg")!
let nftURL = URL(string: "https://ipfs.io/ipfs/QmUtKP7LnZnL2pWw2ERvNDndP9v5EPoJH7g566XNdgoRfE")!
Expand Down Expand Up @@ -71,7 +72,7 @@ class ERC721Tests: XCTestCase {
waitForExpectations(timeout: 10)
}

func test_GivenAddressWithTransfer_FindsTransferEvent() {
func test_GivenAddressWithTransfer_FindsInTransferEvent() {
let expect = expectation(description: "Events")

erc721.transferEventsTo(recipient: tokenOwner,
Expand All @@ -80,14 +81,32 @@ class ERC721Tests: XCTestCase {
toBlock: .Number(
6948276),
completion: { (error, events) in
XCTAssertEqual(events?.first?.from, EthereumAddress("0x64d0ea4fc60f27e74f1a70aa6f39d403bbe56793"))
XCTAssertEqual(events?.first?.from, previousOwner)
XCTAssertEqual(events?.first?.to, tokenOwner)
XCTAssertEqual(events?.first?.tokenId, 23)
expect.fulfill()
})

waitForExpectations(timeout: 10)
}

func test_GivenAddressWithTransfer_FindsOutTransferEvent() {
let expect = expectation(description: "Events")

erc721.transferEventsFrom(sender: previousOwner,
fromBlock: .Number(
6948276),
toBlock: .Number(
6948276),
completion: { (error, events) in
XCTAssertEqual(events?.first?.to, tokenOwner)
XCTAssertEqual(events?.first?.from, previousOwner)
XCTAssertEqual(events?.first?.tokenId, 23)
expect.fulfill()
})

waitForExpectations(timeout: 10)
}
}

class ERC721MetadataTests: XCTestCase {
Expand Down
4 changes: 2 additions & 2 deletions web3swift/src/Account/EthereumAccount+SignTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ enum EthereumSignerError: Error {
public extension EthereumAccount {

func signRaw(_ transaction: EthereumTransaction) throws -> Data {
let signed: SignedTransaction = try sign(transaction)
let signed: SignedTransaction = try sign(transaction: transaction)
guard let raw = signed.raw else {
throw EthereumSignerError.unknownError
}
return raw
}

internal func sign(_ transaction: EthereumTransaction) throws -> SignedTransaction {
internal func sign(transaction: EthereumTransaction) throws -> SignedTransaction {

guard let raw = transaction.raw else {
throw EthereumSignerError.emptyRawTransaction
Expand Down
2 changes: 1 addition & 1 deletion web3swift/src/Account/EthereumAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ protocol EthereumAccountProtocol {
func sign(hex: String) throws -> Data
func sign(message: Data) throws -> Data
func sign(message: String) throws -> Data
func sign(_ transaction: EthereumTransaction) throws -> SignedTransaction
func sign(transaction: EthereumTransaction) throws -> SignedTransaction
}

public enum EthereumAccountError: Error {
Expand Down
2 changes: 1 addition & 1 deletion web3swift/src/Client/EthereumClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public class EthereumClient: EthereumClientProtocol {
transaction.chainId = network.intValue
}

guard let _ = transaction.chainId, let signedTx = (try? account.sign(transaction)), let transactionHex = signedTx.raw?.web3.hexString else {
guard let _ = transaction.chainId, let signedTx = (try? account.sign(transaction: transaction)), let transactionHex = signedTx.raw?.web3.hexString else {
group.leave()
return completion(EthereumClientError.encodeIssue, nil)
}
Expand Down
8 changes: 4 additions & 4 deletions web3swift/src/Client/Models/EthereumTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ public struct EthereumTransaction: EthereumTransactionProtocol, Equatable, Codab
}
}

struct SignedTransaction {
let transaction: EthereumTransaction
public struct SignedTransaction {
public let transaction: EthereumTransaction
let v: Int
let r: Data
let s: Data
Expand All @@ -145,13 +145,13 @@ struct SignedTransaction {
self.s = s.web3.strippingZeroesFromBytes
}

var raw: Data? {
public var raw: Data? {
let txArray: [Any?] = [transaction.nonce, transaction.gasPrice, transaction.gasLimit, transaction.to.value.web3.noHexPrefix, transaction.value, transaction.data, self.v, self.r, self.s]

return RLP.encode(txArray)
}

var hash: Data? {
public var hash: Data? {
return raw?.web3.keccak256
}
}
39 changes: 33 additions & 6 deletions web3swift/src/Contract/ABIDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,45 @@ public class ABIDecoder {

try deepDecode(data: data, type: arrayType, result: &result, offset: &newOffset, size: &size)
return result
case .Tuple:
throw ABIError.notCurrentlySupported
case .Tuple(let types):
var result: [String] = []

if type.isDynamic {
guard let offsetHex = (try decode(data, forType: ABIRawType.FixedUInt(256), offset: offset)).first else {
throw ABIError.invalidValue
}

let tail = Array(data.dropFirst(Int(hex: offsetHex) ?? offset))
var newOffset = 0
for type in types {
result += try decode(
tail,
forType: type,
offset: newOffset)
newOffset += type.memory
}

return result
} else {
var newOffset = offset
for type in types {
result += try decode(
Array(data.dropFirst(newOffset)),
forType: type,
offset: 0)
newOffset += type.memory
}

return result
}
}
}

private static func deepDecode(data: [UInt8], type: ABIRawType, result: inout [String], offset: inout Int, size: inout Int) throws -> Void {
if size < 1 { return }

guard let stringValue = (try decode(data, forType: type, offset: offset)).first else {
throw ABIError.invalidValue
}
result.append(stringValue)
let decoded = try decode(data, forType: type, offset: offset)
result.append(contentsOf: decoded)
offset += type.memory
size -= 1

Expand Down
11 changes: 11 additions & 0 deletions web3swift/src/Contract/ABIRawType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ extension ABIRawType: RawRepresentable {
}
}

var isTuple: Bool {
switch self {
case .Tuple:
return true
default:
return false
}
}

var isPaddedInDynamic: Bool {
switch self {
case .FixedUInt, .FixedInt:
Expand Down Expand Up @@ -155,6 +164,8 @@ extension ABIRawType: RawRepresentable {
switch self {
case .FixedArray(let type, let size):
return type.memory * size
case .Tuple(let types):
return types.map(\.memory).reduce(0, +)
default:
return 32
}
Expand Down
23 changes: 23 additions & 0 deletions web3swift/src/Contract/Statically Typed/ABIDecoder+Static.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ extension ABIDecoder {

return parsed
}

public func decodedTupleArray<T: ABITuple>() throws -> [T] {
let parse = T.parser

let tupleElements = T.types.count
let size = entry.count / tupleElements

var parsed = [T]()
var leftElements = entry
while leftElements.count >= tupleElements {
let slice = Array(leftElements[0..<tupleElements])
if let abc = try parse(slice) as? T {
parsed.append(abc)
}
leftElements = Array(leftElements.dropFirst(tupleElements))
}

guard parsed.count == size else {
throw ABIError.invalidValue
}

return parsed
}
}

public static func decodeData(_ data: RawABI, types: [ABIType.Type], asArray: Bool = false) throws -> [DecodedValue] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,12 @@ extension ABITuple {
}
public static var parser: ParserFunction {
return { data in
let first = data.first ?? ""
return try ABIDecoder.decode(first, to: String.self)
let values = data.map { ABIDecoder.DecodedValue(entry: [$0]) }
guard let decoded = try? self.init(values: values) else {
throw ABIError.invalidValue
}

return decoded
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions web3swift/src/ERC721/ERC721.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ public class ERC721: ERC165 {
}
}
}

public func transferEventsFrom(sender: EthereumAddress,
fromBlock: EthereumBlock,
toBlock: EthereumBlock,
completion: @escaping((Error?, [ERC721Events.Transfer]?) -> Void)) {
guard let result = try? ABIEncoder.encode(sender).bytes, let sig = try? ERC721Events.Transfer.signature() else {
completion(EthereumSignerError.unknownError, nil)
return
}

client.getEvents(addresses: nil,
topics: [ sig, String(hexFromBytes: result)],
fromBlock: fromBlock,
toBlock: toBlock,
eventTypes: [ERC721Events.Transfer.self]) { (error, events, unprocessedLogs) in

if let events = events as? [ERC721Events.Transfer] {
return completion(error, events)
} else {
return completion(error ?? EthereumClientError.decodeIssue, nil)
}
}
}
}

public class ERC721Metadata: ERC721 {
Expand Down

0 comments on commit b411430

Please sign in to comment.