Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.8.0 #143

Merged
merged 5 commits into from
Jun 11, 2021
Merged

0.8.0 #143

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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