@@ -16,9 +16,8 @@ import Splice.Fees
1616import Splice.Expiry
1717import Splice.Round
1818import Splice.Types
19- import Splice.RelRound
20- import Splice.Schedule
2119import Splice.Testing.Registries.AmuletRegistry.Parameters
20+ import Splice.Testing.Registries.AmuletRegistry (advanceToNextRoundChange)
2221import Splice.Scripts.Util
2322
2423import DA.Foldable (forA_)
@@ -34,39 +33,14 @@ scaleAmuletConfig amuletPrice config = AmuletConfig with
3433 transferPreapprovalFee = fmap (/ amuletPrice) config.transferPreapprovalFee
3534 featuredAppActivityMarkerAmount = fmap (/ amuletPrice) config.featuredAppActivityMarkerAmount
3635 optDevelopmentFundManager = config.optDevelopmentFundManager
36+ externalPartyConfigStateTickDuration = config.externalPartyConfigStateTickDuration
3737
3838test : Script ()
3939test = script do
4040 DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
41- advanceToNextRoundChange app -- advance time so we're out of the initial mining round
42- currentTime <- getTime
41+ advanceToNextRoundChange app.dso -- advance time so we're out of the initial mining round
4342 let amuletPrice = 2.5
4443 config = scaleAmuletConfig amuletPrice defaultAmuletConfig
45- tickDuration = config.tickDuration
46-
47- t00_00 = currentTime
48- t05_00 = addRelRoundN 2 currentTime tickDuration
49- t07_30 = addRelRoundN 3 currentTime tickDuration
50- t12_30 = addRelRoundN 5 currentTime tickDuration
51- t15_00 = addRelRoundN 6 currentTime tickDuration
52- t17_30 = addRelRoundN 7 currentTime tickDuration
53-
54- issuingFor0 = days 0
55- issuingFor3 = issuingFor0 + tickDuration + tickDuration + tickDuration
56-
57- amuletConfigUsd = defaultAmuletConfig
58- transferConfigUsd = amuletConfigUsd.transferConfig
59- issuanceConfig0 = getValueAsOf issuingFor0 amuletConfigUsd.issuanceCurve
60- issuanceConfig3 = getValueAsOf issuingFor3 amuletConfigUsd.issuanceCurve
61- openRound0 = OpenMiningRound with dso = app.dso; round = Round 0; amuletPrice; opensAt = t00_00; targetClosesAt = t05_00; issuingFor = issuingFor0; transferConfigUsd; issuanceConfig = issuanceConfig0; tickDuration
62- openRound3 = openRound0 with round = Round 3; opensAt = t07_30 ; targetClosesAt = t12_30; issuanceConfig = issuanceConfig3
63-
64- -- Lock expires at T00:12:30
65- let lockExpiresAt12_30 = TimeLock with holders = [alice.primaryParty]; expiresAt = t12_30; optContext = None
66- -- Lock expires at T00:15:00
67- let lockExpiresAt15_00 = TimeLock with holders = [alice.primaryParty]; expiresAt = t15_00; optContext = None
68- -- Lock expires at T00:17:30
69- let lockExpiresAt17_30 = TimeLock with holders = [alice.primaryParty]; expiresAt = t17_30; optContext = None
7044
7145 -- This amulet is estimated to expire at round 5.
7246 -- we exploit that there are exactly three open rounds active at any point in time.
@@ -76,105 +50,63 @@ test = script do
7650 let amountExpiresAtRound5 = ExpiringAmount with initialAmount = config.transferConfig.holdingFee.rate * 3.5; createdAt = Round 1; ratePerRound = config.transferConfig.holdingFee
7751 let bounded = amountExpiresAt amountExpiresAtRound5
7852 bounded === Singleton (Round 5)
79- let (Singleton r) = bounded
80- estimateOpenRoundCreatedAt config.tickDuration openRound3 (Singleton(addRelRound r (RelRound 2))) === Some (Singleton t15_00)
81-
82- -- lock expires at T00:12:30 before amulet expires at T00:15:00
83- let lockExpireBeforeAmulet = doesLockExpireBeforeAmulet openRound3 lockExpiresAt12_30 amountExpiresAtRound5 config.tickDuration
84- assert lockExpireBeforeAmulet
85-
86- -- lock expires at T00:15:00 while amulet expires at T00:15:00
87- let lockExpiresAsAmulet = doesLockExpireBeforeAmulet openRound3 lockExpiresAt15_00 amountExpiresAtRound5 config.tickDuration
88- assert $ not lockExpiresAsAmulet
89-
90- -- lock expires at T00:17:30 after amulet expires at T00:15:00
91- let lockExpiresAfterAmulet = doesLockExpireBeforeAmulet openRound3 lockExpiresAt17_30 amountExpiresAtRound5 config.tickDuration
92- assert $ not lockExpiresAfterAmulet
93-
94- let largeAmount = ExpiringAmount with initialAmount = 10000.0; createdAt = Round 1; ratePerRound = config.transferConfig.holdingFee
95- -- lock expires at T00:12:30 before amulet with large amount, which is regarded as never expires (EstimatedTime.AfterMaxTime)
96- let lockExpiresBeforeLargeAmulet = doesLockExpireBeforeAmulet openRound3 lockExpiresAt12_30 largeAmount config.tickDuration
97- assert lockExpiresBeforeLargeAmulet
98-
99- pure ()
100-
101- -- From #2336, This test case is to make sure we handle DA.Time overflow
102- -- in estimateOpenRoundCreatedAt when the amulet amount is too large
103- testLargeAmuletExpiry : Script ()
104- testLargeAmuletExpiry = script do
105- DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
106- now <- getTime
107-
108- let amuletPrice = 2.5
109- configUsd = defaultAmuletConfig
110- transferConfigAmulet = scaleFees (1.0 / amuletPrice) configUsd.transferConfig
111- e = ExpiringAmount with initialAmount = 10000.0; createdAt = Round 1; ratePerRound = transferConfigAmulet.holdingFee
112- Singleton r = amountExpiresAt e
113- expiringRound = Singleton $ addRelRound r (RelRound 2)
114- issuingFor = minutes 5
115- currentMiningRound = OpenMiningRound with
116- dso = app.dso
117- round = Round 1
118- amuletPrice = 1.0
119- opensAt = now
120- targetClosesAt = addRelTime now (minutes 5)
121- issuingFor
122- transferConfigUsd = configUsd.transferConfig
123- issuanceConfig = getValueAsOf issuingFor configUsd.issuanceCurve
124- tickDuration = configUsd.tickDuration
125-
126- estimateOpenRoundCreatedAt configUsd.tickDuration currentMiningRound expiringRound === Some AfterMaxBound
12753 pure ()
12854
12955testExpireLockedAmulet : Script ()
13056testExpireLockedAmulet = do
13157 DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
132- advanceToNextRoundChange app -- advance time so we're out of the initial mining round
58+ advanceToNextRoundChange app.dso -- advance time so we're out of the initial mining round
13359 passTime (minutes 10) -- pass enough time so that round 2 expires in 10 minutes
13460
135- -- Locking the amulet with with initialAmount 0.000005 and created at round 2 but with a
136- -- lock for 10 minutes should fail, as round 2 ends in 10 minutes
137- getLockedAmuletMustFail app alice bob.primaryParty defaultTransferConfig.holdingFee.rate (minutes 10)
61+ -- We don't enforce that locks expire before the underlying amulet does so locking an amulet with with initialAmount 0.000005 and created at round 2 but with a
62+ -- lock for 10 minutes should succeed even if round 2 ends in 10 minutes.
63+ cid0 <- getLockedAmulet app alice bob.primaryParty defaultTransferConfig.holdingFee.rate (minutes 10)
13864 -- Lock the amulet with initialAmount 0.000005 and created at round 2
13965 -- As the amulet amount is equal to one round's holding fee, it will be expired at round 3
14066 cid <- getLockedAmulet app alice bob.primaryParty defaultTransferConfig.holdingFee.rate (seconds 90 - convertMicrosecondsToRelTime 1)
14167 Some(lockedAmulet) <- queryContractId @LockedAmulet alice.primaryParty cid
14268
143- roundCid <- advanceRound app (Round 3)
69+ _ <- advanceRound app (Round 3)
70+ updateExternalPartyConfigState app
71+ passTime (hours 24)
72+ updateExternalPartyConfigState app
14473
14574 -- Lock is not yet expired
14675 -- DSO party fails to expire the amulet
76+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
14777 testChoiceFailed [app.dso] [app.dso] $
148- exerciseCmd cid (LockedAmulet_ExpireAmulet roundCid)
78+ exerciseCmd cid (LockedAmulet_ExpireAmuletV2 with
79+ externalPartyConfigState0Cid
80+ externalPartyConfigState1Cid)
14981
150- -- Current round is 4
151- roundCid <- advanceRound app (Round 4)
82+ debug "past first"
15283
153- -- Amulet is not yet expired
154- -- 2 rounds before current round = 4 - 2 = 2 which is before round 3 when the amulet expires
155- -- DSO party fails to expire the amulet
156- testChoiceFailed [app.dso] [app.dso] $
157- exerciseCmd cid (LockedAmulet_ExpireAmulet roundCid)
158-
159- -- Current round is 5
160- roundCid <- advanceRound app (Round 5)
84+ -- Current round is 4
85+ _ <- advanceRound app (Round 4)
86+ passTime (hours 24)
87+ updateExternalPartyConfigState app
88+ passTime (hours 24)
89+ updateExternalPartyConfigState app
16190
16291 normalizedBalanceBeforeExpiry <- getNormalizedBalance app.dso
16392
16493 -- Amulet is expired
16594 -- 2 rounds before current round => 5 - 2 = 3 which is equal to round 3 when the amulet expires
16695 -- DSO party expires the amulet
167- result <- checkTxMetadata app TxKind_ExpireDust alice.primaryParty $
96+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
97+ result <- checkTxMetadata' app TxKind_ExpireDust alice.primaryParty $
16898 testChoice [app.dso] [app.dso] $
169- exerciseCmd cid (LockedAmulet_ExpireAmulet roundCid)
99+ exerciseCmd cid $ LockedAmulet_ExpireAmuletV2 with
100+ externalPartyConfigState0Cid
101+ externalPartyConfigState1Cid
170102 let expireSummary = result.expireSum
171103
172104 normalizedBalanceAfterExpiry <- getNormalizedBalance app.dso
173105 expireSummary.changeToInitialAmountAsOfRoundZero === normalizedBalanceAfterExpiry - normalizedBalanceBeforeExpiry
174106 expireSummary.changeToHoldingFeesRate === - (lockedAmulet.amulet.amount.ratePerRound.rate)
175107
176108 lockedAmulets <- query @LockedAmulet bob.primaryParty
177- lockedAmulets === []
109+ map fst lockedAmulets === [cid0 ]
178110
179111 pure ()
180112
@@ -195,41 +127,51 @@ testExpireAmulet = do
195127 amulet.amount.createdAt === Round 1
196128
197129 -- current latest active round is 3
198- latestActiveRoundCid <- advanceRound app (Round 3)
130+ _ <- advanceRound app (Round 3)
131+ updateExternalPartyConfigState app
132+ passTime (hours 24)
133+ updateExternalPartyConfigState app
199134
200135 -- 2 rounds before latest active round = 3 - 2 = 1 which is before round 3 when the amulet expires
201136 -- DSO party fails to expire the amulet
137+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
202138 testChoiceFailed [app.dso] [app.dso] $
203- exerciseCmd cid (Amulet_Expire latestActiveRoundCid)
139+ exerciseCmd cid Amulet_ExpireV2 with
140+ externalPartyConfigState0Cid
141+ externalPartyConfigState1Cid
204142
205143 -- current latest active round is 4
206- latestActiveRoundCid <- advanceRound app (Round 4)
207-
208- -- 2 rounds before current latest active round => 4 - 2 = 2 which is before round 3 when the amulet expires
209- -- DSO party fails to expire the amulet
210- testChoiceFailed [app.dso] [app.dso] $
211- exerciseCmd cid (Amulet_Expire latestActiveRoundCid)
212-
213- -- current latest active round is 5
214- latestActiveRoundCid <- advanceRound app (Round 5)
144+ _ <- advanceRound app (Round 4)
145+ passTime (hours 24)
146+ updateExternalPartyConfigState app
147+ passTime (hours 24)
148+ updateExternalPartyConfigState app
215149
216150 normalizedBalanceBeforeExpiry <- getNormalizedBalance app.dso
217151
152+ passTime (hours 24)
153+ updateExternalPartyConfigState app
154+ passTime (hours 24)
155+ updateExternalPartyConfigState app
156+
218157 -- 2 rounds before current latest active round => 5 - 2 = 3 which is equal to round 3 when the amulet expires
219158 -- DSO party expires the amulet
220- result <- checkTxMetadata app TxKind_ExpireDust alice.primaryParty $
159+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
160+ result <- checkTxMetadata' app TxKind_ExpireDust alice.primaryParty $
221161 testChoice [app.dso] [app.dso] $
222- exerciseCmd cid (Amulet_Expire latestActiveRoundCid)
162+ exerciseCmd cid $ Amulet_ExpireV2 with
163+ externalPartyConfigState0Cid
164+ externalPartyConfigState1Cid
223165 let expireSummary = result.expireSum
224166
225167 normalizedBalanceAfterExpiry <- getNormalizedBalance app.dso
226168 expireSummary.changeToInitialAmountAsOfRoundZero === normalizedBalanceAfterExpiry - normalizedBalanceBeforeExpiry
227169 expireSummary.changeToHoldingFeesRate === - (amulet.amount.ratePerRound.rate)
228170
229- -- current active current mining round is 3, 4 and 5
171+ -- current active current mining round is 2, 3 and 4
230172 allActiveOpenMiningRoundCids <- query @OpenMiningRound app.dso
231173 forA_ allActiveOpenMiningRoundCids $ \(_, openRound) -> do
232- assert $ openRound.round.number >= 3 && openRound.round.number <= 5
174+ assert $ openRound.round.number >= 2 && openRound.round.number <= 4
233175
234176 -- alice now only owns a amulet with initial amount of 1.0
235177 [(_, onlyAmulet)] <- query @Amulet alice.primaryParty
0 commit comments