hive-stream 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/AGENTS.md +35 -0
  2. package/DOCUMENTATION.md +396 -0
  3. package/README.md +160 -39
  4. package/dist/actions.d.ts +3 -3
  5. package/dist/actions.js +7 -7
  6. package/dist/actions.js.map +1 -1
  7. package/dist/adapters/base.adapter.d.ts +19 -1
  8. package/dist/adapters/base.adapter.js +16 -0
  9. package/dist/adapters/base.adapter.js.map +1 -1
  10. package/dist/adapters/mongodb.adapter.d.ts +5 -11
  11. package/dist/adapters/mongodb.adapter.js +10 -10
  12. package/dist/adapters/mongodb.adapter.js.map +1 -1
  13. package/dist/adapters/postgresql.adapter.d.ts +17 -0
  14. package/dist/adapters/postgresql.adapter.js +99 -8
  15. package/dist/adapters/postgresql.adapter.js.map +1 -1
  16. package/dist/adapters/sqlite.adapter.d.ts +17 -0
  17. package/dist/adapters/sqlite.adapter.js +99 -8
  18. package/dist/adapters/sqlite.adapter.js.map +1 -1
  19. package/dist/api.js +86 -0
  20. package/dist/api.js.map +1 -1
  21. package/dist/config.d.ts +26 -0
  22. package/dist/config.js +76 -4
  23. package/dist/config.js.map +1 -1
  24. package/dist/contracts/coinflip.contract.d.ts +8 -26
  25. package/dist/contracts/coinflip.contract.js +123 -144
  26. package/dist/contracts/coinflip.contract.js.map +1 -1
  27. package/dist/contracts/contract.d.ts +3 -0
  28. package/dist/contracts/contract.js +26 -0
  29. package/dist/contracts/contract.js.map +1 -0
  30. package/dist/contracts/dice.contract.d.ts +9 -36
  31. package/dist/contracts/dice.contract.js +135 -200
  32. package/dist/contracts/dice.contract.js.map +1 -1
  33. package/dist/contracts/exchange.contract.d.ts +11 -0
  34. package/dist/contracts/exchange.contract.js +492 -0
  35. package/dist/contracts/exchange.contract.js.map +1 -0
  36. package/dist/contracts/lotto.contract.d.ts +15 -19
  37. package/dist/contracts/lotto.contract.js +154 -162
  38. package/dist/contracts/lotto.contract.js.map +1 -1
  39. package/dist/contracts/nft.contract.d.ts +4 -0
  40. package/dist/contracts/nft.contract.js +65 -0
  41. package/dist/contracts/nft.contract.js.map +1 -1
  42. package/dist/contracts/poll.contract.d.ts +4 -0
  43. package/dist/contracts/poll.contract.js +105 -0
  44. package/dist/contracts/poll.contract.js.map +1 -0
  45. package/dist/contracts/rps.contract.d.ts +9 -0
  46. package/dist/contracts/rps.contract.js +217 -0
  47. package/dist/contracts/rps.contract.js.map +1 -0
  48. package/dist/contracts/tipjar.contract.d.ts +4 -0
  49. package/dist/contracts/tipjar.contract.js +60 -0
  50. package/dist/contracts/tipjar.contract.js.map +1 -0
  51. package/dist/contracts/token.contract.d.ts +3 -17
  52. package/dist/contracts/token.contract.js +128 -80
  53. package/dist/contracts/token.contract.js.map +1 -1
  54. package/dist/exchanges/coingecko.d.ts +7 -1
  55. package/dist/exchanges/coingecko.js +38 -21
  56. package/dist/exchanges/coingecko.js.map +1 -1
  57. package/dist/exchanges/exchange.d.ts +15 -8
  58. package/dist/exchanges/exchange.js +65 -11
  59. package/dist/exchanges/exchange.js.map +1 -1
  60. package/dist/hive-rates.d.ts +29 -4
  61. package/dist/hive-rates.js +179 -92
  62. package/dist/hive-rates.js.map +1 -1
  63. package/dist/index.d.ts +11 -3
  64. package/dist/index.js +19 -4
  65. package/dist/index.js.map +1 -1
  66. package/dist/metadata.d.ts +63 -0
  67. package/dist/metadata.js +407 -0
  68. package/dist/metadata.js.map +1 -0
  69. package/dist/streamer.d.ts +104 -11
  70. package/dist/streamer.js +415 -143
  71. package/dist/streamer.js.map +1 -1
  72. package/dist/test.js +11 -12
  73. package/dist/test.js.map +1 -1
  74. package/dist/types/hive-stream.d.ts +85 -14
  75. package/dist/types/rates.d.ts +47 -0
  76. package/dist/types/rates.js +29 -0
  77. package/dist/types/rates.js.map +1 -0
  78. package/dist/utils.d.ts +318 -11
  79. package/dist/utils.js +804 -115
  80. package/dist/utils.js.map +1 -1
  81. package/examples/contracts/README.md +8 -0
  82. package/examples/contracts/exchange.ts +38 -0
  83. package/examples/contracts/poll.ts +21 -0
  84. package/examples/contracts/rps.ts +19 -0
  85. package/examples/contracts/tipjar.ts +19 -0
  86. package/package.json +20 -19
  87. package/test-contract-block.md +3 -3
  88. package/tests/actions.spec.ts +7 -7
  89. package/tests/adapters/actions-persistence.spec.ts +4 -4
  90. package/tests/adapters/sqlite.adapter.spec.ts +2 -2
  91. package/tests/config-input.spec.ts +90 -0
  92. package/tests/contracts/coinflip.contract.spec.ts +26 -154
  93. package/tests/contracts/dice.contract.spec.ts +24 -140
  94. package/tests/contracts/exchange.contract.spec.ts +84 -0
  95. package/tests/contracts/lotto.contract.spec.ts +30 -295
  96. package/tests/contracts/token.contract.spec.ts +72 -316
  97. package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
  98. package/tests/exchanges/exchange.base.spec.ts +246 -0
  99. package/tests/helpers/mock-fetch.ts +165 -0
  100. package/tests/hive-chain-features.spec.ts +319 -0
  101. package/tests/hive-rates.spec.ts +443 -0
  102. package/tests/integration/hive-rates.integration.spec.ts +35 -0
  103. package/tests/metadata.spec.ts +63 -0
  104. package/tests/streamer-actions.spec.ts +29 -18
  105. package/tests/streamer.spec.ts +142 -49
  106. package/tests/types/rates.spec.ts +216 -0
  107. package/tests/utils.spec.ts +27 -6
@@ -1,10 +1,8 @@
1
- import { CoinflipContract } from '../../src/contracts/coinflip.contract';
1
+ import { createCoinflipContract } from '../../src/contracts/coinflip.contract';
2
2
  import { sleep } from '@hiveio/dhive/lib/utils';
3
3
  import { Streamer } from '../../src/streamer';
4
4
  import { createMockAdapter } from '../helpers/mock-adapter';
5
- import BigNumber from 'bignumber.js';
6
5
 
7
- // Mock uuid module at the top level
8
6
  jest.mock('uuid', () => ({
9
7
  v4: jest.fn()
10
8
  }));
@@ -13,13 +11,12 @@ import { v4 as uuidv4 } from 'uuid';
13
11
 
14
12
  describe('Coinflip Contract', () => {
15
13
  let sut: Streamer;
16
- let contract: CoinflipContract;
14
+ let contract: ReturnType<typeof createCoinflipContract>;
17
15
 
18
16
  beforeEach(async () => {
19
17
  sut = new Streamer();
20
18
  await sut.registerAdapter(createMockAdapter());
21
-
22
- contract = new CoinflipContract();
19
+ contract = createCoinflipContract();
23
20
 
24
21
  // @ts-ignore
25
22
  sut.api = jest.fn();
@@ -27,40 +24,29 @@ describe('Coinflip Contract', () => {
27
24
 
28
25
  afterEach(async () => {
29
26
  await sut.stop();
30
- });
31
-
32
- afterEach(() => {
33
27
  jest.restoreAllMocks();
34
28
  });
35
29
 
36
- afterAll(() => {
37
- jest.resetAllMocks();
38
- });
39
-
40
- test('Registers the contract', () => {
41
- sut.registerContract('coinflip', contract);
30
+ test('Registers the contract', async () => {
31
+ await sut.registerContract(contract);
42
32
 
43
33
  const findContract = sut['contracts'].find(c => c.name === 'coinflip');
44
-
45
34
  expect(findContract).not.toBeUndefined();
46
35
  });
47
36
 
48
37
  test('User wins a flip', async () => {
49
- sut.registerContract('coinflip', contract);
38
+ await sut.registerContract(contract);
50
39
 
51
- contract['_instance'] = sut;
52
-
53
- jest.spyOn(contract as any, 'flip');
54
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
55
-
56
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
40
+ jest.spyOn(sut, 'getTransaction').mockResolvedValue({ test: 123 } as any);
57
41
  jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
58
42
  jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
43
+ jest.spyOn(sut['client'].database, 'getAccounts').mockResolvedValue([{ balance: '2000.000 HIVE' }] as any);
44
+
45
+ (uuidv4 as jest.Mock).mockReturnValue('j93jgsjghjdhgjfhgkfdhgkj34872394723');
59
46
 
60
47
  const memo = JSON.stringify({
61
- hivePayload: {
62
- id: 'hivestream',
63
- name: 'coinflip',
48
+ hive_stream: {
49
+ contract: 'coinflip',
64
50
  action: 'flip',
65
51
  payload: {
66
52
  guess: 'heads',
@@ -69,154 +55,40 @@ describe('Coinflip Contract', () => {
69
55
  }
70
56
  });
71
57
 
72
- (uuidv4 as jest.Mock).mockReturnValue('j93jgsjghjdhgjfhgkfdhgkj34872394723');
73
-
74
- sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjs7878dkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
58
+ await sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjs7878dkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
75
59
 
76
60
  await sleep(100);
77
61
 
78
- expect(contract['flip']).toBeCalled();
79
- expect(contract['_instance'].getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
80
- expect(contract['_instance'].transferHiveTokens).toBeCalledWith('beggars', 'testuser', '18.000', 'HIVE', '[Winner] | Guess: heads | Server Roll: heads | Previous block id: fkjs7878dkfj | BlockID: dfjfsdfsdfs4hfkj88787 | Trx ID: fhkjsdhfkjsdf | Server Seed: j93jgsjghjdhgjfhgkfdhgkj34872394723');
62
+ expect(sut.getTransaction).toHaveBeenCalledWith(778782, 'fhkjsdhfkjsdf');
63
+ expect(sut.transferHiveTokens).toHaveBeenCalledWith('beggars', 'testuser', '18.000', 'HIVE', '[Winner] | Guess: heads | Server Roll: heads | Previous block id: fkjs7878dkfj | BlockID: dfjfsdfsdfs4hfkj88787 | Trx ID: fhkjsdhfkjsdf | Server Seed: j93jgsjghjdhgjfhgkfdhgkj34872394723');
81
64
  });
82
65
 
83
66
  test('User loses a flip', async () => {
84
- sut.registerContract('coinflip', contract);
85
-
86
- contract['_instance'] = sut;
67
+ await sut.registerContract(contract);
87
68
 
88
- jest.spyOn(contract as any, 'flip');
89
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
90
-
91
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
69
+ jest.spyOn(sut, 'getTransaction').mockResolvedValue({ test: 123 } as any);
92
70
  jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
93
71
  jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
94
-
95
- const memo = JSON.stringify({
96
- hivePayload: {
97
- id: 'hivestream',
98
- name: 'coinflip',
99
- action: 'flip',
100
- payload: {
101
- guess: 'heads',
102
- seed: 'tulips'
103
- }
104
- }
105
- });
72
+ jest.spyOn(sut['client'].database, 'getAccounts').mockResolvedValue([{ balance: '2000.000 HIVE' }] as any);
106
73
 
107
74
  (uuidv4 as jest.Mock).mockReturnValue('j93jgsjghjdhgjfhgkfdhgkj34872394723');
108
75
 
109
- sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjs7878dkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
110
-
111
- await sleep(100);
112
-
113
- expect(contract['flip']).toBeCalled();
114
- expect(contract['_instance'].getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
115
- expect(contract['_instance'].transferHiveTokens).toBeCalledWith('beggars', 'testuser', '0.001', 'HIVE', '[Lost] | Guess: heads | Server Roll: tails | Previous block id: fkjs7878dkfj | BlockID: dfjfsdfsdfs4hfkj88787 | Trx ID: fhkjsdhfkjsdf | Server Seed: j93jgsjghjdhgjfhgkfdhgkj34872394723');
116
- });
117
-
118
- test('User sent an unsupported currency, refund them', async () => {
119
- sut.registerContract('coinflip', contract);
120
-
121
- contract['_instance'] = sut;
122
-
123
- jest.spyOn(contract as any, 'flip');
124
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
125
-
126
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
127
- jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
128
- jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
129
-
130
76
  const memo = JSON.stringify({
131
- hivePayload: {
132
- id: 'hivestream',
133
- name: 'coinflip',
77
+ hive_stream: {
78
+ contract: 'coinflip',
134
79
  action: 'flip',
135
80
  payload: {
136
- guess: 'heads'
137
- }
138
- }
139
- });
140
-
141
- sut.processOperation(['transfer', { from: 'testuser', amount: '10.000 HBD', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
142
-
143
- await sleep(100);
144
-
145
- expect(contract['flip']).toBeCalled();
146
- expect(sut.getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
147
- expect(sut.transferHiveTokens).toBeCalledWith('beggars', 'testuser', '10.000', 'HBD', '[Refund] You sent an invalid currency.');
148
- });
149
-
150
- test('Server cannot afford payout, refund user', async () => {
151
- sut.registerContract('coinflip', contract);
152
-
153
- contract['_instance'] = sut;
154
-
155
- jest.spyOn(contract as any, 'flip');
156
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(10)); // Low balance
157
-
158
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
159
- jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
160
- jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
161
-
162
- const memo = JSON.stringify({
163
- hivePayload: {
164
- id: 'hivestream',
165
- name: 'coinflip',
166
- action: 'flip',
167
- payload: {
168
- guess: 'heads'
81
+ guess: 'heads',
82
+ seed: 'tulips'
169
83
  }
170
84
  }
171
85
  });
172
86
 
173
- sut.processOperation(['transfer', { from: 'testuser', amount: '15.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
87
+ await sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjs7878dkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
174
88
 
175
89
  await sleep(100);
176
90
 
177
- expect(contract['flip']).toBeCalled();
178
- expect(sut.getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
179
- expect(sut.transferHiveTokens).toBeCalledWith('beggars', 'testuser', '15.000', 'HIVE', '[Refund] The server cannot afford this bet payout.');
180
- });
181
-
182
- test('Queue processes multiple concurrent bets', async () => {
183
- sut.registerContract('coinflip', contract);
184
-
185
- contract['_instance'] = sut;
186
-
187
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
188
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
189
- jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
190
- jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
191
-
192
- const memo1 = JSON.stringify({
193
- hivePayload: {
194
- id: 'hivestream',
195
- name: 'coinflip',
196
- action: 'flip',
197
- payload: { guess: 'heads', seed: 'seed1' }
198
- }
199
- });
200
-
201
- const memo2 = JSON.stringify({
202
- hivePayload: {
203
- id: 'hivestream',
204
- name: 'coinflip',
205
- action: 'flip',
206
- payload: { guess: 'heads', seed: 'seed2' }
207
- }
208
- });
209
-
210
- (uuidv4 as jest.Mock).mockReturnValue('test-server-seed');
211
-
212
- // Process multiple bets concurrently
213
- const bet1Promise = sut.processOperation(['transfer', { from: 'user1', amount: '5.000 HIVE', memo: memo1 }], 778782, 'block1', 'prevblock1', 'trx1', '2019-06-23' as any);
214
- const bet2Promise = sut.processOperation(['transfer', { from: 'user2', amount: '5.000 HIVE', memo: memo2 }], 778783, 'block2', 'prevblock2', 'trx2', '2019-06-23' as any);
215
-
216
- await Promise.all([bet1Promise, bet2Promise]);
217
- await sleep(200); // Allow queue processing to complete
218
-
219
- // Both transfers should have been processed
220
- expect(sut.transferHiveTokens).toHaveBeenCalledTimes(2);
91
+ expect(sut.getTransaction).toHaveBeenCalledWith(778782, 'fhkjsdhfkjsdf');
92
+ expect(sut.transferHiveTokens).toHaveBeenCalledWith('beggars', 'testuser', '0.001', 'HIVE', '[Lost] | Guess: heads | Server Roll: tails | Previous block id: fkjs7878dkfj | BlockID: dfjfsdfsdfs4hfkj88787 | Trx ID: fhkjsdhfkjsdf | Server Seed: j93jgsjghjdhgjfhgkfdhgkj34872394723');
221
93
  });
222
- });
94
+ });
@@ -1,18 +1,17 @@
1
1
  import { sleep } from '@hiveio/dhive/lib/utils';
2
- import { DiceContract } from './../../src/contracts/dice.contract';
2
+ import { createDiceContract } from './../../src/contracts/dice.contract';
3
3
  import { Streamer } from '../../src/streamer';
4
4
  import { createMockAdapter } from '../helpers/mock-adapter';
5
- import BigNumber from 'bignumber.js';
6
5
 
7
6
  describe('Dice Contract', () => {
8
7
  let sut: Streamer;
9
- let contract: DiceContract;
8
+ let contract: ReturnType<typeof createDiceContract>;
10
9
 
11
10
  beforeEach(async () => {
12
11
  sut = new Streamer();
13
12
  await sut.registerAdapter(createMockAdapter());
14
-
15
- contract = new DiceContract();
13
+
14
+ contract = createDiceContract({ name: 'testdice' });
16
15
 
17
16
  // @ts-ignore
18
17
  sut.api = jest.fn();
@@ -26,8 +25,8 @@ describe('Dice Contract', () => {
26
25
  jest.restoreAllMocks();
27
26
  });
28
27
 
29
- test('Registers the dice contract', () => {
30
- sut.registerContract('testdice', contract);
28
+ test('Registers the dice contract', async () => {
29
+ await sut.registerContract(contract);
31
30
 
32
31
  const findContract = sut['contracts'].find(c => c.name === 'testdice');
33
32
 
@@ -35,21 +34,16 @@ describe('Dice Contract', () => {
35
34
  });
36
35
 
37
36
  test('User wins a roll', async () => {
38
- sut.registerContract('testdice', contract);
39
-
40
- contract['_instance'] = sut;
37
+ await sut.registerContract(contract);
41
38
 
42
- jest.spyOn(contract as any, 'roll');
43
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
44
-
45
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
39
+ jest.spyOn(sut, 'getTransaction').mockResolvedValue({ test: 123 } as any);
46
40
  jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
47
41
  jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
42
+ jest.spyOn(sut['client'].database, 'getAccounts').mockResolvedValue([{ balance: '2000.000 HIVE' }] as any);
48
43
 
49
44
  const memo = JSON.stringify({
50
- hivePayload: {
51
- id: 'hivestream',
52
- name: 'testdice',
45
+ hive_stream: {
46
+ contract: 'testdice',
53
47
  action: 'roll',
54
48
  payload: {
55
49
  roll: 69
@@ -57,147 +51,37 @@ describe('Dice Contract', () => {
57
51
  }
58
52
  });
59
53
 
60
- sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfsd34hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
54
+ await sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfsd34hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
61
55
 
62
56
  await sleep(100);
63
57
 
64
- expect(contract['roll']).toBeCalled();
65
- expect(contract['_instance'].getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
66
- expect(contract['_instance'].transferHiveTokens).toBeCalledWith('beggars', 'testuser', '12.391', 'HIVE', 'You won 12.391 HIVE. Roll: 54, Your guess: 69');
58
+ expect(sut.getTransaction).toHaveBeenCalledWith(778782, 'fhkjsdhfkjsdf');
59
+ expect(sut.transferHiveTokens).toHaveBeenCalledWith('beggars', 'testuser', '12.391', 'HIVE', 'You won 12.391 HIVE. Roll: 54, Your guess: 69');
67
60
  });
68
61
 
69
62
  test('User loses a roll', async () => {
70
- sut.registerContract('testdice', contract);
71
-
72
- contract['_instance'] = sut;
63
+ await sut.registerContract(contract);
73
64
 
74
- jest.spyOn(contract as any, 'roll');
75
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
76
-
77
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
65
+ jest.spyOn(sut, 'getTransaction').mockResolvedValue({ test: 123 } as any);
78
66
  jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
79
67
  jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
68
+ jest.spyOn(sut['client'].database, 'getAccounts').mockResolvedValue([{ balance: '2000.000 HIVE' }] as any);
80
69
 
81
70
  const memo = JSON.stringify({
82
- hivePayload: {
83
- id: 'hivestream',
84
- name: 'testdice',
71
+ hive_stream: {
72
+ contract: 'testdice',
85
73
  action: 'roll',
86
74
  payload: {
87
- roll: 69
75
+ roll: 10
88
76
  }
89
77
  }
90
78
  });
91
79
 
92
- sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
80
+ await sut.processOperation(['transfer', { from: 'testuser', amount: '9.000 HIVE', memo }], 778782, 'dfjfsdfsdfsd34hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
93
81
 
94
82
  await sleep(100);
95
83
 
96
- expect(contract['roll']).toBeCalled();
97
- expect(sut.getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
98
- expect(sut.transferHiveTokens).toBeCalledWith('beggars', 'testuser', '0.001', 'HIVE', 'You lost 9.000 HIVE. Roll: 81, Your guess: 69');
99
- });
100
-
101
- test('User sent an invalid amount, refund them', async () => {
102
- sut.registerContract('testdice', contract);
103
-
104
- contract['_instance'] = sut;
105
-
106
- jest.spyOn(contract as any, 'roll');
107
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
108
-
109
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
110
- jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
111
- jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
112
-
113
- const memo = JSON.stringify({
114
- hivePayload: {
115
- id: 'hivestream',
116
- name: 'testdice',
117
- action: 'roll',
118
- payload: {
119
- roll: 69
120
- }
121
- }
122
- });
123
-
124
- sut.processOperation(['transfer', { from: 'testuser', amount: '100.000 HIVE', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
125
-
126
- await sleep(100);
127
-
128
- expect(contract['roll']).toBeCalled();
129
- expect(sut.getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
130
- expect(sut.transferHiveTokens).toBeCalledWith('beggars', 'testuser', '100.000', 'HIVE', '[Refund] You sent an invalid bet amount.');
131
- });
132
-
133
- test('User sent an unsupported currency, refund them', async () => {
134
- sut.registerContract('testdice', contract);
135
-
136
- contract['_instance'] = sut;
137
-
138
- jest.spyOn(contract as any, 'roll');
139
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
140
-
141
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
142
- jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
143
- jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
144
-
145
- const memo = JSON.stringify({
146
- hivePayload: {
147
- id: 'hivestream',
148
- name: 'testdice',
149
- action: 'roll',
150
- payload: {
151
- roll: 69
152
- }
153
- }
154
- });
155
-
156
- sut.processOperation(['transfer', { from: 'testuser', amount: '10.000 HBD', memo }], 778782, 'dfjfsdfsdfs4hfkj88787', 'fkjsdkfj', 'fhkjsdhfkjsdf', '2019-06-23' as any);
157
-
158
- await sleep(100);
159
-
160
- expect(contract['roll']).toBeCalled();
161
- expect(sut.getTransaction).toBeCalledWith(778782, 'fhkjsdhfkjsdf');
162
- expect(sut.transferHiveTokens).toBeCalledWith('beggars', 'testuser', '10.000', 'HBD', '[Refund] Invalid bet params.');
163
- });
164
-
165
- test('Queue processes multiple concurrent rolls', async () => {
166
- sut.registerContract('testdice', contract);
167
-
168
- contract['_instance'] = sut;
169
-
170
- jest.spyOn(contract as any, 'getBalance').mockResolvedValue(new BigNumber(2000));
171
- jest.spyOn(sut, 'getTransaction').mockResolvedValue({test: 123} as any);
172
- jest.spyOn(sut, 'verifyTransfer').mockResolvedValue(true as any);
173
- jest.spyOn(sut, 'transferHiveTokens').mockResolvedValue(true as any);
174
-
175
- const memo1 = JSON.stringify({
176
- hivePayload: {
177
- id: 'hivestream',
178
- name: 'testdice',
179
- action: 'roll',
180
- payload: { roll: 50 }
181
- }
182
- });
183
-
184
- const memo2 = JSON.stringify({
185
- hivePayload: {
186
- id: 'hivestream',
187
- name: 'testdice',
188
- action: 'roll',
189
- payload: { roll: 75 }
190
- }
191
- });
192
-
193
- // Process multiple bets concurrently
194
- const bet1Promise = sut.processOperation(['transfer', { from: 'user1', amount: '5.000 HIVE', memo: memo1 }], 778782, 'block1', 'prevblock1', 'trx1', '2019-06-23' as any);
195
- const bet2Promise = sut.processOperation(['transfer', { from: 'user2', amount: '5.000 HIVE', memo: memo2 }], 778783, 'block2', 'prevblock2', 'trx2', '2019-06-23' as any);
196
-
197
- await Promise.all([bet1Promise, bet2Promise]);
198
- await sleep(200); // Allow queue processing to complete
199
-
200
- // Both transfers should have been processed
201
- expect(sut.transferHiveTokens).toHaveBeenCalledTimes(2);
84
+ expect(sut.getTransaction).toHaveBeenCalledWith(778782, 'fhkjsdhfkjsdf');
85
+ expect(sut.transferHiveTokens).toHaveBeenCalledWith('beggars', 'testuser', '0.001', 'HIVE', 'You lost 9.000 HIVE. Roll: 54, Your guess: 10');
202
86
  });
203
- });
87
+ });
@@ -0,0 +1,84 @@
1
+ import { Streamer } from '../../src/streamer';
2
+ import { SqliteAdapter } from '../../src/adapters/sqlite.adapter';
3
+ import { createExchangeContract } from '../../src/contracts/exchange.contract';
4
+
5
+ describe('Exchange Contract', () => {
6
+ let streamer: Streamer;
7
+ let adapter: SqliteAdapter;
8
+ let contract: ReturnType<typeof createExchangeContract>;
9
+
10
+ beforeEach(async () => {
11
+ streamer = new Streamer({
12
+ JSON_ID: 'hivestream',
13
+ PAYLOAD_IDENTIFIER: 'hive_stream'
14
+ });
15
+
16
+ adapter = new SqliteAdapter(':memory:');
17
+ await streamer.registerAdapter(adapter);
18
+
19
+ contract = createExchangeContract({ name: 'exchange' });
20
+ await streamer.registerContract(contract);
21
+ });
22
+
23
+ afterEach(async () => {
24
+ await streamer.stop();
25
+ });
26
+
27
+ const baseContext = (trigger: 'custom_json' | 'transfer' | 'time', sender: string) => ({
28
+ trigger,
29
+ streamer,
30
+ adapter,
31
+ config: streamer['config'],
32
+ block: { number: 100, id: 'block', previousId: 'prev', time: new Date() },
33
+ transaction: { id: `trx-${trigger}-${sender}` },
34
+ sender,
35
+ customJson: trigger === 'custom_json' ? { id: 'hivestream', json: {}, isSignedWithActiveKey: true } : undefined,
36
+ transfer: trigger === 'transfer' ? { from: sender, to: 'exchange', rawAmount: '', amount: '', asset: '', memo: '' } : undefined
37
+ });
38
+
39
+ test('Deposits, places orders, and matches', async () => {
40
+ await contract.actions.createPair.handler({ base: 'HIVE', quote: 'HBD' }, baseContext('custom_json', 'alice'));
41
+
42
+ const aliceDepositContext = baseContext('transfer', 'alice');
43
+ aliceDepositContext.transfer.rawAmount = '100.000 HBD';
44
+ aliceDepositContext.transfer.amount = '100.000';
45
+ aliceDepositContext.transfer.asset = 'HBD';
46
+ await contract.actions.deposit.handler({}, aliceDepositContext);
47
+
48
+ const bobDepositContext = baseContext('transfer', 'bob');
49
+ bobDepositContext.transfer.rawAmount = '10.000 HIVE';
50
+ bobDepositContext.transfer.amount = '10.000';
51
+ bobDepositContext.transfer.asset = 'HIVE';
52
+ await contract.actions.deposit.handler({}, bobDepositContext);
53
+
54
+ await contract.actions.placeOrder.handler({
55
+ side: 'buy',
56
+ base: 'HIVE',
57
+ quote: 'HBD',
58
+ price: '2',
59
+ amount: '5'
60
+ }, baseContext('custom_json', 'alice'));
61
+
62
+ await contract.actions.placeOrder.handler({
63
+ side: 'sell',
64
+ base: 'HIVE',
65
+ quote: 'HBD',
66
+ price: '2',
67
+ amount: '5'
68
+ }, baseContext('custom_json', 'bob'));
69
+
70
+ await contract.actions.matchOrders.handler({ base: 'HIVE', quote: 'HBD', limit: 10, snapshot: true, depth: 20 }, baseContext('time', 'system'));
71
+
72
+ const aliceHive = await adapter.query('SELECT available, locked FROM exchange_balances WHERE account = ? AND asset = ?', ['alice', 'HIVE']);
73
+ const bobHbd = await adapter.query('SELECT available, locked FROM exchange_balances WHERE account = ? AND asset = ?', ['bob', 'HBD']);
74
+
75
+ expect(aliceHive[0].available).toBe('4.995');
76
+ expect(bobHbd[0].available).toBe('9.980');
77
+
78
+ const snapshots = await adapter.query(
79
+ 'SELECT bids, asks FROM exchange_orderbook_snapshots WHERE base_asset = ? AND quote_asset = ?',
80
+ ['HIVE', 'HBD']
81
+ );
82
+ expect(snapshots.length).toBeGreaterThan(0);
83
+ });
84
+ });