1+ package com.getcode.opencode.model.financial
2+
3+ import com.getcode.opencode.internal.solana.vmAuthority
4+ import com.getcode.opencode.solana.keys.TimelockDerivedAccounts
5+ import com.getcode.opencode.utils.padded
6+ import com.getcode.solana.keys.PublicKey
7+ import java.security.KeyPairGenerator
8+ import java.security.SecureRandom
9+ import kotlin.test.Test
10+ import kotlin.test.assertEquals
11+
12+ import kotlin.test.assertTrue
13+ class LocalFiatTests {
14+
15+ private val launchpadMetadata = LaunchpadMetadata (
16+ currencyConfig = generateRandomPublicKeyForTest(),
17+ liquidityPool = generateRandomPublicKeyForTest(),
18+ seed = generateRandomPublicKeyForTest(),
19+ authority = vmAuthority,
20+ mintVault = generateRandomPublicKeyForTest(),
21+ coreMintVault = generateRandomPublicKeyForTest(),
22+ coreMintFees = generateRandomPublicKeyForTest(),
23+ currentCirculatingSupplyQuarks = 0 ,
24+ coreMintLockedQuarks = 0 ,
25+ sellFeeBps = 0
26+ )
27+
28+ private val token = Token (
29+ address = vmAuthority,
30+ decimals = 6 ,
31+ name = " USDC" ,
32+ symbol = " USDC" ,
33+ description = " " ,
34+ imageUrl = " " ,
35+ vmMetadata = VmMetadata (
36+ authority = vmAuthority,
37+ vm = vmAuthority,
38+ lockDurationInDays = TimelockDerivedAccounts .lockoutInDays.toInt()
39+ ),
40+ launchpadMetadata = launchpadMetadata,
41+ billCustomizations = null ,
42+ )
43+
44+ @Test
45+ fun `test sending amounts` () {
46+ val startSupply = 1_00_00_000_000
47+ val endSupply = 21_000_000_00_00_000_000
48+
49+ val fiatToTest = listOf (
50+ 5.00 .toFiat(),
51+ 10.00 .toFiat(),
52+ 100.00 .toFiat(),
53+ 500.00 .toFiat(),
54+ 1_000.00 .toFiat(),
55+ )
56+
57+ val output = buildString {
58+ var supply = startSupply
59+ while (supply <= endSupply) {
60+ val updatedTokenOnChain = token.copy(
61+ launchpadMetadata = launchpadMetadata.copy(
62+ currentCirculatingSupplyQuarks = supply
63+ )
64+ )
65+ fiatToTest.forEach { amount ->
66+ val exchanged = LocalFiat .valueExchangeIn(
67+ amount = amount,
68+ token = updatedTokenOnChain,
69+ rate = Rate .oneToOne,
70+ debug = false ,
71+ )
72+
73+ val formattedSupply = supply.toString().padded(20 )
74+ val underlying = exchanged.underlyingTokenAmount.quarks.toString().padded(20 )
75+ val converted = exchanged.nativeAmount.formatted().padded(20 )
76+
77+ appendLine(" $formattedSupply $underlying $converted " )
78+ }
79+ supply * = 10
80+ }
81+ }.trim()
82+
83+ println (output)
84+
85+ val expectedOutput = """
86+ 10000000000 5001092401997 $5.00
87+ 10000000000 10004379663394 $10.00
88+ 10000000000 100441080923772 $100.00
89+ 10000000000 511295760602584 $500.00
90+ 10000000000 1046604099690267 $1,000.00
91+ 100000000000 5001052911980 $5.00
92+ 100000000000 10004300648691 $10.00
93+ 100000000000 100440284483693 $100.00
94+ 100000000000 511291632270217 $500.00
95+ 100000000000 1046595446081380 $1,000.00
96+ 1000000000000 5000658028967 $5.00
97+ 1000000000000 10003510535997 $10.00
98+ 1000000000000 100432320431767 $100.00
99+ 1000000000000 511250350821238 $500.00
100+ 1000000000000 1046508914111068 $1,000.00
101+ 10000000000000 4996710913709 $5.00
102+ 10000000000000 9995612841801 $10.00
103+ 10000000000000 100352714788750 $100.00
104+ 10000000000000 510837723741344 $500.00
105+ 10000000000000 1045644006120180 $1,000.00
106+ 100000000000000 4957410747532 $5.00
107+ 100000000000000 9916978171984 $10.00
108+ 100000000000000 99560135637918 $100.00
109+ 100000000000000 506730134317242 $500.00
110+ 100000000000000 1037035954468485 $1,000.00
111+ 1000000000000000 4581018238122 $5.00
112+ 1000000000000000 9163878032451 $10.00
113+ 1000000000000000 91971957711638 $100.00
114+ 1000000000000000 467464270426932 $500.00
115+ 1000000000000000 954919469210810 $1,000.00
116+ 10000000000000000 2079970815228 $5.00
117+ 10000000000000000 4160321190168 $10.00
118+ 10000000000000000 41671690967489 $100.00
119+ 10000000000000000 209898607695880 $500.00
120+ 10000000000000000 423734428251854 $1,000.00
121+ 100000000000000000 775257916 $5.00
122+ 100000000000000000 1550515885 $10.00
123+ 100000000000000000 15505168342 $100.00
124+ 100000000000000000 77526052595 $500.00
125+ 100000000000000000 155052632401 $1,000.00
126+ """ .trimIndent()
127+
128+ val actualLines = output.lines()
129+ val expectedLines = expectedOutput.lines()
130+
131+ assertEquals(expectedLines.size, actualLines.size)
132+
133+ for (i in actualLines.indices) {
134+ val actualParts = actualLines[i].split(" \\ s+" .toRegex()).filter { it.isNotEmpty() }
135+ val expectedParts = expectedLines[i].split(" \\ s+" .toRegex()).filter { it.isNotEmpty() }
136+
137+ assertEquals(expectedParts[0 ], actualParts[0 ], " Column 1 mismatch on line $i " )
138+ val diff = (expectedParts[1 ].toLong() - actualParts[1 ].toLong()).let { if (it < 0 ) - it else it }
139+ assertTrue(diff <= 1 , " Column 2 is not within 1 on line $i " )
140+ assertEquals(expectedParts[2 ], actualParts[2 ], " Column 3 mismatch on line $i " )
141+ }
142+ }
143+
144+ @Test
145+ fun `test quarks to balance conversion` () {
146+ val startVol = 1_000_000L
147+ val endVol = 100_000_000_000_000L
148+
149+ val quarks = 1_000_000_000_000L
150+
151+ val output = buildString {
152+ var index = 0
153+ var volume = startVol
154+ while (volume <= endVol) {
155+ val updatedTokenOnChain = token.copy(
156+ launchpadMetadata = launchpadMetadata.copy(
157+ coreMintLockedQuarks = volume
158+ )
159+ )
160+ val tokenBalance = Fiat .tokenBalance(
161+ quarks = quarks,
162+ token = updatedTokenOnChain,
163+ )
164+
165+ val exchanged = LocalFiat .valueExchangeIn(
166+ amount = tokenBalance,
167+ token = updatedTokenOnChain,
168+ rate = Rate .oneToOne,
169+ debug = false ,
170+ )
171+
172+ val nativeAmountsForExchangedQuarks = exchanged.nativeAmount.formatted()
173+
174+ appendLine(nativeAmountsForExchangedQuarks)
175+
176+ volume * = 10
177+ index + = 1
178+ }
179+ }.trim()
180+
181+ println (output)
182+
183+ val expectedOutput = listOf (
184+ " $1.00" ,
185+ " $1.00" ,
186+ " $1.01" ,
187+ " $1.09" ,
188+ " $1.88" ,
189+ " $9.77" ,
190+ " $88.71" ,
191+ " $878.14" ,
192+ " $8,772.37"
193+ )
194+
195+ val generatedOutput = output.lines()
196+
197+ assertEquals(expectedOutput, generatedOutput)
198+ }
199+ }
200+
201+ /* *
202+ * Generates a random Public Key for testing purposes.
203+ */
204+ private fun generateRandomPublicKeyForTest (): PublicKey {
205+ // 1. Generate a KeyPair
206+ val keyGen = KeyPairGenerator .getInstance(" RSA" )
207+ keyGen.initialize(2048 , SecureRandom ()) // Use SecureRandom for strong keys
208+ val keyPair = keyGen.generateKeyPair()
209+
210+ // 2. Extract the public key bytes
211+ val publicKeyBytes = keyPair.public.encoded.toList()
212+
213+ return PublicKey (publicKeyBytes)
214+ }
0 commit comments