Skip to content

Commit

Permalink
Merge pull request #123 from keefertaylor/exchange_client
Browse files Browse the repository at this point in the history
Implementation of DEXter Exchange
  • Loading branch information
keefertaylor authored Sep 19, 2019
2 parents 5f05e9d + 54d2cbc commit 33c1a38
Show file tree
Hide file tree
Showing 12 changed files with 621 additions and 7 deletions.
182 changes: 182 additions & 0 deletions IntegrationTests/Dexter/DexterExchangeClientIntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright Keefer Taylor, 2019.

@testable import TezosKit
import XCTest

/// Integration tests to run against a DEXter Exchange Contract. These tests require a live alphanet node.
///
/// To get an alphanet node running locally, follow instructions here:
/// https://tezos.gitlab.io/alphanet/introduction/howtoget.html
///
/// These tests are not hermetic and may fail for a number or reasons, such as:
/// - Insufficient balance in account.
/// - Adverse network conditions.
///
/// Before running the tests, you should make sure that there's sufficient tokens in the owners account (which is
/// tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW) and liquidity in the exchange:
/// Exchange: https://alphanet.tzscan.io/KT18dHMg7xWwRvo2TA9DSkcPkaG3AkDyEeKB
/// Address: https://alphanet.tzscan.io/tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW

extension Address {
public static let exchangeContractAddress = "KT18dHMg7xWwRvo2TA9DSkcPkaG3AkDyEeKB"
}

class DexterExchangeClientIntegrationTests: XCTestCase {
public var nodeClient = TezosNodeClient()
public var exchangeClient = DexterExchangeClient(exchangeContractAddress: "")

public override func setUp() {
super.setUp()

let nodeClient = TezosNodeClient(remoteNodeURL: .nodeURL)
exchangeClient = DexterExchangeClient(
exchangeContractAddress: .exchangeContractAddress,
tezosNodeClient: nodeClient
)
}

public func testGetBalanceTez() {
let completionExpectation = XCTestExpectation(description: "Completion called")

exchangeClient.getExchangeBalanceTez { result in
guard case let .success(balance) = result else {
XCTFail()
return
}

XCTAssert(balance > Tez.zeroBalance)
completionExpectation.fulfill()
}
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}

public func testGetBalanceTokens() {
let completionExpectation = XCTestExpectation(description: "Completion called")

exchangeClient.getExchangeBalanceTokens(tokenContractAddress: .tokenContractAddress) { result in
guard case let .success(balance) = result else {
XCTFail()
return
}

XCTAssert(balance > 0)
completionExpectation.fulfill()
}
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}

public func testGetExchangeLiquidity() {
let completionExpectation = XCTestExpectation(description: "Completion called")

exchangeClient.getExchangeLiquidity { result in
guard case let .success(liquidity) = result else {
XCTFail()
return
}

XCTAssert(liquidity > 0)
completionExpectation.fulfill()
}
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}

public func testAddLiquidity() {
let completionExpectation = XCTestExpectation(description: "Completion called")

let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future
exchangeClient.addLiquidity(
from: Wallet.testWallet.address,
amount: Tez(10.0),
signatureProvider: Wallet.testWallet,
minLiquidity: 1,
maxTokensDeposited: 10,
deadline: deadline
) { result in
switch result {
case .failure(let error):
print(error)
XCTFail()
case .success(let hash):
print(hash)
completionExpectation.fulfill()
}
}

wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}

public func testRemoveLiquidity() {
let completionExpectation = XCTestExpectation(description: "Completion called")

let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future
exchangeClient.withdrawLiquidity(
from: Wallet.testWallet.address,
signatureProvider: Wallet.testWallet,
liquidityBurned: 100,
tezToWidthdraw: Tez(0.000_001),
minTokensToWithdraw: 1,
deadline: deadline
) { result in
switch result {
case .failure(let error):
print(error)
XCTFail()
case .success(let hash):
print(hash)
completionExpectation.fulfill()
}
}

wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}

public func testTradeTezForToken() {
let completionExpectation = XCTestExpectation(description: "Completion called")

let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future

exchangeClient.tradeTezForToken(
source: Wallet.testWallet.address,
amount: Tez(10.0),
signatureProvider: Wallet.testWallet,
minTokensToPurchase: 1,
deadline: deadline
) { result in
switch result {
case .failure(let error):
print(error)
XCTFail()
case .success(let hash):
print(hash)
completionExpectation.fulfill()
}
}

wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}

func testTradeTokenForTez() {
let completionExpectation = XCTestExpectation(description: "Completion called")

let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future

exchangeClient.tradeTokenForTez(
source: Wallet.testWallet.address,
signatureProvider: Wallet.testWallet,
tokensToSell: 1,
minTezToBuy: Tez(0.000_001),
deadline: deadline
) { result in
switch result {
case .failure(let error):
print(error)
XCTFail()
case .success(let hash):
print(hash)
completionExpectation.fulfill()
}
}

wait(for: [ completionExpectation ], timeout: .expectationTimeout)
}
}
5 changes: 3 additions & 2 deletions IntegrationTests/Dexter/TokenContractIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@testable import TezosKit
import XCTest

/// Integration tests to run against a Dexter Token Contract. These tests require a live alphanet node.
/// Integration tests to run against a DEXter Token Contract. These tests require a live alphanet node.
///
/// To get an alphanet node running locally, follow instructions here:
/// https://tezos.gitlab.io/alphanet/introduction/howtoget.html
Expand All @@ -14,7 +14,8 @@ import XCTest
///
/// Before running the tests, you should make sure that there's sufficient tokens in the owners account (which is
/// tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW) in the token contract at:
/// https://alphanet.tzscan.io/KT1PARMPddZ9WD1MPmPthXYBCgErmxAHKBD8
/// Token Contract: https://alphanet.tzscan.io/KT1PARMPddZ9WD1MPmPthXYBCgErmxAHKBD8
/// Address: https://alphanet.tzscan.io/tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW

extension Address {
public static let tokenContractAddress = "KT1WiDkoaKgH6dcmHa3tLJKzfnW5QuPjppgn"
Expand Down
152 changes: 152 additions & 0 deletions Tests/Dexter/DexterExchangeClientTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright Keefer Taylor, 2019.

@testable import TezosKit
import XCTest

final class DexterExchangeClientTests: XCTestCase {
private var exchangeClient: DexterExchangeClient?

override func setUp() {
super.setUp()

let contract = Address.testExchangeContractAddress
let networkClient = FakeNetworkClient.tezosNodeNetworkClient

let tezosNodeClient = TezosNodeClient(networkClient: networkClient)
exchangeClient = DexterExchangeClient(
exchangeContractAddress: contract,
tezosNodeClient: tezosNodeClient
)
}

func testGetExchangeLiquidity() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.getExchangeLiquidity { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testGetExchangeBalanceTokens() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.getExchangeBalanceTokens(tokenContractAddress: .testTokenContractAddress) { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testGetExchangeBalanceTez() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.getExchangeBalanceTez { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testAddLiquidity() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.addLiquidity(
from: Address.testAddress,
amount: Tez(1.0),
signatureProvider: FakeSignatureProvider.testSignatureProvider,
minLiquidity: 1,
maxTokensDeposited: 1,
deadline: Date()
) { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testWithdrawLiquidity() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.withdrawLiquidity(
from: Address.testAddress,
signatureProvider: FakeSignatureProvider.testSignatureProvider,
liquidityBurned: 1,
tezToWidthdraw: Tez(1.0),
minTokensToWithdraw: 1,
deadline: Date()
) { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testTradeTezToTokens() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.tradeTezForToken(
source: .testAddress,
amount: Tez(1.0),
signatureProvider: FakeSignatureProvider.testSignatureProvider,
minTokensToPurchase: 1,
deadline: Date()
) { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}

func testTradeTokensForTez() {
let expectation = XCTestExpectation(description: "completion called")

exchangeClient?.tradeTokenForTez(
source: .testAddress,
signatureProvider: FakeSignatureProvider.testSignatureProvider,
tokensToSell: 1,
minTezToBuy: Tez(1.0),
deadline: Date()
) { result in
switch result {
case .success:
expectation.fulfill()
case .failure:
XCTFail()
}
}

wait(for: [expectation], timeout: .expectationTimeout)
}
}
5 changes: 4 additions & 1 deletion Tests/TestObjects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extension String {
public static let testSignature = "edsigabc123"
public static let testAddress = "tz1abc123xyz"
public static let testTokenContractAddress = "tz1tokencontract"
public static let testExchangeContractAddress = "tz1exchangecontract"
public static let testDestinationAddress = "tz1destination"
public static let testForgeResult = "test_forge_result"
public static let testPublicKey = "edpk_test"
Expand Down Expand Up @@ -167,7 +168,9 @@ extension FakeNetworkClient {
"/chains/main/blocks/" + .testBranch + "/helpers/preapply/operations": "[{\"contents\":[{\"kind\":\"transaction\",\"source\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"fee\":\"1272\",\"counter\":\"30801\",\"gas_limit\":\"10100\",\"storage_limit\":\"257\",\"amount\":\"1\",\"destination\":\"tz3WXYtyDUNL91qfiCJtVUX746QpNv5i5ve5\",\"metadata\":{\"balance_updates\":[{\"kind\":\"contract\",\"contract\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"change\":\"-1272\"},{\"kind\":\"freezer\",\"category\":\"fees\",\"delegate\":\"tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU\",\"level\":125,\"change\":\"1272\"}],\"operation_result\":{\"status\":\"applied\",\"balance_updates\":[{\"kind\":\"contract\",\"contract\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"change\":\"-1\"},{\"kind\":\"contract\",\"contract\":\"tz3WXYtyDUNL91qfiCJtVUX746QpNv5i5ve5\",\"change\":\"1\"}],\"consumed_gas\":\"10100\"}}}],\"signature\":\"edsigtpsh2VpWyZTZ46q9j54VfsWZLZuxL7UGEhfgCNx6SXwaWu4gMHx59bRdogbSmDCCpXeQeighgpHk5x32k3rtFu8w5EZyEr\"}]\n",
"/chains/main/blocks/head/helpers/scripts/run_operation": "{\"contents\":[{\"kind\":\"origination\",\"source\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"fee\":\"1265\",\"counter\":\"31038\",\"gas_limit\":\"10000\",\"storage_limit\":\"257\",\"manager_pubkey\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"balance\":\"0\",\"metadata\":{\"balance_updates\":[{\"kind\":\"contract\",\"contract\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"change\":\"-1265\"},{\"kind\":\"freezer\",\"category\":\"fees\",\"delegate\":\"tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU\",\"cycle\":247,\"change\":\"1265\"}],\"operation_result\":{\"status\":\"applied\",\"balance_updates\":[{\"kind\":\"contract\",\"contract\":\"tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW\",\"change\":\"-257000\"}],\"originated_contracts\":[\"KT1RAHAXehUNusndqZpcxM8SfCjLi83utZsR\"],\"consumed_gas\":\"10000\"}}}]}\n",
"/injection/operation": "\"ooTransactionHash\"",
"/chains/main/blocks/head/context/contracts/tz1tokencontract/big_map_get": "{\"args\":[{\"int\":\"999\"},[]],\"prim\":\"Pair\"}"
"/chains/main/blocks/head/context/contracts/tz1tokencontract/big_map_get": "{\"args\":[{\"int\":\"999\"},[]],\"prim\":\"Pair\"}",
"/chains/main/blocks/head/context/contracts/tz1exchangecontract/storage": "{\"prim\":\"Pair\",\"args\":[[],{\"prim\":\"Pair\",\"args\":[{\"prim\":\"Pair\",\"args\":[{\"string\":\"KT1VsiG5djAjLqZcjEpXBxWEv1ocuW178Psa\"},{\"string\":\"KT1WiDkoaKgH6dcmHa3tLJKzfnW5QuPjppgn\"}]},{\"prim\":\"Pair\",\"args\":[{\"int\":\"1089999900\"},[]]}]}]}",
"/chains/main/blocks/head/context/contracts/tz1exchangecontract/balance": "\"100\""
]

public static let tezosNodeNetworkClient =
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion Tests/TezosKit/GetContractStorageRPCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ final class GetContractStorageRPCTest: XCTestCase {
let address = "abc123"
let rpc = GetContractStorageRPC(address: address)

XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/context/contracts/\(address)/storage")
XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/\(address)/storage")
XCTAssertNil(rpc.payload)
XCTAssertFalse(rpc.isPOSTRequest)
}
Expand Down
7 changes: 7 additions & 0 deletions Tests/TezosKit/MichelsonTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ final class MichelsonTests: XCTestCase {
XCTAssertEqual(encoded, Helpers.orderJSONString(MichelsonTests.expectedMichelsonUnitEncoding))
}

func testEncodeDateToJSON() {
let date = Date(timeIntervalSince1970: 1_593_453_621) // Monday, June 29, 2020 6:00:21 PM, GMT
let michelson = StringMichelsonParameter(date: date)
let encoded = JSONUtils.jsonString(for: michelson.networkRepresentation)
XCTAssertEqual(encoded, Helpers.orderJSONString("{\"string\":\"2020-06-29T18:00:21Z\"}"))
}

func testEncodeStringToJSON() {
let michelson = MichelsonTests.michelsonString
let encoded = JSONUtils.jsonString(for: michelson.networkRepresentation)
Expand Down
Loading

0 comments on commit 33c1a38

Please sign in to comment.