@@ -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,58 @@ 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)
144-
145- -- Lock is not yet expired
146- -- DSO party fails to expire the amulet
147- testChoiceFailed [app.dso] [app.dso] $
148- exerciseCmd cid (LockedAmulet_ExpireAmulet roundCid)
69+ _ <- advanceRound app (Round 3)
14970
15071 -- Current round is 4
151- roundCid <- advanceRound app (Round 4)
72+ _ <- advanceRound app (Round 4)
15273
153- -- Amulet is not yet expired
154- -- 2 rounds before current round = 4 - 2 = 2 which is before round 3 when the amulet expires
74+ -- Lock is not yet expired
15575 -- DSO party fails to expire the amulet
76+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
15677 testChoiceFailed [app.dso] [app.dso] $
157- exerciseCmd cid (LockedAmulet_ExpireAmulet roundCid)
78+ exerciseCmd cid (LockedAmulet_ExpireAmuletV2 with
79+ externalPartyConfigState0Cid
80+ externalPartyConfigState1Cid)
81+
15882
15983 -- Current round is 5
160- roundCid <- advanceRound app (Round 5)
84+ _ <- advanceRound app (Round 5)
16185
16286 normalizedBalanceBeforeExpiry <- getNormalizedBalance app.dso
16387
16488 -- Amulet is expired
16589 -- 2 rounds before current round => 5 - 2 = 3 which is equal to round 3 when the amulet expires
16690 -- DSO party expires the amulet
167- result <- checkTxMetadata app TxKind_ExpireDust alice.primaryParty $
91+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
92+ result <- checkTxMetadata' app TxKind_ExpireDust alice.primaryParty $
16893 testChoice [app.dso] [app.dso] $
169- exerciseCmd cid (LockedAmulet_ExpireAmulet roundCid)
94+ exerciseCmd cid $ LockedAmulet_ExpireAmuletV2 with
95+ externalPartyConfigState0Cid
96+ externalPartyConfigState1Cid
17097 let expireSummary = result.expireSum
17198
17299 normalizedBalanceAfterExpiry <- getNormalizedBalance app.dso
173100 expireSummary.changeToInitialAmountAsOfRoundZero === normalizedBalanceAfterExpiry - normalizedBalanceBeforeExpiry
174101 expireSummary.changeToHoldingFeesRate === - (lockedAmulet.amulet.amount.ratePerRound.rate)
175102
176103 lockedAmulets <- query @LockedAmulet bob.primaryParty
177- lockedAmulets === []
104+ map fst lockedAmulets === [cid0 ]
178105
179106 pure ()
180107
@@ -195,31 +122,39 @@ testExpireAmulet = do
195122 amulet.amount.createdAt === Round 1
196123
197124 -- current latest active round is 3
198- latestActiveRoundCid <- advanceRound app (Round 3)
125+ _ <- advanceRound app (Round 3)
199126
200127 -- 2 rounds before latest active round = 3 - 2 = 1 which is before round 3 when the amulet expires
201128 -- DSO party fails to expire the amulet
129+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
202130 testChoiceFailed [app.dso] [app.dso] $
203- exerciseCmd cid (Amulet_Expire latestActiveRoundCid)
131+ exerciseCmd cid Amulet_ExpireV2 with
132+ externalPartyConfigState0Cid
133+ externalPartyConfigState1Cid
204134
205135 -- current latest active round is 4
206- latestActiveRoundCid <- advanceRound app (Round 4)
136+ _ <- advanceRound app (Round 4)
207137
208138 -- 2 rounds before current latest active round => 4 - 2 = 2 which is before round 3 when the amulet expires
209139 -- DSO party fails to expire the amulet
140+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
210141 testChoiceFailed [app.dso] [app.dso] $
211- exerciseCmd cid (Amulet_Expire latestActiveRoundCid)
142+ exerciseCmd cid Amulet_ExpireV2 with
143+ externalPartyConfigState0Cid
144+ externalPartyConfigState1Cid
212145
213- -- current latest active round is 5
214- latestActiveRoundCid <- advanceRound app (Round 5)
146+ _ <- advanceRound app (Round 5)
215147
216148 normalizedBalanceBeforeExpiry <- getNormalizedBalance app.dso
217149
218150 -- 2 rounds before current latest active round => 5 - 2 = 3 which is equal to round 3 when the amulet expires
219151 -- DSO party expires the amulet
220- result <- checkTxMetadata app TxKind_ExpireDust alice.primaryParty $
152+ (externalPartyConfigState0Cid, externalPartyConfigState1Cid) <- getExternalPartyConfigStates app
153+ result <- checkTxMetadata' app TxKind_ExpireDust alice.primaryParty $
221154 testChoice [app.dso] [app.dso] $
222- exerciseCmd cid (Amulet_Expire latestActiveRoundCid)
155+ exerciseCmd cid $ Amulet_ExpireV2 with
156+ externalPartyConfigState0Cid
157+ externalPartyConfigState1Cid
223158 let expireSummary = result.expireSum
224159
225160 normalizedBalanceAfterExpiry <- getNormalizedBalance app.dso
@@ -243,8 +178,16 @@ advanceRound app expectedRound = do
243178 (latestActiveRoundCid, _) <- getLatestActiveOpenRound app
244179 Some(currentRound) <- queryContractId @OpenMiningRound app.dso latestActiveRoundCid
245180 currentRound.round === expectedRound
181+ syncExternalPartyConfigStatesWithRounds app
246182 pure latestActiveRoundCid
247183
184+ syncExternalPartyConfigStatesWithRounds : AmuletApp -> Script ()
185+ syncExternalPartyConfigStatesWithRounds app = do
186+ passTime (hours 24)
187+ updateExternalPartyConfigState app
188+ passTime (hours 24)
189+ updateExternalPartyConfigState app
190+
248191addRelRoundN : Int -> Time -> RelTime -> Time
249192addRelRoundN n t relTime
250193 | n <= 0 = t
0 commit comments