Skip to content

Commit 33c1a38

Browse files
authored
Merge pull request #123 from keefertaylor/exchange_client
Implementation of DEXter Exchange
2 parents 5f05e9d + 54d2cbc commit 33c1a38

12 files changed

+621
-7
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright Keefer Taylor, 2019.
2+
3+
@testable import TezosKit
4+
import XCTest
5+
6+
/// Integration tests to run against a DEXter Exchange Contract. These tests require a live alphanet node.
7+
///
8+
/// To get an alphanet node running locally, follow instructions here:
9+
/// https://tezos.gitlab.io/alphanet/introduction/howtoget.html
10+
///
11+
/// These tests are not hermetic and may fail for a number or reasons, such as:
12+
/// - Insufficient balance in account.
13+
/// - Adverse network conditions.
14+
///
15+
/// Before running the tests, you should make sure that there's sufficient tokens in the owners account (which is
16+
/// tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW) and liquidity in the exchange:
17+
/// Exchange: https://alphanet.tzscan.io/KT18dHMg7xWwRvo2TA9DSkcPkaG3AkDyEeKB
18+
/// Address: https://alphanet.tzscan.io/tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW
19+
20+
extension Address {
21+
public static let exchangeContractAddress = "KT18dHMg7xWwRvo2TA9DSkcPkaG3AkDyEeKB"
22+
}
23+
24+
class DexterExchangeClientIntegrationTests: XCTestCase {
25+
public var nodeClient = TezosNodeClient()
26+
public var exchangeClient = DexterExchangeClient(exchangeContractAddress: "")
27+
28+
public override func setUp() {
29+
super.setUp()
30+
31+
let nodeClient = TezosNodeClient(remoteNodeURL: .nodeURL)
32+
exchangeClient = DexterExchangeClient(
33+
exchangeContractAddress: .exchangeContractAddress,
34+
tezosNodeClient: nodeClient
35+
)
36+
}
37+
38+
public func testGetBalanceTez() {
39+
let completionExpectation = XCTestExpectation(description: "Completion called")
40+
41+
exchangeClient.getExchangeBalanceTez { result in
42+
guard case let .success(balance) = result else {
43+
XCTFail()
44+
return
45+
}
46+
47+
XCTAssert(balance > Tez.zeroBalance)
48+
completionExpectation.fulfill()
49+
}
50+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
51+
}
52+
53+
public func testGetBalanceTokens() {
54+
let completionExpectation = XCTestExpectation(description: "Completion called")
55+
56+
exchangeClient.getExchangeBalanceTokens(tokenContractAddress: .tokenContractAddress) { result in
57+
guard case let .success(balance) = result else {
58+
XCTFail()
59+
return
60+
}
61+
62+
XCTAssert(balance > 0)
63+
completionExpectation.fulfill()
64+
}
65+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
66+
}
67+
68+
public func testGetExchangeLiquidity() {
69+
let completionExpectation = XCTestExpectation(description: "Completion called")
70+
71+
exchangeClient.getExchangeLiquidity { result in
72+
guard case let .success(liquidity) = result else {
73+
XCTFail()
74+
return
75+
}
76+
77+
XCTAssert(liquidity > 0)
78+
completionExpectation.fulfill()
79+
}
80+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
81+
}
82+
83+
public func testAddLiquidity() {
84+
let completionExpectation = XCTestExpectation(description: "Completion called")
85+
86+
let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future
87+
exchangeClient.addLiquidity(
88+
from: Wallet.testWallet.address,
89+
amount: Tez(10.0),
90+
signatureProvider: Wallet.testWallet,
91+
minLiquidity: 1,
92+
maxTokensDeposited: 10,
93+
deadline: deadline
94+
) { result in
95+
switch result {
96+
case .failure(let error):
97+
print(error)
98+
XCTFail()
99+
case .success(let hash):
100+
print(hash)
101+
completionExpectation.fulfill()
102+
}
103+
}
104+
105+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
106+
}
107+
108+
public func testRemoveLiquidity() {
109+
let completionExpectation = XCTestExpectation(description: "Completion called")
110+
111+
let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future
112+
exchangeClient.withdrawLiquidity(
113+
from: Wallet.testWallet.address,
114+
signatureProvider: Wallet.testWallet,
115+
liquidityBurned: 100,
116+
tezToWidthdraw: Tez(0.000_001),
117+
minTokensToWithdraw: 1,
118+
deadline: deadline
119+
) { result in
120+
switch result {
121+
case .failure(let error):
122+
print(error)
123+
XCTFail()
124+
case .success(let hash):
125+
print(hash)
126+
completionExpectation.fulfill()
127+
}
128+
}
129+
130+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
131+
}
132+
133+
public func testTradeTezForToken() {
134+
let completionExpectation = XCTestExpectation(description: "Completion called")
135+
136+
let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future
137+
138+
exchangeClient.tradeTezForToken(
139+
source: Wallet.testWallet.address,
140+
amount: Tez(10.0),
141+
signatureProvider: Wallet.testWallet,
142+
minTokensToPurchase: 1,
143+
deadline: deadline
144+
) { result in
145+
switch result {
146+
case .failure(let error):
147+
print(error)
148+
XCTFail()
149+
case .success(let hash):
150+
print(hash)
151+
completionExpectation.fulfill()
152+
}
153+
}
154+
155+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
156+
}
157+
158+
func testTradeTokenForTez() {
159+
let completionExpectation = XCTestExpectation(description: "Completion called")
160+
161+
let deadline = Date().addingTimeInterval(24 * 60 * 60) // 24 hours in the future
162+
163+
exchangeClient.tradeTokenForTez(
164+
source: Wallet.testWallet.address,
165+
signatureProvider: Wallet.testWallet,
166+
tokensToSell: 1,
167+
minTezToBuy: Tez(0.000_001),
168+
deadline: deadline
169+
) { result in
170+
switch result {
171+
case .failure(let error):
172+
print(error)
173+
XCTFail()
174+
case .success(let hash):
175+
print(hash)
176+
completionExpectation.fulfill()
177+
}
178+
}
179+
180+
wait(for: [ completionExpectation ], timeout: .expectationTimeout)
181+
}
182+
}

IntegrationTests/Dexter/TokenContractIntegrationTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
@testable import TezosKit
44
import XCTest
55

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

1920
extension Address {
2021
public static let tokenContractAddress = "KT1WiDkoaKgH6dcmHa3tLJKzfnW5QuPjppgn"
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright Keefer Taylor, 2019.
2+
3+
@testable import TezosKit
4+
import XCTest
5+
6+
final class DexterExchangeClientTests: XCTestCase {
7+
private var exchangeClient: DexterExchangeClient?
8+
9+
override func setUp() {
10+
super.setUp()
11+
12+
let contract = Address.testExchangeContractAddress
13+
let networkClient = FakeNetworkClient.tezosNodeNetworkClient
14+
15+
let tezosNodeClient = TezosNodeClient(networkClient: networkClient)
16+
exchangeClient = DexterExchangeClient(
17+
exchangeContractAddress: contract,
18+
tezosNodeClient: tezosNodeClient
19+
)
20+
}
21+
22+
func testGetExchangeLiquidity() {
23+
let expectation = XCTestExpectation(description: "completion called")
24+
25+
exchangeClient?.getExchangeLiquidity { result in
26+
switch result {
27+
case .success:
28+
expectation.fulfill()
29+
case .failure:
30+
XCTFail()
31+
}
32+
}
33+
34+
wait(for: [expectation], timeout: .expectationTimeout)
35+
}
36+
37+
func testGetExchangeBalanceTokens() {
38+
let expectation = XCTestExpectation(description: "completion called")
39+
40+
exchangeClient?.getExchangeBalanceTokens(tokenContractAddress: .testTokenContractAddress) { result in
41+
switch result {
42+
case .success:
43+
expectation.fulfill()
44+
case .failure:
45+
XCTFail()
46+
}
47+
}
48+
49+
wait(for: [expectation], timeout: .expectationTimeout)
50+
}
51+
52+
func testGetExchangeBalanceTez() {
53+
let expectation = XCTestExpectation(description: "completion called")
54+
55+
exchangeClient?.getExchangeBalanceTez { result in
56+
switch result {
57+
case .success:
58+
expectation.fulfill()
59+
case .failure:
60+
XCTFail()
61+
}
62+
}
63+
64+
wait(for: [expectation], timeout: .expectationTimeout)
65+
}
66+
67+
func testAddLiquidity() {
68+
let expectation = XCTestExpectation(description: "completion called")
69+
70+
exchangeClient?.addLiquidity(
71+
from: Address.testAddress,
72+
amount: Tez(1.0),
73+
signatureProvider: FakeSignatureProvider.testSignatureProvider,
74+
minLiquidity: 1,
75+
maxTokensDeposited: 1,
76+
deadline: Date()
77+
) { result in
78+
switch result {
79+
case .success:
80+
expectation.fulfill()
81+
case .failure:
82+
XCTFail()
83+
}
84+
}
85+
86+
wait(for: [expectation], timeout: .expectationTimeout)
87+
}
88+
89+
func testWithdrawLiquidity() {
90+
let expectation = XCTestExpectation(description: "completion called")
91+
92+
exchangeClient?.withdrawLiquidity(
93+
from: Address.testAddress,
94+
signatureProvider: FakeSignatureProvider.testSignatureProvider,
95+
liquidityBurned: 1,
96+
tezToWidthdraw: Tez(1.0),
97+
minTokensToWithdraw: 1,
98+
deadline: Date()
99+
) { result in
100+
switch result {
101+
case .success:
102+
expectation.fulfill()
103+
case .failure:
104+
XCTFail()
105+
}
106+
}
107+
108+
wait(for: [expectation], timeout: .expectationTimeout)
109+
}
110+
111+
func testTradeTezToTokens() {
112+
let expectation = XCTestExpectation(description: "completion called")
113+
114+
exchangeClient?.tradeTezForToken(
115+
source: .testAddress,
116+
amount: Tez(1.0),
117+
signatureProvider: FakeSignatureProvider.testSignatureProvider,
118+
minTokensToPurchase: 1,
119+
deadline: Date()
120+
) { result in
121+
switch result {
122+
case .success:
123+
expectation.fulfill()
124+
case .failure:
125+
XCTFail()
126+
}
127+
}
128+
129+
wait(for: [expectation], timeout: .expectationTimeout)
130+
}
131+
132+
func testTradeTokensForTez() {
133+
let expectation = XCTestExpectation(description: "completion called")
134+
135+
exchangeClient?.tradeTokenForTez(
136+
source: .testAddress,
137+
signatureProvider: FakeSignatureProvider.testSignatureProvider,
138+
tokensToSell: 1,
139+
minTezToBuy: Tez(1.0),
140+
deadline: Date()
141+
) { result in
142+
switch result {
143+
case .success:
144+
expectation.fulfill()
145+
case .failure:
146+
XCTFail()
147+
}
148+
}
149+
150+
wait(for: [expectation], timeout: .expectationTimeout)
151+
}
152+
}

Tests/TestObjects.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ extension String {
1313
public static let testSignature = "edsigabc123"
1414
public static let testAddress = "tz1abc123xyz"
1515
public static let testTokenContractAddress = "tz1tokencontract"
16+
public static let testExchangeContractAddress = "tz1exchangecontract"
1617
public static let testDestinationAddress = "tz1destination"
1718
public static let testForgeResult = "test_forge_result"
1819
public static let testPublicKey = "edpk_test"
@@ -167,7 +168,9 @@ extension FakeNetworkClient {
167168
"/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",
168169
"/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",
169170
"/injection/operation": "\"ooTransactionHash\"",
170-
"/chains/main/blocks/head/context/contracts/tz1tokencontract/big_map_get": "{\"args\":[{\"int\":\"999\"},[]],\"prim\":\"Pair\"}"
171+
"/chains/main/blocks/head/context/contracts/tz1tokencontract/big_map_get": "{\"args\":[{\"int\":\"999\"},[]],\"prim\":\"Pair\"}",
172+
"/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\"},[]]}]}]}",
173+
"/chains/main/blocks/head/context/contracts/tz1exchangecontract/balance": "\"100\""
171174
]
172175

173176
public static let tezosNodeNetworkClient =
File renamed without changes.

Tests/TezosKit/GetContractStorageRPCTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ final class GetContractStorageRPCTest: XCTestCase {
88
let address = "abc123"
99
let rpc = GetContractStorageRPC(address: address)
1010

11-
XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/context/contracts/\(address)/storage")
11+
XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/\(address)/storage")
1212
XCTAssertNil(rpc.payload)
1313
XCTAssertFalse(rpc.isPOSTRequest)
1414
}

Tests/TezosKit/MichelsonTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ final class MichelsonTests: XCTestCase {
4646
XCTAssertEqual(encoded, Helpers.orderJSONString(MichelsonTests.expectedMichelsonUnitEncoding))
4747
}
4848

49+
func testEncodeDateToJSON() {
50+
let date = Date(timeIntervalSince1970: 1_593_453_621) // Monday, June 29, 2020 6:00:21 PM, GMT
51+
let michelson = StringMichelsonParameter(date: date)
52+
let encoded = JSONUtils.jsonString(for: michelson.networkRepresentation)
53+
XCTAssertEqual(encoded, Helpers.orderJSONString("{\"string\":\"2020-06-29T18:00:21Z\"}"))
54+
}
55+
4956
func testEncodeStringToJSON() {
5057
let michelson = MichelsonTests.michelsonString
5158
let encoded = JSONUtils.jsonString(for: michelson.networkRepresentation)

0 commit comments

Comments
 (0)