tempo.ts 0.0.6 → 0.1.0

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 (195) hide show
  1. package/dist/chains.d.ts +244 -541
  2. package/dist/chains.d.ts.map +1 -1
  3. package/dist/chains.js +10 -23
  4. package/dist/chains.js.map +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/ox/SignatureEnvelope.d.ts +245 -0
  7. package/dist/ox/SignatureEnvelope.d.ts.map +1 -0
  8. package/dist/ox/SignatureEnvelope.js +437 -0
  9. package/dist/ox/SignatureEnvelope.js.map +1 -0
  10. package/dist/ox/Transaction.d.ts +61 -24
  11. package/dist/ox/Transaction.d.ts.map +1 -1
  12. package/dist/ox/Transaction.js +63 -18
  13. package/dist/ox/Transaction.js.map +1 -1
  14. package/dist/ox/TransactionEnvelopeAA.d.ts +461 -0
  15. package/dist/ox/TransactionEnvelopeAA.d.ts.map +1 -0
  16. package/dist/ox/TransactionEnvelopeAA.js +528 -0
  17. package/dist/ox/TransactionEnvelopeAA.js.map +1 -0
  18. package/dist/ox/TransactionRequest.d.ts +7 -5
  19. package/dist/ox/TransactionRequest.d.ts.map +1 -1
  20. package/dist/ox/TransactionRequest.js +21 -12
  21. package/dist/ox/TransactionRequest.js.map +1 -1
  22. package/dist/ox/index.d.ts +5 -4
  23. package/dist/ox/index.d.ts.map +1 -1
  24. package/dist/ox/index.js +5 -4
  25. package/dist/ox/index.js.map +1 -1
  26. package/dist/prool/Instance.d.ts +0 -4
  27. package/dist/prool/Instance.d.ts.map +1 -1
  28. package/dist/prool/Instance.js +7 -7
  29. package/dist/prool/Instance.js.map +1 -1
  30. package/dist/prool/index.d.ts +1 -1
  31. package/dist/prool/index.d.ts.map +1 -1
  32. package/dist/prool/index.js +1 -1
  33. package/dist/prool/index.js.map +1 -1
  34. package/dist/viem/{abis.d.ts → Abis.d.ts} +523 -9
  35. package/dist/viem/Abis.d.ts.map +1 -0
  36. package/dist/viem/{abis.js → Abis.js} +321 -9
  37. package/dist/viem/Abis.js.map +1 -0
  38. package/dist/viem/{actions → Actions}/amm.d.ts +21 -21
  39. package/dist/viem/Actions/amm.d.ts.map +1 -0
  40. package/dist/viem/{actions → Actions}/amm.js +55 -43
  41. package/dist/viem/Actions/amm.js.map +1 -0
  42. package/dist/viem/Actions/dex.d.ts +3263 -0
  43. package/dist/viem/Actions/dex.d.ts.map +1 -0
  44. package/dist/viem/Actions/dex.js +1357 -0
  45. package/dist/viem/Actions/dex.js.map +1 -0
  46. package/dist/viem/{actions → Actions}/fee.d.ts +8 -8
  47. package/dist/viem/Actions/fee.d.ts.map +1 -0
  48. package/dist/viem/{actions → Actions}/fee.js +14 -13
  49. package/dist/viem/Actions/fee.js.map +1 -0
  50. package/dist/viem/Actions/index.d.ts +6 -0
  51. package/dist/viem/Actions/index.d.ts.map +1 -0
  52. package/dist/viem/Actions/index.js +6 -0
  53. package/dist/viem/Actions/index.js.map +1 -0
  54. package/dist/viem/{actions → Actions}/policy.d.ts +19 -19
  55. package/dist/viem/Actions/policy.d.ts.map +1 -0
  56. package/dist/viem/{actions → Actions}/policy.js +59 -46
  57. package/dist/viem/Actions/policy.js.map +1 -0
  58. package/dist/viem/{actions → Actions}/token.d.ts +3250 -700
  59. package/dist/viem/Actions/token.d.ts.map +1 -0
  60. package/dist/viem/{actions → Actions}/token.js +419 -83
  61. package/dist/viem/Actions/token.js.map +1 -0
  62. package/dist/viem/Addresses.d.ts +9 -0
  63. package/dist/viem/Addresses.d.ts.map +1 -0
  64. package/dist/viem/Addresses.js +9 -0
  65. package/dist/viem/Addresses.js.map +1 -0
  66. package/dist/viem/{chain.d.ts → Chain.d.ts} +81 -57
  67. package/dist/viem/Chain.d.ts.map +1 -0
  68. package/dist/viem/{chain.js → Chain.js} +7 -7
  69. package/dist/viem/Chain.js.map +1 -0
  70. package/dist/viem/{client.d.ts → Client.d.ts} +4 -4
  71. package/dist/viem/Client.d.ts.map +1 -0
  72. package/dist/viem/{client.js → Client.js} +3 -3
  73. package/dist/viem/Client.js.map +1 -0
  74. package/dist/viem/{decorator.d.ts → Decorator.d.ts} +507 -5
  75. package/dist/viem/Decorator.d.ts.map +1 -0
  76. package/dist/viem/{decorator.js → Decorator.js} +31 -5
  77. package/dist/viem/Decorator.js.map +1 -0
  78. package/dist/viem/{formatters.d.ts → Formatters.d.ts} +2 -2
  79. package/dist/viem/Formatters.d.ts.map +1 -0
  80. package/dist/viem/{formatters.js → Formatters.js} +24 -17
  81. package/dist/viem/Formatters.js.map +1 -0
  82. package/dist/viem/Tick.d.ts +111 -0
  83. package/dist/viem/Tick.d.ts.map +1 -0
  84. package/dist/viem/Tick.js +127 -0
  85. package/dist/viem/Tick.js.map +1 -0
  86. package/dist/viem/TokenIds.d.ts +3 -0
  87. package/dist/viem/TokenIds.d.ts.map +1 -0
  88. package/dist/viem/TokenIds.js +3 -0
  89. package/dist/viem/TokenIds.js.map +1 -0
  90. package/dist/viem/Transaction.d.ts +57 -0
  91. package/dist/viem/Transaction.d.ts.map +1 -0
  92. package/dist/viem/Transaction.js +137 -0
  93. package/dist/viem/Transaction.js.map +1 -0
  94. package/dist/viem/{transport.d.ts → Transport.d.ts} +3 -3
  95. package/dist/viem/Transport.d.ts.map +1 -0
  96. package/dist/viem/{transport.js → Transport.js} +3 -3
  97. package/dist/viem/Transport.js.map +1 -0
  98. package/dist/viem/index.d.ts +13 -9
  99. package/dist/viem/index.d.ts.map +1 -1
  100. package/dist/viem/index.js +13 -9
  101. package/dist/viem/index.js.map +1 -1
  102. package/dist/viem/{types.d.ts → internal/types.d.ts} +3 -3
  103. package/dist/viem/internal/types.d.ts.map +1 -0
  104. package/dist/viem/{types.js.map → internal/types.js.map} +1 -1
  105. package/dist/viem/internal/utils.d.ts.map +1 -0
  106. package/dist/viem/internal/utils.js.map +1 -0
  107. package/package.json +87 -101
  108. package/src/chains.ts +10 -24
  109. package/src/ox/SignatureEnvelope.test.ts +1252 -0
  110. package/src/ox/SignatureEnvelope.ts +709 -0
  111. package/src/ox/Transaction.test.ts +144 -89
  112. package/src/ox/Transaction.ts +104 -29
  113. package/src/ox/TransactionEnvelopeAA.test.ts +1533 -0
  114. package/src/ox/TransactionEnvelopeAA.ts +858 -0
  115. package/src/ox/TransactionRequest.ts +25 -17
  116. package/src/ox/index.ts +2 -1
  117. package/src/prool/Instance.ts +6 -14
  118. package/src/prool/internal/chain.json +101 -27
  119. package/src/viem/{abis.ts → Abis.ts} +322 -8
  120. package/src/viem/{actions → Actions}/amm.test.ts +65 -68
  121. package/src/viem/{actions → Actions}/amm.ts +72 -60
  122. package/src/viem/Actions/dex.test.ts +1608 -0
  123. package/src/viem/Actions/dex.ts +2026 -0
  124. package/src/viem/{actions → Actions}/fee.test.ts +34 -36
  125. package/src/viem/{actions → Actions}/fee.ts +18 -17
  126. package/src/viem/{actions → Actions}/index.ts +1 -0
  127. package/src/viem/{actions → Actions}/policy.test.ts +2 -2
  128. package/src/viem/{actions → Actions}/policy.ts +77 -64
  129. package/src/viem/{actions → Actions}/token.test.ts +403 -64
  130. package/src/viem/{actions → Actions}/token.ts +672 -133
  131. package/src/viem/Addresses.ts +9 -0
  132. package/src/viem/{chain.ts → Chain.ts} +6 -6
  133. package/src/viem/{client.bench-d.ts → Client.bench-d.ts} +2 -2
  134. package/src/viem/{client.test.ts → Client.test.ts} +31 -6
  135. package/src/viem/{client.ts → Client.ts} +1 -1
  136. package/src/viem/{decorator.bench-d.ts → Decorator.bench-d.ts} +2 -2
  137. package/src/viem/{decorator.test.ts → Decorator.test.ts} +1 -0
  138. package/src/viem/{decorator.ts → Decorator.ts} +586 -4
  139. package/src/viem/{formatters.ts → Formatters.ts} +31 -20
  140. package/src/viem/Tick.test.ts +281 -0
  141. package/src/viem/Tick.ts +176 -0
  142. package/src/viem/TokenIds.ts +2 -0
  143. package/src/viem/Transaction.ts +303 -0
  144. package/src/viem/{transport.ts → Transport.ts} +5 -5
  145. package/src/viem/e2e.test.ts +153 -78
  146. package/src/viem/index.ts +13 -9
  147. package/src/viem/{types.ts → internal/types.ts} +3 -3
  148. package/dist/ox/TransactionEnvelopeFeeToken.d.ts +0 -393
  149. package/dist/ox/TransactionEnvelopeFeeToken.d.ts.map +0 -1
  150. package/dist/ox/TransactionEnvelopeFeeToken.js +0 -452
  151. package/dist/ox/TransactionEnvelopeFeeToken.js.map +0 -1
  152. package/dist/viem/abis.d.ts.map +0 -1
  153. package/dist/viem/abis.js.map +0 -1
  154. package/dist/viem/actions/amm.d.ts.map +0 -1
  155. package/dist/viem/actions/amm.js.map +0 -1
  156. package/dist/viem/actions/fee.d.ts.map +0 -1
  157. package/dist/viem/actions/fee.js.map +0 -1
  158. package/dist/viem/actions/index.d.ts +0 -5
  159. package/dist/viem/actions/index.d.ts.map +0 -1
  160. package/dist/viem/actions/index.js +0 -5
  161. package/dist/viem/actions/index.js.map +0 -1
  162. package/dist/viem/actions/policy.d.ts.map +0 -1
  163. package/dist/viem/actions/policy.js.map +0 -1
  164. package/dist/viem/actions/token.d.ts.map +0 -1
  165. package/dist/viem/actions/token.js.map +0 -1
  166. package/dist/viem/addresses.d.ts +0 -8
  167. package/dist/viem/addresses.d.ts.map +0 -1
  168. package/dist/viem/addresses.js +0 -8
  169. package/dist/viem/addresses.js.map +0 -1
  170. package/dist/viem/chain.d.ts.map +0 -1
  171. package/dist/viem/chain.js.map +0 -1
  172. package/dist/viem/client.d.ts.map +0 -1
  173. package/dist/viem/client.js.map +0 -1
  174. package/dist/viem/decorator.d.ts.map +0 -1
  175. package/dist/viem/decorator.js.map +0 -1
  176. package/dist/viem/formatters.d.ts.map +0 -1
  177. package/dist/viem/formatters.js.map +0 -1
  178. package/dist/viem/transaction.d.ts +0 -54
  179. package/dist/viem/transaction.d.ts.map +0 -1
  180. package/dist/viem/transaction.js +0 -108
  181. package/dist/viem/transaction.js.map +0 -1
  182. package/dist/viem/transport.d.ts.map +0 -1
  183. package/dist/viem/transport.js.map +0 -1
  184. package/dist/viem/types.d.ts.map +0 -1
  185. package/dist/viem/utils.d.ts.map +0 -1
  186. package/dist/viem/utils.js.map +0 -1
  187. package/src/ox/TransactionEnvelopeFeeToken.test.ts +0 -1119
  188. package/src/ox/TransactionEnvelopeFeeToken.ts +0 -717
  189. package/src/prool/internal/consensus.toml +0 -32
  190. package/src/viem/addresses.ts +0 -10
  191. package/src/viem/transaction.ts +0 -253
  192. /package/dist/viem/{types.js → internal/types.js} +0 -0
  193. /package/dist/viem/{utils.d.ts → internal/utils.d.ts} +0 -0
  194. /package/dist/viem/{utils.js → internal/utils.js} +0 -0
  195. /package/src/viem/{utils.ts → internal/utils.ts} +0 -0
@@ -0,0 +1,281 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import * as Tick from './Tick.js'
3
+
4
+ describe('toPrice', () => {
5
+ test('converts tick 0 to price 1', () => {
6
+ expect(Tick.toPrice(0)).toBe('1')
7
+ })
8
+
9
+ test('converts positive ticks correctly', () => {
10
+ expect(Tick.toPrice(100)).toBe('1.001')
11
+ expect(Tick.toPrice(1000)).toBe('1.01')
12
+ expect(Tick.toPrice(2000)).toBe('1.02')
13
+ })
14
+
15
+ test('converts negative ticks correctly', () => {
16
+ expect(Tick.toPrice(-100)).toBe('0.999')
17
+ expect(Tick.toPrice(-1000)).toBe('0.99')
18
+ expect(Tick.toPrice(-2000)).toBe('0.98')
19
+ })
20
+
21
+ test('handles boundary values', () => {
22
+ expect(Tick.toPrice(Tick.minTick)).toBe('0.98')
23
+ expect(Tick.toPrice(Tick.maxTick)).toBe('1.02')
24
+ })
25
+
26
+ test('preserves exact 5 decimal precision', () => {
27
+ expect(Tick.toPrice(1)).toBe('1.00001')
28
+ expect(Tick.toPrice(-1)).toBe('0.99999')
29
+ expect(Tick.toPrice(1234)).toBe('1.01234')
30
+ })
31
+
32
+ test('throws error when tick is below minimum', () => {
33
+ expect(() => Tick.toPrice(-2001)).toThrow(Tick.TickOutOfBoundsError)
34
+ expect(() => Tick.toPrice(-2001)).toThrow('Tick -2001 is out of bounds.')
35
+ expect(() => Tick.toPrice(-3000)).toThrow(Tick.TickOutOfBoundsError)
36
+ })
37
+
38
+ test('throws error when tick is above maximum', () => {
39
+ expect(() => Tick.toPrice(2001)).toThrow(Tick.TickOutOfBoundsError)
40
+ expect(() => Tick.toPrice(2001)).toThrow('Tick 2001 is out of bounds.')
41
+ expect(() => Tick.toPrice(3000)).toThrow(Tick.TickOutOfBoundsError)
42
+ })
43
+
44
+ test('handles edge cases near bounds', () => {
45
+ expect(() => Tick.toPrice(-2000)).not.toThrow()
46
+ expect(() => Tick.toPrice(2000)).not.toThrow()
47
+ })
48
+ })
49
+
50
+ describe('fromPrice', () => {
51
+ test('converts price 1.0 to tick 0', () => {
52
+ expect(Tick.fromPrice('1.0')).toBe(0)
53
+ expect(Tick.fromPrice('1.00000')).toBe(0)
54
+ })
55
+
56
+ test('converts prices above 1.0 correctly', () => {
57
+ expect(Tick.fromPrice('1.001')).toBe(100)
58
+ expect(Tick.fromPrice('1.01')).toBe(1000)
59
+ expect(Tick.fromPrice('1.02')).toBe(2000)
60
+ })
61
+
62
+ test('converts prices below 1.0 correctly', () => {
63
+ expect(Tick.fromPrice('0.999')).toBe(-100)
64
+ expect(Tick.fromPrice('0.99')).toBe(-1000)
65
+ expect(Tick.fromPrice('0.98')).toBe(-2000)
66
+ })
67
+
68
+ test('handles boundary values', () => {
69
+ expect(Tick.fromPrice('0.98')).toBe(Tick.minTick)
70
+ expect(Tick.fromPrice('1.02')).toBe(Tick.maxTick)
71
+ })
72
+
73
+ test('handles different decimal precisions', () => {
74
+ expect(Tick.fromPrice('1.00001')).toBe(1)
75
+ expect(Tick.fromPrice('1.0001')).toBe(10)
76
+ expect(Tick.fromPrice('1.001')).toBe(100)
77
+ expect(Tick.fromPrice('0.99999')).toBe(-1)
78
+ })
79
+
80
+ test('truncates beyond 5 decimal places', () => {
81
+ // Should truncate, not round, extra decimals
82
+ expect(Tick.fromPrice('1.000019')).toBe(1) // Takes first 5: 1.00001
83
+ expect(Tick.fromPrice('1.000015')).toBe(1) // Takes first 5: 1.00001
84
+ })
85
+
86
+ test('validates string format', () => {
87
+ expect(() => Tick.fromPrice('abc')).toThrow(Tick.InvalidPriceFormatError)
88
+ expect(() => Tick.fromPrice('abc')).toThrow('Invalid price format')
89
+ expect(() => Tick.fromPrice('1.2.3')).toThrow(Tick.InvalidPriceFormatError)
90
+ expect(() => Tick.fromPrice('')).toThrow(Tick.InvalidPriceFormatError)
91
+ })
92
+
93
+ test('throws error when price results in tick below minimum', () => {
94
+ expect(() => Tick.fromPrice('0.979')).toThrow(Tick.PriceOutOfBoundsError)
95
+ expect(() => Tick.fromPrice('0.979')).toThrow(
96
+ 'Price "0.979" results in tick -2100 which is out of bounds.',
97
+ )
98
+ expect(() => Tick.fromPrice('0.5')).toThrow(Tick.PriceOutOfBoundsError)
99
+ })
100
+
101
+ test('throws error when price results in tick above maximum', () => {
102
+ expect(() => Tick.fromPrice('1.021')).toThrow(Tick.PriceOutOfBoundsError)
103
+ expect(() => Tick.fromPrice('1.021')).toThrow(
104
+ 'Price "1.021" results in tick 2100 which is out of bounds.',
105
+ )
106
+ expect(() => Tick.fromPrice('1.5')).toThrow(Tick.PriceOutOfBoundsError)
107
+ })
108
+
109
+ test('handles edge cases near bounds', () => {
110
+ expect(() => Tick.fromPrice('0.98')).not.toThrow()
111
+ expect(() => Tick.fromPrice('1.02')).not.toThrow()
112
+ })
113
+
114
+ test('handles whitespace', () => {
115
+ expect(Tick.fromPrice(' 1.0 ')).toBe(0)
116
+ expect(Tick.fromPrice(' 1.001 ')).toBe(100)
117
+ })
118
+ })
119
+
120
+ describe('round-trip conversions', () => {
121
+ test('tick -> price -> tick preserves tick values exactly', () => {
122
+ const ticks = [-2000, -1000, -100, -1, 0, 1, 100, 1000, 2000]
123
+ for (const tick of ticks) {
124
+ const price = Tick.toPrice(tick)
125
+ const roundTripTick = Tick.fromPrice(price)
126
+ expect(roundTripTick).toBe(tick)
127
+ }
128
+ })
129
+
130
+ test('price -> tick -> price preserves price strings exactly', () => {
131
+ const prices = ['0.98', '0.99', '1', '1.01', '1.02']
132
+ for (const price of prices) {
133
+ const tick = Tick.fromPrice(price)
134
+ const roundTripPrice = Tick.toPrice(tick)
135
+ expect(roundTripPrice).toBe(price)
136
+ }
137
+ })
138
+
139
+ test('handles arbitrary precision in input strings', () => {
140
+ // Shorter decimal strings should round-trip correctly
141
+ expect(Tick.toPrice(Tick.fromPrice('1.0'))).toBe('1')
142
+ expect(Tick.toPrice(Tick.fromPrice('0.999'))).toBe('0.999')
143
+
144
+ // Longer decimal strings get truncated to 5 decimals
145
+ expect(Tick.toPrice(Tick.fromPrice('1.000019999'))).toBe('1.00001')
146
+ })
147
+ })
148
+
149
+ describe('error handling', () => {
150
+ test('InvalidPriceFormatError is catchable and has correct properties', () => {
151
+ try {
152
+ Tick.fromPrice('invalid')
153
+ expect.fail('Should have thrown')
154
+ } catch (error) {
155
+ expect(error).toBeInstanceOf(Tick.InvalidPriceFormatError)
156
+ expect(error).toHaveProperty('name', 'Tick.InvalidPriceFormatError')
157
+ expect((error as Error).message).toContain('Invalid price format')
158
+ expect((error as Error).message).toContain('invalid')
159
+ expect((error as Error).message).toContain(
160
+ 'Price must be a decimal number string',
161
+ )
162
+ }
163
+ })
164
+
165
+ test('TickOutOfBoundsError is catchable and has correct properties', () => {
166
+ try {
167
+ Tick.toPrice(-2001)
168
+ expect.fail('Should have thrown')
169
+ } catch (error) {
170
+ expect(error).toBeInstanceOf(Tick.TickOutOfBoundsError)
171
+ expect(error).toHaveProperty('name', 'Tick.TickOutOfBoundsError')
172
+ // BaseError appends metaMessages to the message
173
+ expect((error as Error).message).toContain('Tick -2001 is out of bounds.')
174
+ expect((error as Error).message).toContain(
175
+ 'Tick must be between -2000 and 2000.',
176
+ )
177
+ }
178
+ })
179
+
180
+ test('PriceOutOfBoundsError is catchable and has correct properties', () => {
181
+ try {
182
+ Tick.fromPrice('0.979')
183
+ expect.fail('Should have thrown')
184
+ } catch (error) {
185
+ expect(error).toBeInstanceOf(Tick.PriceOutOfBoundsError)
186
+ expect(error).toHaveProperty('name', 'Tick.PriceOutOfBoundsError')
187
+ expect((error as Error).message).toContain(
188
+ 'Price "0.979" results in tick -2100 which is out of bounds.',
189
+ )
190
+ expect((error as Error).message).toContain(
191
+ 'Tick must be between -2000 and 2000.',
192
+ )
193
+ }
194
+ })
195
+
196
+ test('can distinguish between error types', () => {
197
+ try {
198
+ Tick.fromPrice('invalid')
199
+ } catch (error) {
200
+ if (error instanceof Tick.InvalidPriceFormatError) {
201
+ expect(true).toBe(true) // Successfully caught as InvalidPriceFormatError
202
+ } else {
203
+ expect.fail('Should be InvalidPriceFormatError')
204
+ }
205
+ }
206
+
207
+ try {
208
+ Tick.toPrice(-2001)
209
+ } catch (error) {
210
+ if (error instanceof Tick.TickOutOfBoundsError) {
211
+ expect(true).toBe(true) // Successfully caught as TickOutOfBoundsError
212
+ } else {
213
+ expect.fail('Should be TickOutOfBoundsError')
214
+ }
215
+ }
216
+
217
+ try {
218
+ Tick.fromPrice('0.979')
219
+ } catch (error) {
220
+ if (error instanceof Tick.PriceOutOfBoundsError) {
221
+ expect(true).toBe(true) // Successfully caught as PriceOutOfBoundsError
222
+ } else {
223
+ expect.fail('Should be PriceOutOfBoundsError')
224
+ }
225
+ }
226
+ })
227
+ })
228
+
229
+ describe('precision and edge cases', () => {
230
+ test('handles very small price increments', () => {
231
+ // 0.1 bps = 0.001% = 0.00001 in decimal
232
+ expect(Tick.fromPrice('1.00001')).toBe(1)
233
+ expect(Tick.fromPrice('1.00002')).toBe(2)
234
+ })
235
+
236
+ test('price scale maintains exact 5 decimal precision', () => {
237
+ // The price scale is 100,000 which gives us exactly 5 decimal places
238
+ const price0 = Tick.toPrice(0)
239
+ const price1 = Tick.toPrice(1)
240
+ expect(price0).toBe('1')
241
+ expect(price1).toBe('1.00001')
242
+
243
+ // Verify exact difference
244
+ const diff = Number(price1) - Number(price0)
245
+ expect(diff).toBeCloseTo(0.00001, 10)
246
+ })
247
+
248
+ test('symmetric around 1.0', () => {
249
+ // +100 ticks should have same magnitude as -100 ticks from 1.0
250
+ const priceUp = Tick.toPrice(100)
251
+ const priceDown = Tick.toPrice(-100)
252
+ expect(priceUp).toBe('1.001')
253
+ expect(priceDown).toBe('0.999')
254
+
255
+ const upDiff = Number(priceUp) - 1.0
256
+ const downDiff = 1.0 - Number(priceDown)
257
+ expect(upDiff).toBeCloseTo(downDiff, 10)
258
+ })
259
+
260
+ test('validates 2% range', () => {
261
+ // At minimum tick, price should be exactly 2% below 1.0
262
+ const minPrice = Tick.toPrice(Tick.minTick)
263
+ expect(minPrice).toBe('0.98')
264
+ expect((1.0 - Number(minPrice)) / 1.0).toBeCloseTo(0.02, 10)
265
+
266
+ // At maximum tick, price should be exactly 2% above 1.0
267
+ const maxPrice = Tick.toPrice(Tick.maxTick)
268
+ expect(maxPrice).toBe('1.02')
269
+ expect((Number(maxPrice) - 1.0) / 1.0).toBeCloseTo(0.02, 10)
270
+ })
271
+
272
+ test('no floating point precision errors', () => {
273
+ // String-based parsing should eliminate float precision issues
274
+ expect(Tick.fromPrice('0.99999')).toBe(-1)
275
+ expect(Tick.fromPrice('1.00001')).toBe(1)
276
+
277
+ // These would potentially fail with float arithmetic
278
+ expect(Tick.fromPrice('0.98001')).toBe(-1999)
279
+ expect(Tick.fromPrice('1.01999')).toBe(1999)
280
+ })
281
+ })
@@ -0,0 +1,176 @@
1
+ import * as Errors from 'ox/Errors'
2
+
3
+ /**
4
+ * Minimum allowed tick value.
5
+ */
6
+ export const minTick = -2000
7
+
8
+ /**
9
+ * Maximum allowed tick value.
10
+ */
11
+ export const maxTick = 2000
12
+
13
+ /**
14
+ * Price scaling factor (5 decimal places for 0.1 bps precision).
15
+ *
16
+ * @description
17
+ * The DEX uses a tick-based pricing system where:
18
+ * - Tick size: 0.1 bps (basis points)
19
+ * - Price range: ±2% from 1.0 (ticks from -2000 to +2000)
20
+ */
21
+ export const priceScale = 100_000
22
+
23
+ /**
24
+ * Converts a tick to a price string.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { Tick } from 'tempo.ts/viem'
29
+ *
30
+ * // Tick 0 = price of 1.0
31
+ * const price1 = Tick.toPrice(0) // "1"
32
+ *
33
+ * // Tick 100 = price of 1.001 (0.1% higher)
34
+ * const price2 = Tick.toPrice(100) // "1.001"
35
+ *
36
+ * // Tick -100 = price of 0.999 (0.1% lower)
37
+ * const price3 = Tick.toPrice(-100) // "0.999"
38
+ * ```
39
+ *
40
+ * @param tick - The tick value (range: -2000 to +2000).
41
+ * @returns The price as a string with exact decimal representation.
42
+ * @throws {TickOutOfBoundsError} If tick is out of bounds.
43
+ */
44
+ export function toPrice(tick: toPrice.Tick): toPrice.ReturnType {
45
+ if (tick < minTick || tick > maxTick) {
46
+ throw new TickOutOfBoundsError({ tick })
47
+ }
48
+ // Use integer arithmetic to avoid floating point errors
49
+ const price = priceScale + tick
50
+ const whole = Math.floor(price / priceScale)
51
+
52
+ let decimal = (price % priceScale).toString().padStart(5, '0')
53
+ decimal = decimal.replace(/0+$/, '')
54
+
55
+ if (decimal.length === 0) return whole.toString()
56
+ return `${whole}.${decimal}`
57
+ }
58
+
59
+ export declare namespace toPrice {
60
+ export type Tick = number
61
+ export type ReturnType = string
62
+ }
63
+
64
+ /**
65
+ * Converts a price string to a tick.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { Tick } from 'tempo.ts/viem'
70
+ *
71
+ * // Price of 1.0 = tick 0
72
+ * const tick1 = Tick.fromPrice('1.0') // 0
73
+ * const tick2 = Tick.fromPrice('1.00000') // 0
74
+ *
75
+ * // Price of 1.001 = tick 100
76
+ * const tick3 = Tick.fromPrice('1.001') // 100
77
+ *
78
+ * // Price of 0.999 = tick -100
79
+ * const tick4 = Tick.fromPrice('0.999') // -100
80
+ * ```
81
+ *
82
+ * @param price - The price as a string (e.g., "1.001", "0.999").
83
+ * @returns The tick value.
84
+ * @throws {InvalidPriceTypeError} If the price is not a string.
85
+ * @throws {InvalidPriceFormatError} If the price format is invalid.
86
+ * @throws {PriceOutOfBoundsError} If the resulting tick is out of bounds.
87
+ */
88
+ export function fromPrice(price: fromPrice.Price): fromPrice.ReturnType {
89
+ const priceStr = price.trim()
90
+ if (!/^-?\d+(\.\d+)?$/.test(priceStr))
91
+ throw new InvalidPriceFormatError({ price })
92
+
93
+ // Parse price using string manipulation to avoid float precision issues
94
+ const [w, d = '0'] = priceStr.split('.')
95
+ const whole = BigInt(w!)
96
+
97
+ // Pad or truncate decimal to exactly 5 digits
98
+ const decimal = BigInt(d.padEnd(5, '0').slice(0, 5))
99
+
100
+ // Calculate price
101
+ const priceInt = whole * BigInt(priceScale) + decimal
102
+
103
+ // Calculate tick
104
+ const tick = Number(priceInt - BigInt(priceScale))
105
+
106
+ if (tick < minTick || tick > maxTick)
107
+ throw new PriceOutOfBoundsError({ price, tick })
108
+
109
+ return tick
110
+ }
111
+
112
+ export declare namespace fromPrice {
113
+ export type Price = string
114
+ export type ReturnType = number
115
+ }
116
+
117
+ /**
118
+ * Error thrown when a tick value is out of the allowed bounds.
119
+ */
120
+ export class TickOutOfBoundsError extends Errors.BaseError {
121
+ override readonly name = 'Tick.TickOutOfBoundsError'
122
+
123
+ constructor(options: TickOutOfBoundsError.Options) {
124
+ super(`Tick ${options.tick} is out of bounds.`, {
125
+ metaMessages: [`Tick must be between ${minTick} and ${maxTick}.`],
126
+ })
127
+ }
128
+ }
129
+
130
+ export declare namespace TickOutOfBoundsError {
131
+ export type Options = {
132
+ tick: number
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Error thrown when a price string has an invalid format.
138
+ */
139
+ export class InvalidPriceFormatError extends Errors.BaseError {
140
+ override readonly name = 'Tick.InvalidPriceFormatError'
141
+
142
+ constructor(options: InvalidPriceFormatError.Options) {
143
+ super(`Invalid price format: "${options.price}".`, {
144
+ metaMessages: ['Price must be a decimal number string (e.g., "1.001").'],
145
+ })
146
+ }
147
+ }
148
+
149
+ export declare namespace InvalidPriceFormatError {
150
+ export type Options = {
151
+ price: string
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Error thrown when a price string results in an out-of-bounds tick.
157
+ */
158
+ export class PriceOutOfBoundsError extends Errors.BaseError {
159
+ override readonly name = 'Tick.PriceOutOfBoundsError'
160
+
161
+ constructor(options: PriceOutOfBoundsError.Options) {
162
+ super(
163
+ `Price "${options.price}" results in tick ${options.tick} which is out of bounds.`,
164
+ {
165
+ metaMessages: [`Tick must be between ${minTick} and ${maxTick}.`],
166
+ },
167
+ )
168
+ }
169
+ }
170
+
171
+ export declare namespace PriceOutOfBoundsError {
172
+ export type Options = {
173
+ price: string
174
+ tick: number
175
+ }
176
+ }
@@ -0,0 +1,2 @@
1
+ export const defaultFeeToken = 1n
2
+ export const defaultQuoteToken = 0n