diff --git a/apps/app/src/main/scala/org/lfdecentralizedtrust/splice/config/ConfigTransforms.scala b/apps/app/src/main/scala/org/lfdecentralizedtrust/splice/config/ConfigTransforms.scala index 01549c4d28..7126c5e2fe 100644 --- a/apps/app/src/main/scala/org/lfdecentralizedtrust/splice/config/ConfigTransforms.scala +++ b/apps/app/src/main/scala/org/lfdecentralizedtrust/splice/config/ConfigTransforms.scala @@ -200,6 +200,7 @@ object ConfigTransforms { updateAllAutomationConfigs( _.copy(rewardOperationRoundsCloseBufferDuration = NonNegativeFiniteDuration.ofMillis(100)) ), + disableDevelopmentFund(), ) } @@ -299,6 +300,9 @@ object ConfigTransforms { def disableZeroFees(): ConfigTransform = updateAllSvAppFoundDsoConfigs_(c => c.copy(zeroTransferFees = false)) + def disableDevelopmentFund(): ConfigTransform = + updateAllSvAppFoundDsoConfigs_(c => c.copy(developmentFundPercentage = Some(0.0))) + def updateAllValidatorAppConfigs( update: (String, ValidatorAppBackendConfig) => ValidatorAppBackendConfig ): ConfigTransform = @@ -718,6 +722,9 @@ object ConfigTransforms { } } + def withDevelopmentFundPercentage(percentage: BigDecimal): ConfigTransform = + updateAllSvAppFoundDsoConfigs_(c => c.copy(developmentFundPercentage = Some(percentage))) + private def portTransform(bump: Int, c: AdminServerConfig): AdminServerConfig = c.copy(internalPort = c.internalPort.map(_ + bump)) diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/AppUpgradeIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/AppUpgradeIntegrationTest.scala index 07ea6d7b20..d386eb2142 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/AppUpgradeIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/AppUpgradeIntegrationTest.scala @@ -258,6 +258,7 @@ class AppUpgradeIntegrationTest ), java.util.Optional.empty(), java.util.Optional.empty(), + java.util.Optional.empty(), ) val upgradeAction = new ARC_AmuletRules( new CRARC_SetConfig( diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/BootstrapPackageConfigIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/BootstrapPackageConfigIntegrationTest.scala index 61baa81af7..d5f0eaeb1d 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/BootstrapPackageConfigIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/BootstrapPackageConfigIntegrationTest.scala @@ -229,6 +229,7 @@ class BootstrapPackageConfigIntegrationTest extends IntegrationTest with Splitwe ), java.util.Optional.empty(), java.util.Optional.empty(), + java.util.Optional.empty(), ) val upgradeAction = new ARC_AmuletRules( @@ -368,6 +369,7 @@ class BootstrapPackageConfigIntegrationTest extends IntegrationTest with Splitwe amuletConfig.packageConfig, java.util.Optional.empty(), java.util.Optional.empty(), + java.util.Optional.empty(), ) val upgradeAction = new ARC_AmuletRules( diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DevelopmentFundCouponIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DevelopmentFundCouponIntegrationTest.scala new file mode 100644 index 0000000000..a06485c777 --- /dev/null +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DevelopmentFundCouponIntegrationTest.scala @@ -0,0 +1,99 @@ +package org.lfdecentralizedtrust.splice.integration.tests + +import com.digitalasset.canton.config.NonNegativeFiniteDuration +import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet.UnclaimedDevelopmentFundCoupon +import org.lfdecentralizedtrust.splice.config.ConfigTransforms +import org.lfdecentralizedtrust.splice.config.ConfigTransforms.{ + ConfigurableApp, + updateAutomationConfig, +} +import org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition +import org.lfdecentralizedtrust.splice.sv.automation.delegatebased.AdvanceOpenMiningRoundTrigger +import org.lfdecentralizedtrust.splice.util.{TriggerTestUtil, WalletTestUtil} + +@org.lfdecentralizedtrust.splice.util.scalatesttags.SpliceDsoGovernance_0_1_21 +class DevelopmentFundCouponIntegrationTest + extends SvIntegrationTestBase + with TriggerTestUtil + with WalletTestUtil { + + private val threshold = 3 + + override def environmentDefinition + : org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition = + EnvironmentDefinition + .simpleTopology1Sv(this.getClass.getSimpleName) + .addConfigTransforms((_, config) => + updateAutomationConfig(ConfigurableApp.Sv)( + _.withPausedTrigger[AdvanceOpenMiningRoundTrigger] + )(config) + ) + .addConfigTransform((_, config) => + ConfigTransforms.updateInitialTickDuration(NonNegativeFiniteDuration.ofMillis(500))(config) + ) + .addConfigTransforms((_, config) => + ConfigTransforms.updateAllSvAppConfigs_( + _.copy( + unclaimedDevelopmentFundCouponsThreshold = threshold + ) + )(config) + ) + .addConfigTransform((_, config) => + ConfigTransforms.withDevelopmentFundPercentage(0.05)(config) + ) + + "UnclaimedDevelopmentFundCoupons are merged" in { implicit env => + val (_, couponAmount) = actAndCheck( + "Advance 5 rounds", { + Range(0, 5).foreach(_ => advanceRoundsByOneTickViaAutomation()) + }, + )( + "5 UnclaimedDevelopmentFundCoupons are created, and the trigger does not merge the coupons, " + + "as it only acts when the number of coupons is ≥ 2 × threshold", + _ => { + val coupons = sv1Backend.participantClient.ledger_api_extensions.acs + .filterJava(UnclaimedDevelopmentFundCoupon.COMPANION)(dsoParty) + coupons.size shouldBe 5 + coupons.head.data.amount + }, + ) + + actAndCheck( + "Advance one round to create one more UnclaimedDevelopmentFundCoupon, reaching 2 × threshold coupons", + advanceRoundsByOneTickViaAutomation(), + )( + "The MergeUnclaimedDevelopmentFundCouponsTrigger is triggered and merges the smallest three coupons (threshold), " + + "while keeping the remaining coupons unchanged.", + _ => { + sv1Backend.participantClient.ledger_api_extensions.acs + .filterJava(UnclaimedDevelopmentFundCoupon.COMPANION)(dsoParty) + .map(_.data.amount) + .sorted shouldBe Seq( + couponAmount, + couponAmount, + couponAmount, + couponAmount.multiply(new java.math.BigDecimal(3)), + ) + }, + ) + + actAndCheck( + "Advance two rounds to create two more UnclaimedDevelopmentFundCoupon, " + + "reaching 2 × threshold coupons and triggering a second merge", + Range(0, 2).foreach(_ => advanceRoundsByOneTickViaAutomation()), + )( + "The MergeUnclaimedDevelopmentFundCouponsTrigger merges the `threshold` smallest coupons", + _ => { + sv1Backend.participantClient.ledger_api_extensions.acs + .filterJava(UnclaimedDevelopmentFundCoupon.COMPANION)(dsoParty) + .map(_.data.amount) + .sorted shouldBe Seq( + couponAmount, + couponAmount, + couponAmount.multiply(new java.math.BigDecimal(3)), + couponAmount.multiply(new java.math.BigDecimal(3)), + ) + }, + ) + } +} diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DisabledWalletTimeBasedIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DisabledWalletTimeBasedIntegrationTest.scala index ac9890a644..478378946d 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DisabledWalletTimeBasedIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DisabledWalletTimeBasedIntegrationTest.scala @@ -68,7 +68,7 @@ class DisabledWalletTimeBasedIntegrationTest val expectedMinAmount = BigDecimal( computeSvRewardInRound0( - defaultIssuanceCurve.initialValue, + defaultIssuanceCurve().initialValue, defaultTickDuration, dsoSize = 1, ) diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanWithGradualStartsTimeBasedIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanWithGradualStartsTimeBasedIntegrationTest.scala index d3487d4b62..2e730e1ec2 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanWithGradualStartsTimeBasedIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanWithGradualStartsTimeBasedIntegrationTest.scala @@ -143,7 +143,7 @@ class ScanWithGradualStartsTimeBasedIntegrationTest val svRewardPerRound = BigDecimal( computeSvRewardInRound0( - defaultIssuanceCurve.initialValue, + defaultIssuanceCurve().initialValue, defaultTickDuration, dsoSize = 2, ) diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvReconcileSynchronizerConfigIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvReconcileSynchronizerConfigIntegrationTest.scala index 48f9dd9989..90c8f4d9bb 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvReconcileSynchronizerConfigIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvReconcileSynchronizerConfigIntegrationTest.scala @@ -141,6 +141,7 @@ class SvReconcileSynchronizerConfigIntegrationTest extends SvIntegrationTestBase amuletConfig.packageConfig, java.util.Optional.empty(), java.util.Optional.empty(), + java.util.Optional.empty(), ) } diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvStateManagementIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvStateManagementIntegrationTest.scala index 8d2c88602d..d0475b025c 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvStateManagementIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvStateManagementIntegrationTest.scala @@ -487,6 +487,7 @@ class SvStateManagementIntegrationTest extends SvIntegrationTestBase with Trigge initialConfig.packageConfig, java.util.Optional.empty(), java.util.Optional.empty(), + java.util.Optional.empty(), ) val (_, voteRequestCid) = actAndCheck( diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvTimeBasedRewardCouponIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvTimeBasedRewardCouponIntegrationTest.scala index c0f8819ef5..09d1e6773f 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvTimeBasedRewardCouponIntegrationTest.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/SvTimeBasedRewardCouponIntegrationTest.scala @@ -170,7 +170,7 @@ class SvTimeBasedRewardCouponIntegrationTest } val eachSvGetInRound0 = - computeSvRewardInRound0(defaultIssuanceCurve.initialValue, defaultTickDuration, svs.size) + computeSvRewardInRound0(defaultIssuanceCurve().initialValue, defaultTickDuration, svs.size) val sv1Party = sv1Backend.getDsoInfo().svParty val aliceValidatorParty = aliceValidatorBackend.getValidatorPartyId() val expectedAliceAmount = eachSvGetInRound0.multiply(new java.math.BigDecimal("0.3333")) diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/util/AmuletConfigUtil.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/util/AmuletConfigUtil.scala index 7e7727c5fd..c823c5c28d 100644 --- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/util/AmuletConfigUtil.scala +++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/util/AmuletConfigUtil.scala @@ -64,6 +64,7 @@ trait AmuletConfigUtil extends TestCommon { existingAmuletConfig.packageConfig, existingAmuletConfig.transferPreapprovalFee, existingAmuletConfig.featuredAppActivityMarkerAmount, + existingAmuletConfig.optDevelopmentFundManager, ) } diff --git a/apps/common/frontend-test-handlers/src/mocks/helpers/amulet-config-helper.ts b/apps/common/frontend-test-handlers/src/mocks/helpers/amulet-config-helper.ts index c898896538..f829f9abdc 100644 --- a/apps/common/frontend-test-handlers/src/mocks/helpers/amulet-config-helper.ts +++ b/apps/common/frontend-test-handlers/src/mocks/helpers/amulet-config-helper.ts @@ -158,6 +158,7 @@ export function getAmuletRulesConfig( amuletToIssuePerYear: '40000000000.0', validatorRewardCap: '0.2', optValidatorFaucetCap: '2.85', + optDevelopmentFundPercentage: '0.05', }, futureValues: [ { @@ -172,6 +173,7 @@ export function getAmuletRulesConfig( amuletToIssuePerYear: '20000000000.0', validatorRewardCap: '0.2', optValidatorFaucetCap: '2.85', + optDevelopmentFundPercentage: '0.05', }, }, { @@ -186,6 +188,7 @@ export function getAmuletRulesConfig( amuletToIssuePerYear: '10000000000.0', validatorRewardCap: '0.2', optValidatorFaucetCap: '2.85', + optDevelopmentFundPercentage: '0.05', }, }, { @@ -200,6 +203,7 @@ export function getAmuletRulesConfig( amuletToIssuePerYear: '5000000000.0', validatorRewardCap: '0.2', optValidatorFaucetCap: '2.85', + optDevelopmentFundPercentage: '0.05', }, }, { @@ -214,12 +218,14 @@ export function getAmuletRulesConfig( amuletToIssuePerYear: '2500000000.0', validatorRewardCap: '0.2', optValidatorFaucetCap: '2.85', + optDevelopmentFundPercentage: '0.05', }, }, ], }, transferPreapprovalFee: null, featuredAppActivityMarkerAmount: null, + optDevelopmentFundManager: null, }; } @@ -306,7 +312,8 @@ export function getExpectedAmuletRulesConfigDiffsHTML( "validatorRewardCap": "0.2", "featuredAppRewardCap": "100.0", "unfeaturedAppRewardCap": "0.6", - "optValidatorFaucetCap": "2.85" + "optValidatorFaucetCap": "2.85", + "optDevelopmentFundPercentage": "0.05" }, "futureValues": [ { @@ -320,7 +327,8 @@ export function getExpectedAmuletRulesConfigDiffsHTML( "validatorRewardCap": "0.2", "featuredAppRewardCap": "100.0", "unfeaturedAppRewardCap": "0.6", - "optValidatorFaucetCap": "2.85" + "optValidatorFaucetCap": "2.85", + "optDevelopmentFundPercentage": "0.05" } }, { @@ -334,7 +342,8 @@ export function getExpectedAmuletRulesConfigDiffsHTML( "validatorRewardCap": "0.2", "featuredAppRewardCap": "100.0", "unfeaturedAppRewardCap": "0.6", - "optValidatorFaucetCap": "2.85" + "optValidatorFaucetCap": "2.85", + "optDevelopmentFundPercentage": "0.05" } }, { @@ -348,7 +357,8 @@ export function getExpectedAmuletRulesConfigDiffsHTML( "validatorRewardCap": "0.2", "featuredAppRewardCap": "100.0", "unfeaturedAppRewardCap": "0.6", - "optValidatorFaucetCap": "2.85" + "optValidatorFaucetCap": "2.85", + "optDevelopmentFundPercentage": "0.05" } }, { @@ -362,12 +372,16 @@ export function getExpectedAmuletRulesConfigDiffsHTML( "validatorRewardCap": "0.2", "featuredAppRewardCap": "100.0", "unfeaturedAppRewardCap": "0.6", - "optValidatorFaucetCap": "2.85" + "optValidatorFaucetCap": "2.85", + "optDevelopmentFundPercentage": "0.05" } } ] -}
null
{
"amulet": "0.1.8",
diff --git a/apps/common/frontend/src/__tests__/mocks/constants.ts b/apps/common/frontend/src/__tests__/mocks/constants.ts
index 771235dc75..10b02c36ab 100644
--- a/apps/common/frontend/src/__tests__/mocks/constants.ts
+++ b/apps/common/frontend/src/__tests__/mocks/constants.ts
@@ -90,6 +90,7 @@ export const plannedVoteResult: DsoRules_CloseVoteRequestResult = {
featuredAppRewardCap: '100.0',
unfeaturedAppRewardCap: '0.6',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
futureValues: [
{
@@ -104,6 +105,7 @@ export const plannedVoteResult: DsoRules_CloseVoteRequestResult = {
featuredAppRewardCap: '100.0',
unfeaturedAppRewardCap: '0.6',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -118,6 +120,7 @@ export const plannedVoteResult: DsoRules_CloseVoteRequestResult = {
featuredAppRewardCap: '100.0',
unfeaturedAppRewardCap: '0.6',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -132,6 +135,7 @@ export const plannedVoteResult: DsoRules_CloseVoteRequestResult = {
featuredAppRewardCap: '100.0',
unfeaturedAppRewardCap: '0.6',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -146,6 +150,7 @@ export const plannedVoteResult: DsoRules_CloseVoteRequestResult = {
featuredAppRewardCap: '100.0',
unfeaturedAppRewardCap: '0.6',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
],
@@ -186,6 +191,7 @@ export const plannedVoteResult: DsoRules_CloseVoteRequestResult = {
},
transferPreapprovalFee: null,
featuredAppActivityMarkerAmount: null,
+ optDevelopmentFundManager: null,
},
},
},
diff --git a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/DarResources.scala b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/DarResources.scala
index 1c1fc789c3..9fa7e68b08 100644
--- a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/DarResources.scala
+++ b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/DarResources.scala
@@ -75,6 +75,7 @@ object DarResources {
val amulet_0_1_12 = DarResource("splice-amulet-0.1.12.dar")
val amulet_0_1_13 = DarResource("splice-amulet-0.1.13.dar")
val amulet_0_1_14 = DarResource("splice-amulet-0.1.14.dar")
+ val amulet_0_1_15 = DarResource("splice-amulet-0.1.15.dar")
val amulet_current = DarResource("splice-amulet-current.dar")
val amulet = PackageResource(
amulet_current,
@@ -95,6 +96,7 @@ object DarResources {
amulet_0_1_12,
amulet_0_1_13,
amulet_0_1_14,
+ amulet_0_1_15,
),
)
@@ -119,6 +121,7 @@ object DarResources {
val dsoGovernance_0_1_18 = DarResource("splice-dso-governance-0.1.18.dar")
val dsoGovernance_0_1_19 = DarResource("splice-dso-governance-0.1.19.dar")
val dsoGovernance_0_1_20 = DarResource("splice-dso-governance-0.1.20.dar")
+ val dsoGovernance_0_1_21 = DarResource("splice-dso-governance-0.1.21.dar")
val dsoGovernance_current = DarResource("splice-dso-governance-current.dar")
val dsoGovernance = PackageResource(
dsoGovernance_current,
@@ -145,6 +148,7 @@ object DarResources {
dsoGovernance_0_1_18,
dsoGovernance_0_1_19,
dsoGovernance_0_1_20,
+ dsoGovernance_0_1_21,
),
)
@@ -164,6 +168,7 @@ object DarResources {
val amuletNameService_0_1_13 = DarResource("splice-amulet-name-service-0.1.13.dar")
val amuletNameService_0_1_14 = DarResource("splice-amulet-name-service-0.1.14.dar")
val amuletNameService_0_1_15 = DarResource("splice-amulet-name-service-0.1.15.dar")
+ val amuletNameService_0_1_16 = DarResource("splice-amulet-name-service-0.1.16.dar")
val amuletNameService_current = DarResource("splice-amulet-name-service-current.dar")
val amuletNameService = PackageResource(
amuletNameService_current,
@@ -185,6 +190,7 @@ object DarResources {
amuletNameService_0_1_13,
amuletNameService_0_1_14,
amuletNameService_0_1_15,
+ amuletNameService_0_1_16,
),
)
@@ -203,6 +209,7 @@ object DarResources {
val splitwell_0_1_12 = DarResource("splitwell-0.1.12.dar")
val splitwell_0_1_13 = DarResource("splitwell-0.1.13.dar")
val splitwell_0_1_14 = DarResource("splitwell-0.1.14.dar")
+ val splitwell_0_1_15 = DarResource("splitwell-0.1.15.dar")
val splitwell_current = DarResource("splitwell-current.dar")
val splitwell = PackageResource(
splitwell_current,
@@ -223,6 +230,7 @@ object DarResources {
splitwell_0_1_12,
splitwell_0_1_13,
splitwell_0_1_14,
+ splitwell_0_1_15,
),
)
@@ -241,6 +249,7 @@ object DarResources {
val wallet_0_1_12 = DarResource("splice-wallet-0.1.12.dar")
val wallet_0_1_13 = DarResource("splice-wallet-0.1.13.dar")
val wallet_0_1_14 = DarResource("splice-wallet-0.1.14.dar")
+ val wallet_0_1_15 = DarResource("splice-wallet-0.1.15.dar")
val wallet_current = DarResource("splice-wallet-current.dar")
val wallet = PackageResource(
wallet_current,
@@ -261,6 +270,7 @@ object DarResources {
wallet_0_1_12,
wallet_0_1_13,
wallet_0_1_14,
+ wallet_0_1_15,
),
)
@@ -279,6 +289,7 @@ object DarResources {
val walletPayments_0_1_12 = DarResource("splice-wallet-payments-0.1.12.dar")
val walletPayments_0_1_13 = DarResource("splice-wallet-payments-0.1.13.dar")
val walletPayments_0_1_14 = DarResource("splice-wallet-payments-0.1.14.dar")
+ val walletPayments_0_1_15 = DarResource("splice-wallet-payments-0.1.15.dar")
val walletPayments_current = DarResource("splice-wallet-payments-current.dar")
val walletPayments = PackageResource(
walletPayments_current,
@@ -299,6 +310,7 @@ object DarResources {
walletPayments_0_1_12,
walletPayments_0_1_13,
walletPayments_0_1_14,
+ walletPayments_0_1_15,
),
)
@@ -308,6 +320,7 @@ object DarResources {
val validatorLifecycle_0_1_3 = DarResource("splice-validator-lifecycle-0.1.3.dar")
val validatorLifecycle_0_1_4 = DarResource("splice-validator-lifecycle-0.1.4.dar")
val validatorLifecycle_0_1_5 = DarResource("splice-validator-lifecycle-0.1.5.dar")
+ val validatorLifecycle_0_1_6 = DarResource("splice-validator-lifecycle-0.1.6.dar")
val validatorLifecycle_current = DarResource("splice-validator-lifecycle-current.dar")
val validatorLifecycle = PackageResource(
validatorLifecycle_current,
@@ -319,6 +332,7 @@ object DarResources {
validatorLifecycle_0_1_3,
validatorLifecycle_0_1_4,
validatorLifecycle_0_1_5,
+ validatorLifecycle_0_1_6,
),
)
diff --git a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/PackageVersionSupport.scala b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/PackageVersionSupport.scala
index a839a1609e..c626859876 100644
--- a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/PackageVersionSupport.scala
+++ b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/PackageVersionSupport.scala
@@ -108,6 +108,18 @@ trait PackageVersionSupport extends NamedLogging {
)
}
+ def supportDevelopmentFund(parties: Seq[PartyId], now: CantonTimestamp)(implicit
+ tc: TraceContext
+ ): Future[FeatureSupport] = {
+ isDarSupported(
+ parties,
+ PackageIdResolver.Package.SpliceDsoGovernance,
+ now,
+ DarResources.dsoGovernance,
+ DarResources.dsoGovernance_0_1_21,
+ )
+ }
+
private def isDarSupported(
parties: Seq[PartyId],
packageId: PackageIdResolver.Package,
diff --git a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/util/SpliceUtil.scala b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/util/SpliceUtil.scala
index 789e47683f..6e02ea8f39 100644
--- a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/util/SpliceUtil.scala
+++ b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/util/SpliceUtil.scala
@@ -207,6 +207,7 @@ object SpliceUtil {
amuletsToIssuePerYear: Double,
validatorPercentage: Double,
appPercentage: Double,
+ developmentFundPercentage: Option[BigDecimal] = None,
): splice.issuance.IssuanceConfig = new IssuanceConfig(
damlDecimal(amuletsToIssuePerYear),
damlDecimal(validatorPercentage),
@@ -223,18 +224,29 @@ object SpliceUtil {
// validatorFaucetCap
Some(damlDecimal(2.85)).toJava,
+
+ // developmentFundPercentage
+ developmentFundPercentage.map(damlDecimal).toJava,
)
private def hours(h: Long): RelTime = new RelTime(TimeUnit.HOURS.toMicros(h))
- val defaultIssuanceCurve: splice.schedule.Schedule[RelTime, IssuanceConfig] =
+ def defaultIssuanceCurve(
+ developmentFundPercentage: Option[BigDecimal] = None
+ ): splice.schedule.Schedule[RelTime, IssuanceConfig] =
new Schedule(
- issuanceConfig(40e9, 0.05, 0.15),
+ issuanceConfig(40e9, 0.05, 0.15, developmentFundPercentage),
Seq(
- new Tuple2(hours(365 * 12), issuanceConfig(20e9, 0.12, 0.4)),
- new Tuple2(hours(3 * 365 * 12), issuanceConfig(10e9, 0.18, 0.62)),
- new Tuple2(hours(5 * 365 * 24), issuanceConfig(5e9, 0.21, 0.69)),
- new Tuple2(hours(10 * 365 * 24), issuanceConfig(2.5e9, 0.20, 0.75)),
+ new Tuple2(hours(365 * 12), issuanceConfig(20e9, 0.12, 0.4, developmentFundPercentage)),
+ new Tuple2(
+ hours(3 * 365 * 12),
+ issuanceConfig(10e9, 0.18, 0.62, developmentFundPercentage),
+ ),
+ new Tuple2(hours(5 * 365 * 24), issuanceConfig(5e9, 0.21, 0.69, developmentFundPercentage)),
+ new Tuple2(
+ hours(10 * 365 * 24),
+ issuanceConfig(2.5e9, 0.20, 0.75, developmentFundPercentage),
+ ),
).asJava,
)
@@ -365,13 +377,14 @@ object SpliceUtil {
transferPreapprovalFee: Option[BigDecimal] = None,
featuredAppActivityMarkerAmount: Option[BigDecimal] = None,
nextSynchronizerId: Option[SynchronizerId] = None,
+ developmentFundPercentage: Option[BigDecimal] = None,
): splice.amuletconfig.AmuletConfig[splice.amuletconfig.USD] =
new splice.amuletconfig.AmuletConfig(
// transferConfig
defaultTransferConfig(initialMaxNumInputs, holdingFee, zeroTransferFees = zeroTransferFees),
// issuance curve
- defaultIssuanceCurve,
+ defaultIssuanceCurve(developmentFundPercentage),
// global domain config
defaultDecentralizedSynchronizerConfig(
@@ -389,6 +402,7 @@ object SpliceUtil {
initialPackageConfig,
transferPreapprovalFee.map(_.bigDecimal).toJava,
featuredAppActivityMarkerAmount.map(_.bigDecimal).toJava,
+ Optional.empty(),
)
def defaultAnsConfig(
diff --git a/apps/common/src/test/java/org/lfdecentralizedtrust/splice/util/scalatesttags/SpliceDsoGovernance_0_1_21.java b/apps/common/src/test/java/org/lfdecentralizedtrust/splice/util/scalatesttags/SpliceDsoGovernance_0_1_21.java
new file mode 100644
index 0000000000..ac24eab842
--- /dev/null
+++ b/apps/common/src/test/java/org/lfdecentralizedtrust/splice/util/scalatesttags/SpliceDsoGovernance_0_1_21.java
@@ -0,0 +1,14 @@
+package org.lfdecentralizedtrust.splice.util.scalatesttags;
+
+import org.scalatest.TagAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// Don't run this test when testing against splice-dso-governance < 0.1.21
+@TagAnnotation
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface SpliceDsoGovernance_0_1_21 {}
diff --git a/apps/package-lock.json b/apps/package-lock.json
index 889a7e583d..3ea50e1ec5 100644
--- a/apps/package-lock.json
+++ b/apps/package-lock.json
@@ -27,13 +27,13 @@
"wallet/external-openapi-ts-client"
],
"dependencies": {
- "@daml.js/ans": "file:common/frontend/daml.js/splice-amulet-name-service-0.1.15",
- "@daml.js/splice-amulet": "file:common/frontend/daml.js/splice-amulet-0.1.14",
- "@daml.js/splice-dso-governance": "file:common/frontend/daml.js/splice-dso-governance-0.1.20",
- "@daml.js/splice-validator-lifecycle": "file:common/frontend/daml.js/splice-validator-lifecycle-0.1.5",
- "@daml.js/splice-wallet": "file:common/frontend/daml.js/splice-wallet-0.1.14",
- "@daml.js/splice-wallet-payments": "file:common/frontend/daml.js/splice-wallet-payments-0.1.14",
- "@daml.js/splitwell": "file:common/frontend/daml.js/splitwell-0.1.14",
+ "@daml.js/ans": "file:common/frontend/daml.js/splice-amulet-name-service-0.1.16",
+ "@daml.js/splice-amulet": "file:common/frontend/daml.js/splice-amulet-0.1.15",
+ "@daml.js/splice-dso-governance": "file:common/frontend/daml.js/splice-dso-governance-0.1.21",
+ "@daml.js/splice-validator-lifecycle": "file:common/frontend/daml.js/splice-validator-lifecycle-0.1.6",
+ "@daml.js/splice-wallet": "file:common/frontend/daml.js/splice-wallet-0.1.15",
+ "@daml.js/splice-wallet-payments": "file:common/frontend/daml.js/splice-wallet-payments-0.1.15",
+ "@daml.js/splitwell": "file:common/frontend/daml.js/splitwell-0.1.15",
"xunit-viewer": "^10.6.1"
}
},
@@ -320,6 +320,25 @@
"common/frontend/daml.js/splice-amulet-0.1.14": {
"name": "@daml.js/splice-amulet-0.1.14",
"version": "0.0.0",
+ "extraneous": true,
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Set-Types-1.0.0": "file:../daml-stdlib-DA-Set-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@daml.js/splice-api-featured-app-v1-1.0.0": "file:../splice-api-featured-app-v1-1.0.0",
+ "@daml.js/splice-api-token-allocation-instruction-v1-1.0.0": "file:../splice-api-token-allocation-instruction-v1-1.0.0",
+ "@daml.js/splice-api-token-allocation-v1-1.0.0": "file:../splice-api-token-allocation-v1-1.0.0",
+ "@daml.js/splice-api-token-holding-v1-1.0.0": "file:../splice-api-token-holding-v1-1.0.0",
+ "@daml.js/splice-api-token-metadata-v1-1.0.0": "file:../splice-api-token-metadata-v1-1.0.0",
+ "@daml.js/splice-api-token-transfer-instruction-v1-1.0.0": "file:../splice-api-token-transfer-instruction-v1-1.0.0",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
+ "common/frontend/daml.js/splice-amulet-0.1.15": {
+ "name": "@daml.js/splice-amulet-0.1.15",
+ "version": "0.0.0",
"license": "UNLICENSED",
"dependencies": {
"@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
@@ -422,6 +441,7 @@
"common/frontend/daml.js/splice-amulet-name-service-0.1.15": {
"name": "@daml.js/splice-amulet-name-service-0.1.15",
"version": "0.0.0",
+ "extraneous": true,
"license": "UNLICENSED",
"dependencies": {
"@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
@@ -432,6 +452,19 @@
"@mojotech/json-type-validation": "^3.1.0"
}
},
+ "common/frontend/daml.js/splice-amulet-name-service-0.1.16": {
+ "name": "@daml.js/splice-amulet-name-service-0.1.16",
+ "version": "0.0.0",
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@daml.js/splice-amulet-0.1.15": "file:../splice-amulet-0.1.15",
+ "@daml.js/splice-api-featured-app-v1-1.0.0": "file:../splice-api-featured-app-v1-1.0.0",
+ "@daml.js/splice-wallet-payments-0.1.15": "file:../splice-wallet-payments-0.1.15",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
"common/frontend/daml.js/splice-amulet-name-service-0.1.9": {
"name": "@daml.js/splice-amulet-name-service-0.1.9",
"version": "0.0.0",
@@ -555,6 +588,7 @@
"common/frontend/daml.js/splice-dso-governance-0.1.20": {
"name": "@daml.js/splice-dso-governance-0.1.20",
"version": "0.0.0",
+ "extraneous": true,
"license": "UNLICENSED",
"dependencies": {
"@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
@@ -567,6 +601,21 @@
"@mojotech/json-type-validation": "^3.1.0"
}
},
+ "common/frontend/daml.js/splice-dso-governance-0.1.21": {
+ "name": "@daml.js/splice-dso-governance-0.1.21",
+ "version": "0.0.0",
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Set-Types-1.0.0": "file:../daml-stdlib-DA-Set-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@daml.js/splice-amulet-0.1.15": "file:../splice-amulet-0.1.15",
+ "@daml.js/splice-amulet-name-service-0.1.16": "file:../splice-amulet-name-service-0.1.16",
+ "@daml.js/splice-wallet-payments-0.1.15": "file:../splice-wallet-payments-0.1.15",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
"common/frontend/daml.js/splice-validator-lifecycle-0.1.3": {
"name": "@daml.js/splice-validator-lifecycle-0.1.3",
"version": "0.0.0",
@@ -590,6 +639,16 @@
"common/frontend/daml.js/splice-validator-lifecycle-0.1.5": {
"name": "@daml.js/splice-validator-lifecycle-0.1.5",
"version": "0.0.0",
+ "extraneous": true,
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
+ "common/frontend/daml.js/splice-validator-lifecycle-0.1.6": {
+ "name": "@daml.js/splice-validator-lifecycle-0.1.6",
+ "version": "0.0.0",
"license": "UNLICENSED",
"dependencies": {
"@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
@@ -667,6 +726,7 @@
"common/frontend/daml.js/splice-wallet-0.1.14": {
"name": "@daml.js/splice-wallet-0.1.14",
"version": "0.0.0",
+ "extraneous": true,
"license": "UNLICENSED",
"dependencies": {
"@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
@@ -680,6 +740,22 @@
"@mojotech/json-type-validation": "^3.1.0"
}
},
+ "common/frontend/daml.js/splice-wallet-0.1.15": {
+ "name": "@daml.js/splice-wallet-0.1.15",
+ "version": "0.0.0",
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@daml.js/splice-amulet-0.1.15": "file:../splice-amulet-0.1.15",
+ "@daml.js/splice-api-token-allocation-instruction-v1-1.0.0": "file:../splice-api-token-allocation-instruction-v1-1.0.0",
+ "@daml.js/splice-api-token-allocation-v1-1.0.0": "file:../splice-api-token-allocation-v1-1.0.0",
+ "@daml.js/splice-api-token-transfer-instruction-v1-1.0.0": "file:../splice-api-token-transfer-instruction-v1-1.0.0",
+ "@daml.js/splice-wallet-payments-0.1.15": "file:../splice-wallet-payments-0.1.15",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
"common/frontend/daml.js/splice-wallet-0.1.9": {
"name": "@daml.js/splice-wallet-0.1.9",
"version": "0.0.0",
@@ -752,6 +828,7 @@
"common/frontend/daml.js/splice-wallet-payments-0.1.14": {
"name": "@daml.js/splice-wallet-payments-0.1.14",
"version": "0.0.0",
+ "extraneous": true,
"license": "UNLICENSED",
"dependencies": {
"@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
@@ -761,6 +838,18 @@
"@mojotech/json-type-validation": "^3.1.0"
}
},
+ "common/frontend/daml.js/splice-wallet-payments-0.1.15": {
+ "name": "@daml.js/splice-wallet-payments-0.1.15",
+ "version": "0.0.0",
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@daml.js/splice-amulet-0.1.15": "file:../splice-amulet-0.1.15",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
"common/frontend/daml.js/splice-wallet-payments-0.1.9": {
"name": "@daml.js/splice-wallet-payments-0.1.9",
"version": "0.0.0",
@@ -833,6 +922,7 @@
"common/frontend/daml.js/splitwell-0.1.14": {
"name": "@daml.js/splitwell-0.1.14",
"version": "0.0.0",
+ "extraneous": true,
"license": "UNLICENSED",
"dependencies": {
"@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
@@ -843,6 +933,19 @@
"@mojotech/json-type-validation": "^3.1.0"
}
},
+ "common/frontend/daml.js/splitwell-0.1.15": {
+ "name": "@daml.js/splitwell-0.1.15",
+ "version": "0.0.0",
+ "license": "UNLICENSED",
+ "dependencies": {
+ "@daml.js/daml-prim-DA-Types-1.0.0": "file:../daml-prim-DA-Types-1.0.0",
+ "@daml.js/daml-stdlib-DA-Time-Types-1.0.0": "file:../daml-stdlib-DA-Time-Types-1.0.0",
+ "@daml.js/ghc-stdlib-DA-Internal-Template-1.0.0": "file:../ghc-stdlib-DA-Internal-Template-1.0.0",
+ "@daml.js/splice-amulet-0.1.15": "file:../splice-amulet-0.1.15",
+ "@daml.js/splice-wallet-payments-0.1.15": "file:../splice-wallet-payments-0.1.15",
+ "@mojotech/json-type-validation": "^3.1.0"
+ }
+ },
"common/frontend/daml.js/splitwell-0.1.9": {
"name": "@daml.js/splitwell-0.1.9",
"version": "0.0.0",
@@ -1312,7 +1415,7 @@
}
},
"node_modules/@daml.js/ans": {
- "resolved": "common/frontend/daml.js/splice-amulet-name-service-0.1.15",
+ "resolved": "common/frontend/daml.js/splice-amulet-name-service-0.1.16",
"link": true
},
"node_modules/@daml.js/daml-prim-DA-Types-1.0.0": {
@@ -1332,15 +1435,15 @@
"link": true
},
"node_modules/@daml.js/splice-amulet": {
- "resolved": "common/frontend/daml.js/splice-amulet-0.1.14",
+ "resolved": "common/frontend/daml.js/splice-amulet-0.1.15",
"link": true
},
- "node_modules/@daml.js/splice-amulet-0.1.14": {
- "resolved": "common/frontend/daml.js/splice-amulet-0.1.14",
+ "node_modules/@daml.js/splice-amulet-0.1.15": {
+ "resolved": "common/frontend/daml.js/splice-amulet-0.1.15",
"link": true
},
- "node_modules/@daml.js/splice-amulet-name-service-0.1.15": {
- "resolved": "common/frontend/daml.js/splice-amulet-name-service-0.1.15",
+ "node_modules/@daml.js/splice-amulet-name-service-0.1.16": {
+ "resolved": "common/frontend/daml.js/splice-amulet-name-service-0.1.16",
"link": true
},
"node_modules/@daml.js/splice-api-featured-app-v1-1.0.0": {
@@ -1380,27 +1483,27 @@
"link": true
},
"node_modules/@daml.js/splice-dso-governance": {
- "resolved": "common/frontend/daml.js/splice-dso-governance-0.1.20",
+ "resolved": "common/frontend/daml.js/splice-dso-governance-0.1.21",
"link": true
},
"node_modules/@daml.js/splice-validator-lifecycle": {
- "resolved": "common/frontend/daml.js/splice-validator-lifecycle-0.1.5",
+ "resolved": "common/frontend/daml.js/splice-validator-lifecycle-0.1.6",
"link": true
},
"node_modules/@daml.js/splice-wallet": {
- "resolved": "common/frontend/daml.js/splice-wallet-0.1.14",
+ "resolved": "common/frontend/daml.js/splice-wallet-0.1.15",
"link": true
},
"node_modules/@daml.js/splice-wallet-payments": {
- "resolved": "common/frontend/daml.js/splice-wallet-payments-0.1.14",
+ "resolved": "common/frontend/daml.js/splice-wallet-payments-0.1.15",
"link": true
},
- "node_modules/@daml.js/splice-wallet-payments-0.1.14": {
- "resolved": "common/frontend/daml.js/splice-wallet-payments-0.1.14",
+ "node_modules/@daml.js/splice-wallet-payments-0.1.15": {
+ "resolved": "common/frontend/daml.js/splice-wallet-payments-0.1.15",
"link": true
},
"node_modules/@daml.js/splitwell": {
- "resolved": "common/frontend/daml.js/splitwell-0.1.14",
+ "resolved": "common/frontend/daml.js/splitwell-0.1.15",
"link": true
},
"node_modules/@daml/ledger": {
diff --git a/apps/package.json b/apps/package.json
index 238de993a0..4c3df22b70 100644
--- a/apps/package.json
+++ b/apps/package.json
@@ -22,13 +22,13 @@
"wallet/external-openapi-ts-client"
],
"dependencies": {
- "@daml.js/ans": "file:common/frontend/daml.js/splice-amulet-name-service-0.1.15",
- "@daml.js/splice-amulet": "file:common/frontend/daml.js/splice-amulet-0.1.14",
- "@daml.js/splice-dso-governance": "file:common/frontend/daml.js/splice-dso-governance-0.1.20",
- "@daml.js/splice-validator-lifecycle": "file:common/frontend/daml.js/splice-validator-lifecycle-0.1.5",
- "@daml.js/splice-wallet": "file:common/frontend/daml.js/splice-wallet-0.1.14",
- "@daml.js/splice-wallet-payments": "file:common/frontend/daml.js/splice-wallet-payments-0.1.14",
- "@daml.js/splitwell": "file:common/frontend/daml.js/splitwell-0.1.14",
+ "@daml.js/ans": "file:common/frontend/daml.js/splice-amulet-name-service-0.1.16",
+ "@daml.js/splice-amulet": "file:common/frontend/daml.js/splice-amulet-0.1.15",
+ "@daml.js/splice-dso-governance": "file:common/frontend/daml.js/splice-dso-governance-0.1.21",
+ "@daml.js/splice-validator-lifecycle": "file:common/frontend/daml.js/splice-validator-lifecycle-0.1.6",
+ "@daml.js/splice-wallet": "file:common/frontend/daml.js/splice-wallet-0.1.15",
+ "@daml.js/splice-wallet-payments": "file:common/frontend/daml.js/splice-wallet-payments-0.1.15",
+ "@daml.js/splitwell": "file:common/frontend/daml.js/splitwell-0.1.15",
"xunit-viewer": "^10.6.1"
}
}
diff --git a/apps/scan/frontend/src/__tests__/mocks/data.ts b/apps/scan/frontend/src/__tests__/mocks/data.ts
index 372eabd1f0..4c74913dca 100644
--- a/apps/scan/frontend/src/__tests__/mocks/data.ts
+++ b/apps/scan/frontend/src/__tests__/mocks/data.ts
@@ -73,6 +73,7 @@ export function amuletRules(zeroTransferFees: boolean): any {
featuredAppRewardCap: '100.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
futureValues: [
{
@@ -87,6 +88,7 @@ export function amuletRules(zeroTransferFees: boolean): any {
featuredAppRewardCap: '100.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -101,6 +103,7 @@ export function amuletRules(zeroTransferFees: boolean): any {
featuredAppRewardCap: '100.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -115,6 +118,7 @@ export function amuletRules(zeroTransferFees: boolean): any {
featuredAppRewardCap: '100.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -129,6 +133,7 @@ export function amuletRules(zeroTransferFees: boolean): any {
featuredAppRewardCap: '100.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
],
@@ -170,6 +175,7 @@ export function amuletRules(zeroTransferFees: boolean): any {
},
transferPreapprovalFee: null,
featuredAppActivityMarkerAmount: null,
+ optDevelopmentFundManager: null,
},
futureValues: [],
},
diff --git a/apps/scan/frontend/src/__tests__/mocks/handlers/scan-api.ts b/apps/scan/frontend/src/__tests__/mocks/handlers/scan-api.ts
index 4de1a82ef1..bdd91a2833 100644
--- a/apps/scan/frontend/src/__tests__/mocks/handlers/scan-api.ts
+++ b/apps/scan/frontend/src/__tests__/mocks/handlers/scan-api.ts
@@ -83,6 +83,7 @@ export const buildScanMock = (baseScanUrl: string): RestHandler[] => {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
opensAt: '2025-02-03T13:43:37.895871Z',
transferConfigUsd: {
@@ -153,6 +154,7 @@ export const buildScanMock = (baseScanUrl: string): RestHandler[] => {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
opensAt: '2025-02-03T13:54:09.430298Z',
transferConfigUsd: {
@@ -223,6 +225,7 @@ export const buildScanMock = (baseScanUrl: string): RestHandler[] => {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
opensAt: '2025-02-03T14:04:37.024279Z',
transferConfigUsd: {
diff --git a/apps/scan/src/test/scala/org/lfdecentralizedtrust/splice/store/db/ScanStoreTest.scala b/apps/scan/src/test/scala/org/lfdecentralizedtrust/splice/store/db/ScanStoreTest.scala
index 4d22faf7f1..3affdbc8b1 100644
--- a/apps/scan/src/test/scala/org/lfdecentralizedtrust/splice/store/db/ScanStoreTest.scala
+++ b/apps/scan/src/test/scala/org/lfdecentralizedtrust/splice/store/db/ScanStoreTest.scala
@@ -1993,6 +1993,7 @@ trait AmuletTransferUtil { self: StoreTest =>
// TODO(#968): track faucet coupon inputs separately
java.util.Optional.empty(),
java.util.Optional.empty(),
+ java.util.Optional.empty(),
)
def mkTransferResult(
diff --git a/apps/sv/frontend/src/__tests__/utils/buildAmuletRulesConfigFromChanges.test.tsx b/apps/sv/frontend/src/__tests__/utils/buildAmuletRulesConfigFromChanges.test.tsx
index 3fb6066979..b03af7d36a 100644
--- a/apps/sv/frontend/src/__tests__/utils/buildAmuletRulesConfigFromChanges.test.tsx
+++ b/apps/sv/frontend/src/__tests__/utils/buildAmuletRulesConfigFromChanges.test.tsx
@@ -128,6 +128,12 @@ describe('buildAmuletRulesConfigFromChanges', () => {
currentValue: '50',
newValue: '100',
},
+ {
+ fieldName: 'issuanceCurveInitialValueOptDevelopmentFundPercentage',
+ label: 'Opt Development Fund Percentage',
+ currentValue: '0.05',
+ newValue: '0.06',
+ },
{
fieldName: 'decentralizedSynchronizerActiveSynchronizer',
label: 'Active Synchronizer',
@@ -237,6 +243,7 @@ describe('buildAmuletRulesConfigFromChanges', () => {
expect(result.issuanceCurve.initialValue.featuredAppRewardCap).toBe('1000');
expect(result.issuanceCurve.initialValue.unfeaturedAppRewardCap).toBe('200');
expect(result.issuanceCurve.initialValue.optValidatorFaucetCap).toBe('100');
+ expect(result.issuanceCurve.initialValue.optDevelopmentFundPercentage).toBe('0.06');
expect(result.decentralizedSynchronizer.activeSynchronizer).toBe('sync2');
const expectedRequiredSynchronizers = Array.from(
@@ -351,6 +358,12 @@ describe('buildAmuletRulesConfigFromChanges', () => {
currentValue: '50',
newValue: '100',
},
+ {
+ fieldName: 'issuanceCurveFutureValues0OptDevelopmentFundPercentage',
+ label: 'Future Value 0 Opt Development Fund Percentage',
+ currentValue: '0.05',
+ newValue: '0.06',
+ },
];
const result = buildAmuletRulesConfigFromChanges(changes);
@@ -366,6 +379,7 @@ describe('buildAmuletRulesConfigFromChanges', () => {
featuredAppRewardCap: '1000',
unfeaturedAppRewardCap: '200',
optValidatorFaucetCap: '100',
+ optDevelopmentFundPercentage: '0.06',
},
});
});
diff --git a/apps/sv/frontend/src/utils/buildAmuletConfigChanges.ts b/apps/sv/frontend/src/utils/buildAmuletConfigChanges.ts
index afdb60053b..3666352b4a 100644
--- a/apps/sv/frontend/src/utils/buildAmuletConfigChanges.ts
+++ b/apps/sv/frontend/src/utils/buildAmuletConfigChanges.ts
@@ -34,6 +34,12 @@ export function buildAmuletConfigChanges(
currentValue: before?.featuredAppActivityMarkerAmount || '',
newValue: after?.featuredAppActivityMarkerAmount || '',
},
+ {
+ fieldName: 'optDevelopmentFundManager',
+ label: 'Development Fund Manager',
+ currentValue: before?.optDevelopmentFundManager || '',
+ newValue: after?.optDevelopmentFundManager || '',
+ },
{
fieldName: 'transferConfigCreateFee',
label: 'Transfer (Create Fee)',
@@ -227,6 +233,12 @@ function buildIssuanceCurveChanges(
currentValue: before?.initialValue?.optValidatorFaucetCap || '',
newValue: after?.initialValue?.optValidatorFaucetCap || '',
},
+ {
+ fieldName: 'issuanceCurveInitialValueOptDevelopmentFundPercentage',
+ label: 'Issuance Curve Initial Value (Development Fund Percentage)',
+ currentValue: before?.initialValue?.optDevelopmentFundPercentage || '',
+ newValue: after?.initialValue?.optDevelopmentFundPercentage || '',
+ },
] as ConfigChange[];
const futureValues =
@@ -281,6 +293,12 @@ function buildIssuanceCurveChanges(
currentValue: fv._2.optValidatorFaucetCap || '',
newValue: after?.futureValues[idx]._2.optValidatorFaucetCap || '',
},
+ {
+ fieldName: `issuanceCurveFutureValues${idx}OptDevelopmentFundPercentage`,
+ label: `Issuance Curve Future Value (Development Fund Percentage) (${idx})`,
+ currentValue: fv._2.optDevelopmentFundPercentage || '',
+ newValue: after?.futureValues[idx]._2.optDevelopmentFundPercentage || '',
+ },
] as ConfigChange[];
})
.flat() || [];
diff --git a/apps/sv/frontend/src/utils/buildAmuletRulesConfigFromChanges.ts b/apps/sv/frontend/src/utils/buildAmuletRulesConfigFromChanges.ts
index cc264c47d5..fbd7d55da5 100644
--- a/apps/sv/frontend/src/utils/buildAmuletRulesConfigFromChanges.ts
+++ b/apps/sv/frontend/src/utils/buildAmuletRulesConfigFromChanges.ts
@@ -62,6 +62,9 @@ export function buildAmuletRulesConfigFromChanges(
const futureValues: Tuple2[] = [];
for (let i = 0; i < futureValuesCount; i++) {
const time = { microseconds: getValue(`issuanceCurveFutureValues${i}`) };
+ const futureOptDevelopmentFundPercentage = getValue(
+ `issuanceCurveFutureValues${i}OptDevelopmentFundPercentage`
+ );
const config: IssuanceConfig = {
amuletToIssuePerYear: getValue(`issuanceCurveFutureValues${i}AmuletToIssuePerYear`),
validatorRewardPercentage: getValue(`issuanceCurveFutureValues${i}ValidatorRewardPercentage`),
@@ -70,15 +73,22 @@ export function buildAmuletRulesConfigFromChanges(
featuredAppRewardCap: getValue(`issuanceCurveFutureValues${i}FeaturedAppRewardCap`),
unfeaturedAppRewardCap: getValue(`issuanceCurveFutureValues${i}UnfeaturedAppRewardCap`),
optValidatorFaucetCap: getValue(`issuanceCurveFutureValues${i}OptValidatorFaucetCap`),
+ optDevelopmentFundPercentage:
+ futureOptDevelopmentFundPercentage === '' ? null : futureOptDevelopmentFundPercentage,
};
futureValues.push({ _1: time, _2: config });
}
const transferPreapprovalFee = getValue('transferPreapprovalFee');
+ const optDevelopmentFundManager = getValue('optDevelopmentFundManager');
+ const initialOptDevelopmentFundPercentage = getValue(
+ 'issuanceCurveInitialValueOptDevelopmentFundPercentage'
+ );
const amuletConfig: AmuletConfig<'USD'> = {
tickDuration: { microseconds: getValue('tickDuration') },
transferPreapprovalFee: transferPreapprovalFee === '' ? null : transferPreapprovalFee,
featuredAppActivityMarkerAmount: getValue('featuredAppActivityMarkerAmount'),
+ optDevelopmentFundManager: optDevelopmentFundManager === '' ? null : optDevelopmentFundManager,
transferConfig: {
createFee: { fee: getValue('transferConfigCreateFee') },
@@ -103,6 +113,8 @@ export function buildAmuletRulesConfigFromChanges(
featuredAppRewardCap: getValue('issuanceCurveInitialValueFeaturedAppRewardCap'),
unfeaturedAppRewardCap: getValue('issuanceCurveInitialValueUnfeaturedAppRewardCap'),
optValidatorFaucetCap: getValue('issuanceCurveInitialValueOptValidatorFaucetCap'),
+ optDevelopmentFundPercentage:
+ initialOptDevelopmentFundPercentage === '' ? null : initialOptDevelopmentFundPercentage,
},
futureValues: futureValues,
},
diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/DsoDelegateBasedAutomationService.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/DsoDelegateBasedAutomationService.scala
index 63bcb92fc0..62d283a439 100644
--- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/DsoDelegateBasedAutomationService.scala
+++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/DsoDelegateBasedAutomationService.scala
@@ -116,6 +116,9 @@ class DsoDelegateBasedAutomationService(
svTaskContext,
)
)
+ registerTrigger(
+ new MergeUnclaimedDevelopmentFundCouponsTrigger(config, triggerContext, svTaskContext)
+ )
}
}
@@ -149,5 +152,6 @@ object DsoDelegateBasedAutomationService extends AutomationServiceCompanion {
aTrigger[AllocateUnallocatedUnclaimedActivityRecordTrigger],
aTrigger[ExpiredUnallocatedUnclaimedActivityRecordTrigger],
aTrigger[ExpiredUnclaimedActivityRecordTrigger],
+ aTrigger[MergeUnclaimedDevelopmentFundCouponsTrigger],
)
}
diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/delegatebased/MergeUnclaimedDevelopmentFundCouponsTrigger.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/delegatebased/MergeUnclaimedDevelopmentFundCouponsTrigger.scala
new file mode 100644
index 0000000000..5a152077e4
--- /dev/null
+++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/delegatebased/MergeUnclaimedDevelopmentFundCouponsTrigger.scala
@@ -0,0 +1,115 @@
+// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfdecentralizedtrust.splice.sv.automation.delegatebased
+
+import org.lfdecentralizedtrust.splice.automation.{
+ PollingParallelTaskExecutionTrigger,
+ TaskOutcome,
+ TaskSuccess,
+ TriggerContext,
+}
+import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet.UnclaimedDevelopmentFundCoupon
+import org.lfdecentralizedtrust.splice.codegen.java.splice.dsorules.DsoRules_MergeUnclaimedDevelopmentFundCoupons
+import org.lfdecentralizedtrust.splice.store.PageLimit
+import org.lfdecentralizedtrust.splice.util.Contract
+import org.lfdecentralizedtrust.splice.util.PrettyInstances.*
+import com.digitalasset.canton.logging.pretty.{Pretty, PrettyPrinting}
+import com.digitalasset.canton.tracing.TraceContext
+import io.opentelemetry.api.trace.Tracer
+import org.apache.pekko.stream.Materializer
+import org.lfdecentralizedtrust.splice.codegen.java.splice.amuletrules.AmuletRules_MergeUnclaimedDevelopmentFundCoupons
+import org.lfdecentralizedtrust.splice.store.AppStoreWithIngestion.SpliceLedgerConnectionPriority
+import org.lfdecentralizedtrust.splice.sv.config.SvAppBackendConfig
+
+import scala.concurrent.{ExecutionContext, Future}
+import scala.jdk.CollectionConverters.*
+
+class MergeUnclaimedDevelopmentFundCouponsTrigger(
+ svConfig: SvAppBackendConfig,
+ override protected val context: TriggerContext,
+ override protected val svTaskContext: SvTaskBasedTrigger.Context,
+)(implicit
+ override val ec: ExecutionContext,
+ mat: Materializer,
+ tracer: Tracer,
+) extends PollingParallelTaskExecutionTrigger[MergeUnclaimedDevelopmentFundCouponsTask]
+ with SvTaskBasedTrigger[MergeUnclaimedDevelopmentFundCouponsTask] {
+
+ private val store = svTaskContext.dsoStore
+
+ protected def retrieveTasks()(implicit
+ tc: TraceContext
+ ): Future[Seq[MergeUnclaimedDevelopmentFundCouponsTask]] = {
+ val threshold = svConfig.unclaimedDevelopmentFundCouponsThreshold
+ val limit = PageLimit.tryCreate(2 * threshold)
+ store.listUnclaimedDevelopmentFundCoupons(limit).map { unclaimedDevelopmentFundCoupons =>
+ if (unclaimedDevelopmentFundCoupons.length >= 2 * threshold) {
+ Seq(
+ MergeUnclaimedDevelopmentFundCouponsTask(
+ // Merge the `threshold` smallest coupons (by amount) to keep larger coupons stable and
+ // reduce contention with externally prepared transactions referencing contract-ids.
+ unclaimedDevelopmentFundCoupons.sortBy(_.payload.amount).take(threshold)
+ )
+ )
+ } else {
+ Seq()
+ }
+ }
+ }
+
+ protected def isStaleTask(
+ unclaimedDevelopmentFundCouponsTask: MergeUnclaimedDevelopmentFundCouponsTask
+ )(implicit tc: TraceContext): Future[Boolean] = store.multiDomainAcsStore.containsArchived(
+ unclaimedDevelopmentFundCouponsTask.contractsToMerge.map(_.contractId)
+ )
+
+ override def completeTaskAsDsoDelegate(
+ unclaimedDevelopmentFundCouponsTask: MergeUnclaimedDevelopmentFundCouponsTask,
+ controller: String,
+ )(implicit tc: TraceContext): Future[TaskOutcome] = {
+ for {
+ dsoRules <- store.getDsoRules()
+ amuletRules <- store.getAmuletRules()
+ choiceArg = new AmuletRules_MergeUnclaimedDevelopmentFundCoupons(
+ unclaimedDevelopmentFundCouponsTask.contractsToMerge
+ .map(_.contractId)
+ .asJava
+ )
+ arg = new DsoRules_MergeUnclaimedDevelopmentFundCoupons(
+ amuletRules.contractId,
+ choiceArg,
+ controller,
+ )
+ cmd = dsoRules.exercise(_.exerciseDsoRules_MergeUnclaimedDevelopmentFundCoupons(arg))
+ res <- for {
+ outcome <- svTaskContext
+ .connection(SpliceLedgerConnectionPriority.Low)
+ .submit(
+ Seq(store.key.svParty),
+ Seq(store.key.dsoParty),
+ cmd,
+ )
+ .noDedup
+ .yieldResult()
+ } yield Some(outcome)
+ } yield {
+ res
+ .map(cid => {
+ TaskSuccess(
+ s"Merged unclaimed development fund coupons into contract ${cid.exerciseResult.result.unclaimedDevelopmentFundCouponCid.contractId}"
+ )
+ })
+ .getOrElse(TaskSuccess(s"Not enough unclaimed development fund coupons to merge"))
+ }
+ }
+}
+
+case class MergeUnclaimedDevelopmentFundCouponsTask(
+ contractsToMerge: Seq[
+ Contract[UnclaimedDevelopmentFundCoupon.ContractId, UnclaimedDevelopmentFundCoupon]
+ ]
+) extends PrettyPrinting {
+ override def pretty: Pretty[this.type] =
+ prettyOfClass(param("contractsToMerge", _.contractsToMerge))
+}
diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/config/SvAppConfig.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/config/SvAppConfig.scala
index 2f2c7a1a9f..d5f57e229a 100644
--- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/config/SvAppConfig.scala
+++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/config/SvAppConfig.scala
@@ -102,6 +102,7 @@ object SvOnboardingConfig {
initialFeaturedAppActivityMarkerAmount: Option[BigDecimal] = Some(BigDecimal(1.0)),
voteCooldownTime: Option[NonNegativeFiniteDuration] = None,
initialRound: Long = 0L,
+ developmentFundPercentage: Option[BigDecimal] = None,
) extends SvOnboardingConfig
case class JoinWithKey(
@@ -363,6 +364,8 @@ case class SvAppBackendConfig(
// If true, we check that topology on mediator and sequencer is the same after
// a migration. This can be a useful assertion but is very slow so should not be enabled on clusters with large topology state.
validateTopologyAfterMigration: Boolean = false,
+ // The threshold above which unclaimed development fund coupons will be merged.
+ unclaimedDevelopmentFundCouponsThreshold: Int = 10,
) extends SpliceBackendConfig {
def shouldSkipSynchronizerInitialization =
diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/onboarding/sv1/SV1Initializer.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/onboarding/sv1/SV1Initializer.scala
index a647d846d3..e122f0a571 100644
--- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/onboarding/sv1/SV1Initializer.scala
+++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/onboarding/sv1/SV1Initializer.scala
@@ -671,22 +671,28 @@ class SV1Initializer(
show"This should never happen.\nAmuletRules: $amuletRules"
)
case None =>
- val amuletConfig = defaultAmuletConfig(
- sv1Config.initialTickDuration,
- sv1Config.initialMaxNumInputs,
- synchronizerId,
- sv1Config.initialSynchronizerFeesConfig.extraTrafficPrice.value,
- sv1Config.initialSynchronizerFeesConfig.minTopupAmount.value,
- sv1Config.initialSynchronizerFeesConfig.baseRateBurstAmount.value,
- sv1Config.initialSynchronizerFeesConfig.baseRateBurstWindow,
- sv1Config.initialSynchronizerFeesConfig.readVsWriteScalingFactor.value,
- sv1Config.initialPackageConfig.toPackageConfig,
- sv1Config.initialHoldingFee,
- sv1Config.zeroTransferFees,
- sv1Config.initialTransferPreapprovalFee,
- sv1Config.initialFeaturedAppActivityMarkerAmount,
- )
for {
+ developmentFund <- packageVersionSupport.supportDevelopmentFund(
+ Seq(svParty),
+ clock.now,
+ )
+ amuletConfig = defaultAmuletConfig(
+ sv1Config.initialTickDuration,
+ sv1Config.initialMaxNumInputs,
+ synchronizerId,
+ sv1Config.initialSynchronizerFeesConfig.extraTrafficPrice.value,
+ sv1Config.initialSynchronizerFeesConfig.minTopupAmount.value,
+ sv1Config.initialSynchronizerFeesConfig.baseRateBurstAmount.value,
+ sv1Config.initialSynchronizerFeesConfig.baseRateBurstWindow,
+ sv1Config.initialSynchronizerFeesConfig.readVsWriteScalingFactor.value,
+ sv1Config.initialPackageConfig.toPackageConfig,
+ sv1Config.initialHoldingFee,
+ sv1Config.zeroTransferFees,
+ sv1Config.initialTransferPreapprovalFee,
+ sv1Config.initialFeaturedAppActivityMarkerAmount,
+ developmentFundPercentage =
+ if (developmentFund.supported) sv1Config.developmentFundPercentage else None,
+ )
sv1SynchronizerNodes <- SvUtil.getSV1SynchronizerNodeConfig(
cometBftNode,
localSynchronizerNode,
diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/store/SvDsoStore.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/store/SvDsoStore.scala
index 9ff3f3baf5..bb81e7dd19 100644
--- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/store/SvDsoStore.scala
+++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/store/SvDsoStore.scala
@@ -6,7 +6,10 @@ package org.lfdecentralizedtrust.splice.sv.store
import cats.implicits.toTraverseOps
import com.digitalasset.daml.lf.data.Time.Timestamp
import org.lfdecentralizedtrust.splice.automation.MultiDomainExpiredContractTrigger.ListExpiredContracts
-import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet.UnclaimedReward
+import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet.{
+ UnclaimedDevelopmentFundCoupon,
+ UnclaimedReward,
+}
import org.lfdecentralizedtrust.splice.codegen.java.splice.amuletrules.{
AmuletRules_MiningRound_Archive,
AppTransferContext,
@@ -958,6 +961,21 @@ trait SvDsoStore
.listContracts(splice.amulet.FeaturedAppActivityMarker.COMPANION, PageLimit.tryCreate(limit))
.map(_.map(_.contract))
+ final def listUnclaimedDevelopmentFundCoupons(
+ limit: Limit
+ )(implicit
+ tc: TraceContext
+ ): Future[Seq[Contract[
+ UnclaimedDevelopmentFundCoupon.ContractId,
+ splice.amulet.UnclaimedDevelopmentFundCoupon,
+ ]]] =
+ for {
+ unclaimedDevelopmentFundCoupon <- multiDomainAcsStore.listContracts(
+ splice.amulet.UnclaimedDevelopmentFundCoupon.COMPANION,
+ limit = limit,
+ )
+ } yield unclaimedDevelopmentFundCoupon map (_.contract)
+
/** Whether there are more than the given number of featured app activity markers. */
def featuredAppActivityMarkerCountAboveOrEqualTo(threshold: Int)(implicit
tc: TraceContext
@@ -1340,6 +1358,13 @@ object SvDsoStore {
Some(PartyId.tryFromProtoPrimitive(contract.payload.publisher)),
)
},
+ mkFilter(splice.amulet.UnclaimedDevelopmentFundCoupon.COMPANION)(co =>
+ co.payload.dso == dso
+ ) { contract =>
+ DsoAcsStoreRowData(
+ contract
+ )
+ },
)
MultiDomainAcsStore.SimpleContractFilter(
diff --git a/apps/wallet/frontend/src/__tests__/mocks/constants.ts b/apps/wallet/frontend/src/__tests__/mocks/constants.ts
index 578ca209cf..93365fdc4e 100644
--- a/apps/wallet/frontend/src/__tests__/mocks/constants.ts
+++ b/apps/wallet/frontend/src/__tests__/mocks/constants.ts
@@ -199,6 +199,7 @@ export const amuletRules = {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
futureValues: [
{
@@ -213,6 +214,7 @@ export const amuletRules = {
amuletToIssuePerYear: '20000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -227,6 +229,7 @@ export const amuletRules = {
amuletToIssuePerYear: '10000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -241,6 +244,7 @@ export const amuletRules = {
amuletToIssuePerYear: '5000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
{
@@ -255,12 +259,14 @@ export const amuletRules = {
amuletToIssuePerYear: '2500000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
},
],
},
transferPreapprovalFee: null,
featuredAppActivityMarkerAmount: null,
+ optDevelopmentFundManager: null,
},
futureValues: [],
},
@@ -300,6 +306,7 @@ export const miningRounds = {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
opensAt: '2024-08-05T16:30:50.974657Z',
transferConfigUsd: {
@@ -369,6 +376,7 @@ export const miningRounds = {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
opensAt: '2024-08-05T16:41:21.419411Z',
transferConfigUsd: {
@@ -438,6 +446,7 @@ export const miningRounds = {
amuletToIssuePerYear: '40000000000.0',
validatorRewardCap: '0.2',
optValidatorFaucetCap: '2.85',
+ optDevelopmentFundPercentage: '0.05',
},
opensAt: '2024-08-05T16:51:47.336118Z',
transferConfigUsd: {
diff --git a/daml/dars.lock b/daml/dars.lock
index 8664cb7ccd..4a4d755093 100644
--- a/daml/dars.lock
+++ b/daml/dars.lock
@@ -5,6 +5,7 @@ splice-amulet 0.1.11 9824927cdb455f833867b74c01cffcd8cb8cc5edd4d2273cea1329b708e
splice-amulet 0.1.12 95a88ff9ffd509e097802ecf3bbd58c83a5dff408e439cca4e2105ebd2cd0760
splice-amulet 0.1.13 6e9fc50fb94e56751b49f09ba2dc84da53a9d7cff08115ebb4f6b7a12d0c990c
splice-amulet 0.1.14 3ca1343ab26b453d38c8adb70dca5f1ead8440c42b59b68f070786955cbf9ec1
+splice-amulet 0.1.15 67fac2f853bce8dbf0b9817bb5ba7c59f10e8120b7c808696f7010e5f0c8a791
splice-amulet 0.1.2 1446ffdf23326cef2de97923df96618eb615792bea36cf1431f03639448f1645
splice-amulet 0.1.3 0d89016d5a90eb8bced48bbac99e81c57781b3a36094b8d48b8e4389851e19af
splice-amulet 0.1.4 a36ef8888fb44caae13d96341ce1fabd84fc9e2e7b209bbc3caabb48b6be1668
@@ -21,6 +22,7 @@ splice-amulet-name-service 0.1.12 557a74491324790b5cf5a379f2481ab1cd2c8b75530858
splice-amulet-name-service 0.1.13 0e8c7e1cb828336bb4d537f6c4ecd94128b94733eb2ae1f55b5962757d357b4b
splice-amulet-name-service 0.1.14 6cb1318176e758c256c2e385f87b86c5060e80fb68a72e8ceb08ac5f9045fff2
splice-amulet-name-service 0.1.15 d4724b90dce9fb08badbb367962d237710b3a603e4f57806a1b0af308cc70fdb
+splice-amulet-name-service 0.1.16 53468a38bce11b51cd2ed10b9c09301c0b73570b50896d5649c4629de15815a3
splice-amulet-name-service 0.1.2 711a2974d65e6ebd149704da75f3f71234798687ab895b92f066c865dbdeeabb
splice-amulet-name-service 0.1.3 beb4b85f3f0cf36dfb93fc917d3ac218ee5d41b6e70604720cb228d85e168ee0
splice-amulet-name-service 0.1.4 053c7f4c2a77312e7d465a4fa7dc8cb298754ad12c0c987a7c401bd724e65efc
@@ -29,8 +31,8 @@ splice-amulet-name-service 0.1.6 a208aab2c4a248ab2eff352bd382f8b3bbadc92464123db
splice-amulet-name-service 0.1.7 ba7806d9b2d593eac74a050161c54ae1325d170bf175cb66a9c1e5e5ffb88c3d
splice-amulet-name-service 0.1.8 efeb3f9b2b92e55fac4ec2d6164f95407a01477240c7465e576df4e310f54bd3
splice-amulet-name-service 0.1.9 f1b5915ad45ded616f43f83c735b7ee158b5eb58abe758a721e50eee19b3e531
-splice-amulet-name-service-test 0.1.18 ccf831c39f1e686f0671bc56b13bdb37f48f9d684ebb84673120df68dde0ad28
-splice-amulet-test 0.1.17 c89d7f4966236ecf0cc1d9d1ad5c12d3be3b2935e5ccb7feb35de8d889bc0b1b
+splice-amulet-name-service-test 0.1.19 6542e39b3393da6895548049bed03208db77b274140557c9d9d3be13c77ec885
+splice-amulet-test 0.1.18 a488e6bd06305296d918e1485abb8103e4984eacc09d0d5f558cac44e4f19e4f
splice-api-featured-app-v1 1.0.0 7804375fe5e4c6d5afe067bd314c42fe0b7d005a1300019c73154dd939da4dda
splice-api-token-allocation-instruction-v1 1.0.0 275064aacfe99cea72ee0c80563936129563776f67415ef9f13e4297eecbc520
splice-api-token-allocation-request-v1 1.0.0 6fe848530b2404017c4a12874c956ad7d5c8a419ee9b040f96b5c13172d2e193
@@ -55,6 +57,7 @@ splice-dso-governance 0.1.18 136484875714fcf24c24858e1d573ac756524eeb607c26999fc
splice-dso-governance 0.1.19 759d1cf002fc1225ac43a55d73f0058becce3e62cfb5485c197f2b69ed8d9d98
splice-dso-governance 0.1.2 4206e127be8b111ac84bd7f98bd9dbf03ed489f1642b46ab31a46ee6d688e7e8
splice-dso-governance 0.1.20 996a3b619d6b65ca7812881978c44c650cac119de78f5317d1f317658943001c
+splice-dso-governance 0.1.21 2d306cfe8cdb3daf2d21f84dfecc3e2f26a41504e58fe25cb7fe5cc65683d11f
splice-dso-governance 0.1.3 b0ae3cc03e418790305a3c15f761fe495572de5827f8d322fb8b96996b783c13
splice-dso-governance 0.1.4 dc24fd18b4d151cd1e0ff6bfb7438bafb2f50fe076d0f16f50565e60b153a0be
splice-dso-governance 0.1.5 9e3ca1d22ad495dfabf3d61acae3dc1a7718f527f02092280b58cf69edfdc84c
@@ -62,8 +65,8 @@ splice-dso-governance 0.1.6 4e7653cfbf7ca249de4507aca9cd3b91060e5489042a522c589d
splice-dso-governance 0.1.7 d406eba1132d464605f4dae3edf8cf5ecbbb34bd8edef0e047e7e526d328718c
splice-dso-governance 0.1.8 1790a114f83d5f290261fae1e7e46fba75a861a3dd603c6b4ef6b67b49053948
splice-dso-governance 0.1.9 9ee83bfd872f91e659b8a8439c5b4eaf240bcf6f19698f884d7d7993ab48c401
-splice-dso-governance-test 0.1.24 0b70f6e759a482e17b01c16c05791b995d27ae24f72ac54ab47e9e37f5c14acc
-splice-token-standard-test 1.0.8 17b088db69a7ab44014bb23d24c0a348c817457b762c991895e1bdc0e21bcad0
+splice-dso-governance-test 0.1.25 a02cba80f1d2035184558614cc2030e7c714e6e549fbbb1e8180a5151ddc3913
+splice-token-standard-test 1.0.9 7010184aafc43bd9db506e02e660593d8bcdd48972b136684b70284f036363db
splice-token-test-dummy-holding 0.0.1 1cd171c6c42ab46dc9cf12d80c6111369e00cea5cdf054924b4f26ce94b1ef5b
splice-token-test-dummy-holding 0.0.2 4f40fb033ef3db89623642c1b494e846097fa32af138b3864a63aa15937a323d
splice-token-test-trading-app 1.0.0 e5c9847d5a88d3b8d65436f01765fc5ba142cc58529692e2dacdd865d9939f71
@@ -72,20 +75,22 @@ splice-util 0.1.1 00bf3632ca479d56e536096ca23bbc75d15084088ab5d12e4b31d6547d1df3
splice-util 0.1.2 3eb8f9ff160b782e0bf7ef0351072cfb6f186f086e082b35d9f7a0317e163372
splice-util 0.1.3 a0538353ed6b7be1a596d94fe7c2acb4a89ea9abdc6cff3388b0eebe0452bafb
splice-util 0.1.4 b7356fbb2cf8a3b22194d8c743c3c216d9c7527b257c8c38b257eb22942be358
+splice-util 0.1.5 5a58024e2cc488ca9e0c952ec7ef41da3a1ed0a78ba23bacd819e5b30afb5546
splice-util-featured-app-proxies 1.0.0 48e0c4fe4ea05e3b740404ebe37004ddd741efbdcd665c1c3199a5d6d9d944d7
splice-util-featured-app-proxies 1.1.0 81dd5a9e5c02d0de03208522a895fb85eeb12fbea4aca7c4ad0ad106f3b0bfce
splice-util-featured-app-proxies 1.2.0 653c48879064332d34af5008bdfd8e349493460e67e62b85e8e7e3392831c842
splice-util-featured-app-proxies 1.2.1 06bab917848ef275317c2539b75c23b94e03ceb55b4a1346936f7832084cd7a6
-splice-util-featured-app-proxies-test 1.0.6 bd75928121fdf25a9b1178950650bf7dd8d89d14b88a3a8a3545d3a8f97029a6
+splice-util-featured-app-proxies-test 1.0.7 f78f117532321b952ac8b6dc4a9c83659369d537be52d3f9a6950a87f12d8f43
splice-util-token-standard-wallet 1.0.0 1da198cb7968fa478cfa12aba9fdf128a63a8af6ab284ea6be238cf92a3733ac
-splice-util-token-standard-wallet-test 1.0.1 876136c37fe37e8fde126631d273ddcdee38a895dbb5ca1fed630d182ff74fc6
+splice-util-token-standard-wallet-test 1.0.2 b2d3452df2c64bda9ff0ecf11fa07ed61344415328ed08cb807938295232b134
splice-validator-lifecycle 0.1.0 cef96fac957362f1fc097120bd13686cac7f84fbc8053afa994a1f9214d9570c
splice-validator-lifecycle 0.1.1 1ddf05c96002914593c929848b786f34c753fb0be07717d1786be177a564aada
splice-validator-lifecycle 0.1.2 57e2f15f9755db1f00e51c52c319294264a21ad71c6bc1e7cd70db4b164c0aaa
splice-validator-lifecycle 0.1.3 33cbc8ef9f2937b581f33e0a19cdafc632084bd6e2f34d73174774fc519f8930
splice-validator-lifecycle 0.1.4 9333354aaaf7378ff228a4c8e51fadec8f4b82308f3343b1c4b68ae13dafa849
splice-validator-lifecycle 0.1.5 455dd4533c2dd0131fb349c93d9d35f3670901d13efadb0aa9b975d35b41dbb2
-splice-validator-lifecycle-test 0.1.5 cf44c0dc8d3cd06658f2f62d779731f8b33117d1064d949f763fd438169e64c3
+splice-validator-lifecycle 0.1.6 a717b2b9c23f630eb1ac907445fa5f5c9cce87fca28374b6165bc510724e07e7
+splice-validator-lifecycle-test 0.1.6 f7b1298c13fe7c4c5044d696db80b3374acb8785e089cd82906d21f26adb2ca5
splice-wallet 0.1.0 5f384fba007e8b93c8ff1ac151cdc2d035ae1e0506f5e5770de469048713024c
splice-wallet 0.1.1 e276113de450c2bb4bf8cf2d2991dd0b397fe7fe8103fe1b5377b8ab6622edec
splice-wallet 0.1.10 9824ffa414d5803d17efafd75c69c1dd53591ac5197d07048b24dbac1720a462
@@ -93,6 +98,7 @@ splice-wallet 0.1.11 991842eee48ec3caa3a649e8f47e3544dd7b688ce4b363aa934a83db7da
splice-wallet 0.1.12 b30bb727552cf6b624dbc9a5ff95f6c158e0a654e2e9c5c27bcfe3f5d0f9ada2
splice-wallet 0.1.13 eb6e01efacc3397e23c6be8b9be7db4bf37672211974d69e24b48980e2f98b7e
splice-wallet 0.1.14 690c1d47bac06db419db344d59a7a30c53fa3f5d961943fe1782cfc6c78794d8
+splice-wallet 0.1.15 fd57252dda29e3ce90028114c91b521cb661df5a9d6e87c41a9e91518215fa5b
splice-wallet 0.1.2 c162e08a4ec0428bfa870b6d9040989e575c74199c3a80558c62e03196dd5146
splice-wallet 0.1.3 2c35bb4f5084ea66db59717d21750bfd64c43147ef5fd5166615092d592a6917
splice-wallet 0.1.4 141dad2d33b6410b8e1c35a0c4f8f76cb691e4d9a4410ce89f33f373855317e1
@@ -108,6 +114,7 @@ splice-wallet-payments 0.1.11 7266d861727757f3482857a77f25f4d647d8925b469e46938a
splice-wallet-payments 0.1.12 88516902a9f045d3fd3835c8f5c8c6bfe4b44d83fae11369241f1883bb5b3ab4
splice-wallet-payments 0.1.13 0b9250642d3864e6bbea553264dcac0d286104f24efad2fbaf4645520bcb4053
splice-wallet-payments 0.1.14 45b29d6e05b5352c39edde850c66b4535c682b9991b06eec312176b1a48ecab5
+splice-wallet-payments 0.1.15 f80fae7a9de9431854372a66c3ca78675f77b2f54ede65abdc1b1abdec707d21
splice-wallet-payments 0.1.2 775f5eb9c0249509adda5eb3ea4ee31bb953601168c18880df6f2ff09ec4298a
splice-wallet-payments 0.1.3 b953b3729c81a55e598a364be7d0c0574750df3de12a7a1b53a300f217cb5c5c
splice-wallet-payments 0.1.4 12177f54873c1094ea169874ad0d7838383fd137f302d16356e93f28dfbc0fcc
@@ -116,7 +123,7 @@ splice-wallet-payments 0.1.6 6124379528eeb6fa17ecdab15577c29abb33d0c0d34dc5f2680
splice-wallet-payments 0.1.7 4e3e0d9cdadf80f4bf8f3cd3660d5287c084c9a29f23c901aabce597d72fd467
splice-wallet-payments 0.1.8 e48ea337ee3335c8bb3206a2501ce947ac1a7bdb1825cee8f28bad64f5a7bc4b
splice-wallet-payments 0.1.9 7f4e081ad96f2ccded0c053b0cf5ddddae1139dfc3bb89cefcf77ea70f2cecb7
-splice-wallet-test 0.1.17 502dece51e746b84af6325286f23cf9a591a58ddec3c875ce415ffd5ff6073ec
+splice-wallet-test 0.1.18 f658717551440b5c05502aee61b834020c4490aef2e4edce118c45313d386f21
splitwell 0.1.0 075c76de553ab88383a7c69de134afa82aacfdf8ea8fcfe8852c4b199c3b2669
splitwell 0.1.1 ccb1a0215053062202052e1a052f9214da3fdae5253a6d43e2e155ff4f57fe75
splitwell 0.1.10 d42676a366f7ca7a2409974dd3054aa4d83ab29baa3b2086ad021407b0a1a295
@@ -124,6 +131,7 @@ splitwell 0.1.11 03b487fa26a8ef67df0876fb337904624c3fac27f11b7ad2d131a4eab26ee1b
splitwell 0.1.12 cc047977ee8da70e858f203a14c3fd302c6aaed27be42383e61a026854d76112
splitwell 0.1.13 c2cf7b5fb3c615cdd2c8e14af42f1ca5fe4df8647cb656c7d02a72420152c3dd
splitwell 0.1.14 bf2ec3fec9bcb58ed5e2ff63072a1e4994d0415ea7a0275942be282906a42021
+splitwell 0.1.15 2f3d8a50f57e66af450c36556a09d04c1d9117b699720118b7bd302556805499
splitwell 0.1.2 778edd2c228c6b68198d4d033885b2d0dae7daaee55d7df3edd9dfdf1f10fbd0
splitwell 0.1.3 7cde068cde689584f86a2499689d5cb165264d96496721e24ac6fb909f770a58
splitwell 0.1.4 85557b86cd4f330f093915db1ea26eac5092de6b5ddae0690146f6059c89419b
@@ -132,4 +140,4 @@ splitwell 0.1.6 872da0dd7986fd768930f85d6a7310a94a0ef924e7fbb7bb7a4e149f2b5feb74
splitwell 0.1.7 841d1c9c86b5c8f3a39059459ecd8febedf7703e18f117300bb0ebf4423db096
splitwell 0.1.8 63b8153a08ceb4bf40d807acc5712372c3eac548c266be4d5e92470b4f655515
splitwell 0.1.9 b6267905698d2798b9ef171e27d49fb88e052ec0ec0e0675a3a1b275c7d037d4
-splitwell-test 0.1.17 3f6daa25da9caec80a986c0f60eecbd8d62c248fba325f4422cbe39452651485
\ No newline at end of file
+splitwell-test 0.1.18 022ee9f4acf3c29af6abcc21ec8d5e3acac2dadfec123adfc97932b1f13348a6
\ No newline at end of file
diff --git a/daml/dars/splice-amulet-0.1.15.dar b/daml/dars/splice-amulet-0.1.15.dar
new file mode 100644
index 0000000000..924f3429f9
Binary files /dev/null and b/daml/dars/splice-amulet-0.1.15.dar differ
diff --git a/daml/dars/splice-amulet-name-service-0.1.16.dar b/daml/dars/splice-amulet-name-service-0.1.16.dar
new file mode 100644
index 0000000000..ae1ee069c4
Binary files /dev/null and b/daml/dars/splice-amulet-name-service-0.1.16.dar differ
diff --git a/daml/dars/splice-dso-governance-0.1.21.dar b/daml/dars/splice-dso-governance-0.1.21.dar
new file mode 100644
index 0000000000..7b6698cbbd
Binary files /dev/null and b/daml/dars/splice-dso-governance-0.1.21.dar differ
diff --git a/daml/dars/splice-util-0.1.5.dar b/daml/dars/splice-util-0.1.5.dar
new file mode 100644
index 0000000000..4500ebd9c6
Binary files /dev/null and b/daml/dars/splice-util-0.1.5.dar differ
diff --git a/daml/dars/splice-validator-lifecycle-0.1.6.dar b/daml/dars/splice-validator-lifecycle-0.1.6.dar
new file mode 100644
index 0000000000..1be4d1eabc
Binary files /dev/null and b/daml/dars/splice-validator-lifecycle-0.1.6.dar differ
diff --git a/daml/dars/splice-wallet-0.1.15.dar b/daml/dars/splice-wallet-0.1.15.dar
new file mode 100644
index 0000000000..de71426413
Binary files /dev/null and b/daml/dars/splice-wallet-0.1.15.dar differ
diff --git a/daml/dars/splice-wallet-payments-0.1.15.dar b/daml/dars/splice-wallet-payments-0.1.15.dar
new file mode 100644
index 0000000000..05036f01e3
Binary files /dev/null and b/daml/dars/splice-wallet-payments-0.1.15.dar differ
diff --git a/daml/dars/splitwell-0.1.15.dar b/daml/dars/splitwell-0.1.15.dar
new file mode 100644
index 0000000000..3f144e6116
Binary files /dev/null and b/daml/dars/splitwell-0.1.15.dar differ
diff --git a/daml/splice-amulet-name-service-test/daml.yaml b/daml/splice-amulet-name-service-test/daml.yaml
index 6f34b4eade..962a21b446 100644
--- a/daml/splice-amulet-name-service-test/daml.yaml
+++ b/daml/splice-amulet-name-service-test/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-amulet-name-service-test
source: daml
-version: 0.1.18
+version: 0.1.19
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-amulet-name-service/daml.yaml b/daml/splice-amulet-name-service/daml.yaml
index 83452c24b4..7259ecb172 100644
--- a/daml/splice-amulet-name-service/daml.yaml
+++ b/daml/splice-amulet-name-service/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-amulet-name-service
source: daml
-version: 0.1.15
+version: 0.1.16
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-amulet-test/daml.yaml b/daml/splice-amulet-test/daml.yaml
index af4110d5ac..e41267fa5e 100644
--- a/daml/splice-amulet-test/daml.yaml
+++ b/daml/splice-amulet-test/daml.yaml
@@ -6,7 +6,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-amulet-test
source: daml
-version: 0.1.17
+version: 0.1.18
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-amulet-test/daml/Splice/Scripts/TestAmuletRulesTransfer.daml b/daml/splice-amulet-test/daml/Splice/Scripts/TestAmuletRulesTransfer.daml
index 50d504a95b..d0fb9adb9c 100644
--- a/daml/splice-amulet-test/daml/Splice/Scripts/TestAmuletRulesTransfer.daml
+++ b/daml/splice-amulet-test/daml/Splice/Scripts/TestAmuletRulesTransfer.daml
@@ -3,6 +3,7 @@
module Splice.Scripts.TestAmuletRulesTransfer where
+import DA.Action (replicateA_)
import DA.Assert
import DA.Foldable (forA_)
import DA.List
@@ -110,6 +111,7 @@ testUsageFees = do
inputUnclaimedActivityRecordAmount = Some 0.0
inputValidatorFaucetAmount = Some 0.0
inputSvRewardAmount = 0.0
+ inputDevelopmentFundAmount = Some 0.0
inputAmuletAmount = 100.0
balanceChanges = Map.empty
holdingFees = 0.0 -- no holding fees charged on transfer
@@ -574,5 +576,68 @@ testUnclaimedActivityRecordTransferInput = do
(_, openRound) <- getLatestActiveOpenRound app
let createFee = openRound.transferConfigUsd.createFee.fee * openRound.amuletPrice
transferResult.summary.senderChangeAmount === amountToMint - createFee
+ transferResult.summary.inputUnclaimedActivityRecordAmount === Some amountToMint
+
+ pure ()
+
+testInputDevelopmentFundCoupon : Script ()
+testInputDevelopmentFundCoupon = do
+ defaultAppWithUsers@DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
+ baseConfig <- getAmuletConfig app
+ -- Replace the default AmuletConfig with a 0.05% allocation for the development fund.
+ -- This configuration applies to the next open round.
+ replaceDevelopmentFundConfig app baseConfig (Some alice.primaryParty) (Some 0.05)
+
+ -- Run 3 issuances for the already open rounds without the development fund configuration.
+ -- No development fund coupons are minted.
+ replicateA_ 3 $ runNextIssuance app
+ -- Mint one development fund coupon per issuance (two total).
+ runNextIssuance app
+ runNextIssuance app
+ context <- getPaymentTransferContext app alice
+ [(cid1, coupon1), (cid2, coupon2)] <- query @UnclaimedDevelopmentFundCoupon app.dso
+ let totalAmount = coupon1.amount + coupon2.amount
+
+ -- Allocate a DevelopmentFundCoupon
+ now <- getTime
+ let
+ expiresAt = addRelTime now (days 2)
+ reason = "Bob fixed issue XXX"
+ AmuletRules_AllocateDevelopmentFundCouponResult
+ { developmentFundCouponCid, optUnclaimedDevelopmentFundCouponCid = None } <-
+ allocateDevelopmentFundCoupon app alice.primaryParty bob.primaryParty totalAmount expiresAt reason [cid1, cid2]
+
+ -- Unhappy - expiresAt has been reached
+ setTime $ addRelTime expiresAt (minutes 1)
+ submitMultiMustFail [bob.primaryParty] [app.dso] $
+ exerciseCmd context.amuletRules AmuletRules_Transfer with
+ transfer = Transfer with
+ sender = bob.primaryParty
+ provider = bob.primaryParty
+ inputs = [InputDevelopmentFundCoupon developmentFundCouponCid]
+ outputs = []
+ beneficiaries = None
+ context = context.context
+ expectedDso = Some app.dso
+ setTime now
+
+ -- Happy
+ transferResult <-
+ checkTransferMetadata app TxKind_MergeSplit bob.primaryParty $
+ checkBalanceChanges defaultAppWithUsers $
+ submitMulti [bob.primaryParty] [app.dso] $
+ exerciseCmd context.amuletRules AmuletRules_Transfer with
+ transfer = Transfer with
+ sender = bob.primaryParty
+ provider = bob.primaryParty
+ inputs = [InputDevelopmentFundCoupon developmentFundCouponCid]
+ outputs = []
+ beneficiaries = None
+ context = context.context
+ expectedDso = Some app.dso
+ (_, openRound) <- getLatestActiveOpenRound app
+ let createFee = openRound.transferConfigUsd.createFee.fee * openRound.amuletPrice
+ transferResult.summary.senderChangeAmount === totalAmount - createFee
+ transferResult.summary.inputDevelopmentFundAmount === Some totalAmount
pure ()
diff --git a/daml/splice-amulet-test/daml/Splice/Scripts/TestDesignExample.daml b/daml/splice-amulet-test/daml/Splice/Scripts/TestDesignExample.daml
index 05ba573799..ccf1084c82 100644
--- a/daml/splice-amulet-test/daml/Splice/Scripts/TestDesignExample.daml
+++ b/daml/splice-amulet-test/daml/Splice/Scripts/TestDesignExample.daml
@@ -158,6 +158,7 @@ test_designExample= do
inputUnclaimedActivityRecordAmount = Some 0.0
inputValidatorFaucetAmount = Some 0.0
inputSvRewardAmount = 0.0
+ inputDevelopmentFundAmount = Some 0.0
inputAmuletAmount = refreshAmuletAmount + lockedAmuletAmount
balanceChanges = Map.empty
holdingFees
diff --git a/daml/splice-amulet-test/daml/Splice/Scripts/TestDevelopmentFundCoupon.daml b/daml/splice-amulet-test/daml/Splice/Scripts/TestDevelopmentFundCoupon.daml
new file mode 100644
index 0000000000..9a66f47446
--- /dev/null
+++ b/daml/splice-amulet-test/daml/Splice/Scripts/TestDevelopmentFundCoupon.daml
@@ -0,0 +1,245 @@
+-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+-- SPDX-License-Identifier: Apache-2.0
+
+module Splice.Scripts.TestDevelopmentFundCoupon where
+
+import DA.Action (replicateA_)
+import DA.Assert
+import DA.Time
+import Daml.Script
+
+import Splice.Amulet
+import Splice.AmuletRules
+import Splice.Scripts.Util
+
+testAllocateDevelopmentFundCoupon : Script ()
+testAllocateDevelopmentFundCoupon = do
+ DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
+ -- Replace the default AmuletConfig with a 0.05% allocation for the development fund.
+ -- No Fund manager configured.
+ -- This configuration applies to the next open round.
+ baseConfig <- getAmuletConfig app
+ replaceDevelopmentFundConfig app baseConfig None (Some 0.05)
+
+ -- Run 3 issuances for the already open rounds without the development fund configuration.
+ -- No development fund coupons are minted.
+ replicateA_ 3 $ runNextIssuance app
+ -- Mint one development fund coupon per issuance (two total).
+ runNextIssuance app
+ runNextIssuance app
+ now <- getTime
+ [(cid1, coupon1), (cid2, coupon2)] <- query @UnclaimedDevelopmentFundCoupon app.dso
+ let
+ totalAmount = coupon1.amount + coupon2.amount
+ unclaimedDevelopmentFundCouponCids = [cid1, cid2]
+ expiresAt = addRelTime now (days 2)
+ reason = "Bob fixed issue XXX"
+ firstAllocationAmount = 10.0
+ secondAllocationAmount = totalAmount - firstAllocationAmount
+
+ -- Unhappy - DevelopmentFundCoupon cannot be allocated without a Development Fund manager configured
+ allocateDevelopmentFundCouponMustFail app alice.primaryParty bob.primaryParty firstAllocationAmount
+ expiresAt reason unclaimedDevelopmentFundCouponCids
+
+ -- Set 5% and Alice as the fund manager
+ replaceDevelopmentFundConfig app baseConfig (Some alice.primaryParty) (Some 0.05)
+
+ -- Unhappy - expiresAt is in the past
+ let invalidExpiresAt = addRelTime now (minutes (-1))
+ allocateDevelopmentFundCouponMustFail app alice.primaryParty bob.primaryParty firstAllocationAmount
+ invalidExpiresAt reason unclaimedDevelopmentFundCouponCids
+
+ -- Unhappy - controller must be the configured Development Fund manager (alice)
+ allocateDevelopmentFundCouponMustFail app bob.primaryParty bob.primaryParty firstAllocationAmount
+ expiresAt reason unclaimedDevelopmentFundCouponCids
+
+ -- Unhappy - insufficient amount to cover the requested allocation
+ allocateDevelopmentFundCouponMustFail app alice.primaryParty bob.primaryParty (totalAmount + 1.0)
+ expiresAt reason unclaimedDevelopmentFundCouponCids
+
+ -- Happy - Development Fund coupon allocated with UnclaimedDevelopmentFundCoupon leftover
+ AmuletRules_AllocateDevelopmentFundCouponResult
+ { developmentFundCouponCid, optUnclaimedDevelopmentFundCouponCid = Some leftOverUnclaimedDevelopmentFundCouponCid } <-
+ allocateDevelopmentFundCoupon app alice.primaryParty bob.primaryParty firstAllocationAmount
+ expiresAt reason unclaimedDevelopmentFundCouponCids
+ Some developmentFundCoupon <- queryContractId bob.primaryParty developmentFundCouponCid
+ Some leftOverUnclaimedDevelopmentFundCoupon <- queryContractId app.dso leftOverUnclaimedDevelopmentFundCouponCid
+ developmentFundCoupon === DevelopmentFundCoupon with
+ dso = app.dso
+ beneficiary = bob.primaryParty
+ fundManager = alice.primaryParty
+ amount = firstAllocationAmount
+ expiresAt
+ reason
+ leftOverUnclaimedDevelopmentFundCoupon === UnclaimedDevelopmentFundCoupon with
+ dso = app.dso
+ amount = secondAllocationAmount
+
+ -- Happy - Development Fund coupon allocated with no UnclaimedDevelopmentFundCoupon leftover
+ AmuletRules_AllocateDevelopmentFundCouponResult { developmentFundCouponCid, optUnclaimedDevelopmentFundCouponCid = None } <-
+ allocateDevelopmentFundCoupon app alice.primaryParty bob.primaryParty secondAllocationAmount
+ expiresAt reason [leftOverUnclaimedDevelopmentFundCouponCid]
+ Some developmentFundCoupon <- queryContractId bob.primaryParty developmentFundCouponCid
+ developmentFundCoupon === DevelopmentFundCoupon with
+ dso = app.dso
+ beneficiary = bob.primaryParty
+ fundManager = alice.primaryParty
+ amount = secondAllocationAmount
+ expiresAt
+ reason
+
+ pure ()
+
+testWithdrawalOfDevelopmentFundCoupon : Script ()
+testWithdrawalOfDevelopmentFundCoupon = do
+ DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
+ now <- getTime
+ let
+ amount = 42.0
+ expiresAt = addRelTime now (days 1)
+ fundManager = alice.primaryParty
+ beneficiary = bob.primaryParty
+ reason = "Some reason"
+
+ developmentFundCouponCid <- createDevelopmentFundCoupon app fundManager beneficiary amount expiresAt reason
+
+ -- Unhappy - expiresAt has been reached
+ setTime $ addRelTime expiresAt (minutes 1)
+ withdrawDevelopmentFundCouponMustFail fundManager reason developmentFundCouponCid
+
+ setTime now
+
+ -- Unhappy - invalid controller
+ withdrawDevelopmentFundCouponMustFail app.dso reason developmentFundCouponCid
+ withdrawDevelopmentFundCouponMustFail beneficiary reason developmentFundCouponCid
+
+ -- Happy
+ unclaimedDevelopmentFundCouponCid <- withdrawDevelopmentFundCoupon fundManager reason developmentFundCouponCid
+ Some unclaimedDevelopmentFundCoupon <- queryContractId app.dso unclaimedDevelopmentFundCouponCid
+ unclaimedDevelopmentFundCoupon === UnclaimedDevelopmentFundCoupon with
+ dso = app.dso
+ amount
+
+ pure ()
+
+testRejectionOfDevelopmentFundCoupon : Script ()
+testRejectionOfDevelopmentFundCoupon = do
+ DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
+ now <- getTime
+ let
+ amount = 42.0
+ expiresAt = addRelTime now (days 1)
+ fundManager = alice.primaryParty
+ beneficiary = bob.primaryParty
+ reason = "Some reason"
+
+ developmentFundCouponCid <- createDevelopmentFundCoupon app fundManager beneficiary amount expiresAt reason
+
+ -- Unhappy - expiresAt has been reached
+ setTime $ addRelTime expiresAt (minutes 1)
+ rejectDevelopmentFundCouponMustFail fundManager reason developmentFundCouponCid
+
+ setTime now
+
+ -- Unhappy - invalid controller
+ rejectDevelopmentFundCouponMustFail app.dso reason developmentFundCouponCid
+ rejectDevelopmentFundCouponMustFail fundManager reason developmentFundCouponCid
+
+ -- Happy
+ unclaimedDevelopmentFundCouponCid <- rejectDevelopmentFundCoupon beneficiary reason developmentFundCouponCid
+ Some unclaimedDevelopmentFundCoupon <- queryContractId app.dso unclaimedDevelopmentFundCouponCid
+ unclaimedDevelopmentFundCoupon === UnclaimedDevelopmentFundCoupon with
+ dso = app.dso
+ amount
+
+ pure ()
+
+testExpiryOfDevelopmentFundCoupon : Script ()
+testExpiryOfDevelopmentFundCoupon = do
+ DefaultAppWithUsers{..} <- setupDefaultAppWithUsers
+ now <- getTime
+ let
+ amount = 42.0
+ expiresAt = addRelTime now (days 1)
+ reason = "Bob fixed issue XXX"
+ fundManager = alice.primaryParty
+ beneficiary = bob.primaryParty
+
+ developmentFundCouponCid <- createDevelopmentFundCoupon app fundManager beneficiary amount expiresAt reason
+
+ -- Unhappy - expiresAt has not been reached
+ expiresDevelopmentFundCouponMustFail app.dso developmentFundCouponCid
+
+ setTime $ addRelTime expiresAt (minutes 1)
+
+ -- -- Unhappy - invalid controller
+ expiresDevelopmentFundCouponMustFail fundManager developmentFundCouponCid
+ expiresDevelopmentFundCouponMustFail beneficiary developmentFundCouponCid
+
+ -- Happy
+ unclaimedDevelopmentFundCouponCid <- expiresDevelopmentFundCoupon app.dso developmentFundCouponCid
+ Some unclaimedDevelopmentFundCoupon <- queryContractId app.dso unclaimedDevelopmentFundCouponCid
+ unclaimedDevelopmentFundCoupon === UnclaimedDevelopmentFundCoupon with
+ dso = app.dso
+ amount
+
+ pure ()
+
+
+-- Helpers
+----------
+
+allocateDevelopmentFundCouponMustFail
+ : AmuletApp -> Party -> Party -> Decimal -> Time -> Text -> [ContractId UnclaimedDevelopmentFundCoupon]
+ -> Script ()
+allocateDevelopmentFundCouponMustFail app fundManager beneficiary amount expiresAt reason unclaimedDevelopmentFundCouponCids = do
+ [(amuletRulesCid, _)] <- query @AmuletRules app.dso
+ submitMultiMustFail [fundManager] [app.dso] do
+ exerciseCmd amuletRulesCid AmuletRules_AllocateDevelopmentFundCoupon with
+ unclaimedDevelopmentFundCouponCids
+ beneficiary
+ amount
+ expiresAt
+ reason
+ fundManager
+
+createDevelopmentFundCoupon : AmuletApp -> Party -> Party -> Decimal -> Time -> Text -> Script (ContractId DevelopmentFundCoupon)
+createDevelopmentFundCoupon app fundManager beneficiary amount expiresAt reason =
+ submit app.dso do
+ createCmd DevelopmentFundCoupon with
+ dso = app.dso
+ beneficiary
+ fundManager
+ amount
+ expiresAt
+ reason
+
+rejectDevelopmentFundCoupon : Party -> Text -> ContractId DevelopmentFundCoupon -> Script (ContractId UnclaimedDevelopmentFundCoupon)
+rejectDevelopmentFundCoupon fundManager reason developmentFundCouponCid =
+ (.unclaimedDevelopmentFundCouponCid) <$> submit fundManager do
+ exerciseCmd developmentFundCouponCid DevelopmentFundCoupon_Reject with reason
+
+rejectDevelopmentFundCouponMustFail : Party -> Text -> ContractId DevelopmentFundCoupon -> Script ()
+rejectDevelopmentFundCouponMustFail actor reason developmentFundCouponCid =
+ submitMustFail actor do
+ exerciseCmd developmentFundCouponCid DevelopmentFundCoupon_Reject with reason
+
+withdrawDevelopmentFundCoupon : Party -> Text -> ContractId DevelopmentFundCoupon -> Script (ContractId UnclaimedDevelopmentFundCoupon)
+withdrawDevelopmentFundCoupon fundManager reason developmentFundCouponCid =
+ (.unclaimedDevelopmentFundCouponCid) <$> submit fundManager do
+ exerciseCmd developmentFundCouponCid DevelopmentFundCoupon_Withdraw with reason
+
+withdrawDevelopmentFundCouponMustFail : Party -> Text -> ContractId DevelopmentFundCoupon -> Script ()
+withdrawDevelopmentFundCouponMustFail actor reason developmentFundCouponCid =
+ submitMustFail actor do
+ exerciseCmd developmentFundCouponCid DevelopmentFundCoupon_Withdraw with reason
+
+expiresDevelopmentFundCoupon : Party -> ContractId DevelopmentFundCoupon -> Script (ContractId UnclaimedDevelopmentFundCoupon)
+expiresDevelopmentFundCoupon dso developmentFundCouponCid =
+ (.unclaimedDevelopmentFundCouponCid) <$> submit dso do
+ exerciseCmd developmentFundCouponCid DevelopmentFundCoupon_DsoExpire
+
+expiresDevelopmentFundCouponMustFail : Party -> ContractId DevelopmentFundCoupon -> Script ()
+expiresDevelopmentFundCouponMustFail actor developmentFundCouponCid =
+ submitMustFail actor do
+ exerciseCmd developmentFundCouponCid DevelopmentFundCoupon_DsoExpire
diff --git a/daml/splice-amulet-test/daml/Splice/Scripts/TestLockAndAmuletExpiry.daml b/daml/splice-amulet-test/daml/Splice/Scripts/TestLockAndAmuletExpiry.daml
index f39dae024a..9d25a4e883 100644
--- a/daml/splice-amulet-test/daml/Splice/Scripts/TestLockAndAmuletExpiry.daml
+++ b/daml/splice-amulet-test/daml/Splice/Scripts/TestLockAndAmuletExpiry.daml
@@ -33,6 +33,7 @@ scaleAmuletConfig amuletPrice config = AmuletConfig with
packageConfig = config.packageConfig
transferPreapprovalFee = fmap (/ amuletPrice) config.transferPreapprovalFee
featuredAppActivityMarkerAmount = fmap (/ amuletPrice) config.featuredAppActivityMarkerAmount
+ optDevelopmentFundManager = config.optDevelopmentFundManager
test : Script ()
test = script do
diff --git a/daml/splice-amulet-test/daml/Splice/Scripts/UnitTests/Issuance.daml b/daml/splice-amulet-test/daml/Splice/Scripts/UnitTests/Issuance.daml
index e32b7b3edb..f657e8b126 100644
--- a/daml/splice-amulet-test/daml/Splice/Scripts/UnitTests/Issuance.daml
+++ b/daml/splice-amulet-test/daml/Splice/Scripts/UnitTests/Issuance.daml
@@ -21,6 +21,7 @@ tickDuration = defaultAmuletConfig.tickDuration
amuletPrice : Decimal
amuletPrice = 0.005
+
-- Issuance curve retrieval
---------------------------
@@ -66,6 +67,16 @@ expectedParameters_E1_0_0p5 = IssuingRoundParameters with
unclaimedValidatorRewards = 7551.7503805175
unclaimedAppRewards = 68395.2511415525
unclaimedSvRewards = 0.0000000001
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_E1_0_0p5_DevFund5 : IssuingRoundParameters
+expectedParameters_E1_0_0p5_DevFund5 =
+ expectedParameters_E1_0_0p5 with
+ issuancePerSvRewardCoupon = 5783.8660578387 -- decreased by ~5 % (expected linear scaling with 5 % fund allocation)
+ unclaimedValidatorRewards = 5649.1628614916 -- decreased by ~25 % (validator tranche near cap: small pool cut sharply reduces unclaimed remainder)
+ unclaimedAppRewards = 62687.4885844749 -- decreased by ~8.3 % (app tranche less constrained; closer to linear scaling)
+ unclaimedSvRewards = 0.0 -- decreased ~5 % (below rounding threshold)
+ optAmuletsToIssueToDevelopmentFund = Some 38051.7503805175
expectedParameters_E1_0p5_1p5 : IssuingRoundParameters
expectedParameters_E1_0p5_1p5 = IssuingRoundParameters with
@@ -77,6 +88,16 @@ expectedParameters_E1_0p5_1p5 = IssuingRoundParameters with
unclaimedValidatorRewards = 15162.1004566210
unclaimedAppRewards = 106447.0015220700
unclaimedSvRewards = 0.000000004
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_E1_0p5_1p5_DevFund5 : IssuingRoundParameters
+expectedParameters_E1_0p5_1p5_DevFund5 =
+ expectedParameters_E1_0p5_1p5 with
+ issuancePerSvRewardCoupon = 1735.1598173516 -- decreased by ~5 % (expected linear scaling with 5 % fund allocation)
+ unclaimedValidatorRewards = 12878.9954337899 -- decreased by ~15 % (validator tranche near cap: small pool cut sharply reduces unclaimed remainder)
+ unclaimedAppRewards = 98836.6514459665 -- decreased by ~10 % (app tranche less constrained; closer to linear scaling)
+ unclaimedSvRewards = 0.0 -- decreased ~5 % (below rounding threshold)
+ optAmuletsToIssueToDevelopmentFund = Some 19025.8751902588
expectedParameters_E1_1p5_5 : IssuingRoundParameters
expectedParameters_E1_1p5_5 = IssuingRoundParameters with
@@ -88,6 +109,16 @@ expectedParameters_E1_1p5_5 = IssuingRoundParameters with
unclaimedValidatorRewards = 3746.5753424658
unclaimedAppRewards = 72200.4261796042
unclaimedSvRewards = 0.0
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_E1_1p5_5_DevFund5 : IssuingRoundParameters
+expectedParameters_E1_1p5_5_DevFund5 =
+ expectedParameters_E1_1p5_5 with
+ issuancePerSvRewardCoupon = 361.4916286149 -- decreased by ~5 % (expected linear scaling with 5 % fund allocation)
+ unclaimedValidatorRewards = 2034.2465753425 -- decreased by ~45 % (validator tranche strongly cap-bound; small pool accentuates drop)
+ unclaimedAppRewards = 66302.4048706240 -- decreased by ~8.2 % (app tranche less constrained; close to linear scaling)
+ unclaimedSvRewards = 0.0000000016 -- minor rounding drift (below significance threshold)
+ optAmuletsToIssueToDevelopmentFund = Some 9512.9375951294
expectedParameters_E1_5_10 : IssuingRoundParameters
expectedParameters_E1_5_10 = IssuingRoundParameters with
@@ -99,6 +130,17 @@ expectedParameters_E1_5_10 = IssuingRoundParameters with
unclaimedValidatorRewards = 0.0000000017
unclaimedAppRewards = 19879.2694063927
unclaimedSvRewards = 0.0
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_E1_5_10_DevFund5 : IssuingRoundParameters
+expectedParameters_E1_5_10_DevFund5 =
+ expectedParameters_E1_5_10 with
+ issuancePerValidatorFaucetCoupon = 339.5662100457 -- decreased by ~5.6 % (slightly above 5 % due to faucet cap effects)
+ issuancePerSvRewardCoupon = 90.3729071537 -- decreased by ~5 % (expected linear scaling)
+ unclaimedValidatorRewards = 0.0 -- minor rounding drift (below significance threshold)
+ unclaimedAppRewards = 16597.3059360731 -- decreased by ~16.5 % (cap constraints amplify reduction)
+ unclaimedSvRewards = 0.0000000029 -- minor rounding drift (below significance threshold)
+ optAmuletsToIssueToDevelopmentFund = Some 4756.4687975647
expectedParameters_E1_10plus : IssuingRoundParameters
expectedParameters_E1_10plus = IssuingRoundParameters with
@@ -110,23 +152,45 @@ expectedParameters_E1_10plus = IssuingRoundParameters with
unclaimedValidatorRewards = 0.0
unclaimedAppRewards = 0.0000000152
unclaimedSvRewards = 0.0000000023
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_E1_10plus_DevFund5 : IssuingRoundParameters
+expectedParameters_E1_10plus_DevFund5 =
+ expectedParameters_E1_10plus with
+ issuancePerFeaturedAppRewardCoupon = 70.3246004566 -- decreased by ~6 % (slightly above 5 % since both featured and unfeatured tranches are below cap)
+ issuancePerValidatorFaucetCoupon = 140.7458143075 -- decreased by ~6.3 % (faucet below cap, reduced pool scales slightly more than linearly)
+ issuancePerSvRewardCoupon = 22.5932267884 -- decreased by ~5 % (expected linear scaling)
+ unclaimedAppRewards = 0.0000000084 -- decreased ~5 % (below rounding threshold)
+ unclaimedSvRewards = 0.0000000033 -- minor rounding drift (below significance threshold)
+ optAmuletsToIssueToDevelopmentFund = Some 2378.2343987823
+
+
testE1 : Script ()
testE1 = script do
validateOpenMiningRoundSummary summaryExample1
+ -- 0% Development Fund
expectedParameters_E1_0_0p5 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_0_0p5 summaryExample1
expectedParameters_E1_0p5_1p5 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_0p5_1p5 summaryExample1
expectedParameters_E1_1p5_5 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_1p5_5 summaryExample1
expectedParameters_E1_5_10 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_5_10 summaryExample1
expectedParameters_E1_10plus === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_10plus summaryExample1
+ -- 5% Development Fund
+ -- Most parameters decrease by ~5%; larger deviations occur when issuance caps bind or rounding applies.
+ expectedParameters_E1_0_0p5_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_0_0p5) summaryExample1
+ expectedParameters_E1_0p5_1p5_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_0p5_1p5) summaryExample1
+ expectedParameters_E1_1p5_5_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_1p5_5) summaryExample1
+ expectedParameters_E1_5_10_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_5_10) summaryExample1
+ expectedParameters_E1_10plus_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_10plus) summaryExample1
+
-- Example 2: all zeros
-----------------------
-summaryExamplNoActivity : OpenMiningRoundSummary
-summaryExamplNoActivity = OpenMiningRoundSummary with
+summaryExampleNoActivity : OpenMiningRoundSummary
+summaryExampleNoActivity = OpenMiningRoundSummary with
totalValidatorRewardCoupons = 0.0
totalFeaturedAppRewardCoupons = 0.0
totalUnfeaturedAppRewardCoupons = 0.0
@@ -143,12 +207,28 @@ expectedParameters_NoActivity_0_0p5 = IssuingRoundParameters with
unclaimedValidatorRewards = 38051.7503805175
unclaimedAppRewards = 114155.2511415525
unclaimedSvRewards = 608828.0060882801
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_NoActivity_0_0p5_DevFund5 : IssuingRoundParameters
+expectedParameters_NoActivity_0_0p5_DevFund5 =
+ expectedParameters_NoActivity_0_0p5 with
+ issuancePerSvRewardCoupon = 578386.6057838661 -- decreased by ~5 % (expected linear scaling; no coupons active)
+ unclaimedValidatorRewards = 36149.1628614916 -- decreased by ~5 % (fund allocation directly reduces total issuance)
+ unclaimedAppRewards = 108447.4885844749 -- decreased by ~5 % (expected linear scaling)
+ unclaimedSvRewards = 578386.6057838661 -- decreased by ~5 % (identical scaling since all rewards remain unclaimed)
+ optAmuletsToIssueToDevelopmentFund = Some 38051.7503805175
+
testNoActivity : Script ()
testNoActivity = script do
- validateOpenMiningRoundSummary summaryExamplNoActivity
+ validateOpenMiningRoundSummary summaryExampleNoActivity
+
+ -- 0% Development Fund
+ expectedParameters_NoActivity_0_0p5 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_0_0p5 summaryExampleNoActivity
- expectedParameters_NoActivity_0_0p5 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_0_0p5 summaryExamplNoActivity
+ -- 5% Development Fund
+ -- Most parameters decrease by ~5%; larger deviations occur when issuance caps bind or rounding applies.
+ expectedParameters_NoActivity_0_0p5_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_0_0p5) summaryExampleNoActivity
-- Example 3: low activity
@@ -173,18 +253,31 @@ expectedParameters_E3_0_0p5 = IssuingRoundParameters with
unclaimedValidatorRewards = 9511.7503805175
unclaimedAppRewards = 104095.2511415525
unclaimedSvRewards = 0.0000000001
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_E3_0_0p5_DevFund5 : IssuingRoundParameters
+expectedParameters_E3_0_0p5_DevFund5 =
+ expectedParameters_E3_0_0p5 with
+ issuancePerSvRewardCoupon = 289193.302891933 -- decreased by ~5 % (expected linear scaling; SV tranche not capped)
+ unclaimedValidatorRewards = 7609.1628614916 -- decreased by ~20 % (validator tranche near cap, amplifying reduction)
+ unclaimedAppRewards = 98387.4885844749 -- decreased by ~5.5 % (mostly linear scaling)
+ optAmuletsToIssueToDevelopmentFund = Some 38051.7503805175
testE4 : Script ()
testE4 = script do
validateOpenMiningRoundSummary summaryExample3
+ -- 0% Development Fund
expectedParameters_E3_0_0p5 === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_0_0p5 summaryExample3
+ -- 5% Development Fund
+ -- Most parameters decrease by ~5%; larger deviations occur when issuance caps bind or rounding applies.
+ expectedParameters_E3_0_0p5_DevFund5 === computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_0_0p5) summaryExample3
+
-- Example 4: millions of SV reward weight
-------------------------------------------
-
summaryExampleLargeSvRewardWeight : OpenMiningRoundSummary
summaryExampleLargeSvRewardWeight = OpenMiningRoundSummary with
totalValidatorRewardCoupons = 0.0
@@ -203,13 +296,56 @@ expectedParameters_LargeSvRewardWeight_10plus = IssuingRoundParameters with
unclaimedAppRewards = 35673.5159817352
unclaimedValidatorRewards = 9512.9375951294
unclaimedSvRewards = 0.0
+ optAmuletsToIssueToDevelopmentFund = Some 0.0
+
+expectedParameters_LargeSvRewardWeight_10plus_DevFund5 : IssuingRoundParameters
+expectedParameters_LargeSvRewardWeight_10plus_DevFund5 =
+ expectedParameters_LargeSvRewardWeight_10plus with
+ issuancePerSvRewardCoupon = 0.0009413844 -- decreased by ~5 % (expected linear scaling; SV weight dominates distribution)
+ unclaimedValidatorRewards = 9037.2907153729 -- decreased by ~5 % (linear with total issuance)
+ unclaimedAppRewards = 33889.8401826484 -- decreased by ~5 % (expected linear scaling)
+ unclaimedSvRewards = 0.0001188433 -- minor rounding drift (below significance threshold)
+ optAmuletsToIssueToDevelopmentFund = Some 2378.2343987823
testLargeSvRewardWeight : Script ()
testLargeSvRewardWeight = script do
- validateOpenMiningRoundSummary summaryExamplNoActivity
+ validateOpenMiningRoundSummary summaryExampleNoActivity
+
+ -- 0% Development Fund
+ expectedParameters_LargeSvRewardWeight_10plus ===
+ computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_10plus summaryExampleLargeSvRewardWeight
+
+ -- 5% Development Fund
+ -- Most parameters decrease by ~5%; larger deviations occur when issuance caps bind or rounding applies.
+ expectedParameters_LargeSvRewardWeight_10plus_DevFund5 ===
+ computeIssuingRoundParameters tickDuration amuletPrice (withDevFund5 issuanceConfig_10plus) summaryExampleLargeSvRewardWeight
+
- let actual = computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_10plus summaryExampleLargeSvRewardWeight
- expectedParameters_LargeSvRewardWeight_10plus === actual
+-- Example 5: Development Fund receives the full issuance
+----------------------------------------------------------
+
+expectedParameters_AllIssuanceForFund : IssuingRoundParameters
+expectedParameters_AllIssuanceForFund = IssuingRoundParameters with
+ issuancePerValidatorRewardCoupon = 0.0
+ issuancePerFeaturedAppRewardCoupon = 0.0
+ issuancePerUnfeaturedAppRewardCoupon = 0.0
+ issuancePerValidatorFaucetCoupon = 0.0
+ issuancePerSvRewardCoupon = 0.0
+ unclaimedAppRewards = 0.0
+ unclaimedValidatorRewards = 0.0
+ unclaimedSvRewards = 0.0
+ optAmuletsToIssueToDevelopmentFund = Some 761035.0076103501
+
+testAllIssuanceForFund : Script ()
+testAllIssuanceForFund = script do
+ validateOpenMiningRoundSummary summaryExample3
+ -- 100% Development Fund
+ -- All issuance goes to the Development Fund; all reward-related fields are zero.
+ let issuanceConfig_0_0p5_fundOne = issuanceConfig_0_0p5 with optDevelopmentFundPercentage = Some 1.0
+ expectedParameters_AllIssuanceForFund === computeIssuingRoundParameters tickDuration amuletPrice issuanceConfig_0_0p5_fundOne summaryExample1
+-- Utility: apply 5% Development Fund allocation
+withDevFund5 : IssuanceConfig -> IssuanceConfig
+withDevFund5 config = config with optDevelopmentFundPercentage = Some 0.05
diff --git a/daml/splice-amulet-test/daml/Splice/Scripts/Util.daml b/daml/splice-amulet-test/daml/Splice/Scripts/Util.daml
index f7b47d47d6..8ecf56223a 100644
--- a/daml/splice-amulet-test/daml/Splice/Scripts/Util.daml
+++ b/daml/splice-amulet-test/daml/Splice/Scripts/Util.daml
@@ -39,7 +39,7 @@ import Splice.Testing.Registries.AmuletRegistry.Parameters
import Splice.Util
-- Bootstrapping Amulet
--------------------
+------------------------
-- | A type to hold the off-ledger information required to interact with the Amulet app.
@@ -85,6 +85,19 @@ genericSetupApp dsoPrefix = do
return app
+-- Replacing AmuletConfig
+--------------------------
+
+
+replaceDevelopmentFundConfig : AmuletApp -> AmuletConfig Unit.USD -> Optional Party -> Optional Decimal -> Script ()
+replaceDevelopmentFundConfig app baseConfig optDevelopmentFundManager optDevelopmentFundPercentage = do
+ setAmuletConfig app baseConfig baseConfig with
+ optDevelopmentFundManager
+ issuanceCurve = baseConfig.issuanceCurve with
+ initialValue = baseConfig.issuanceCurve.initialValue with
+ optDevelopmentFundPercentage
+
+
-- AmuletApp users
--------------
@@ -646,6 +659,26 @@ getAmuletConfig app = do
now <- getTime
pure $ getValueAsOf now amuletRules.configSchedule
+setAmuletConfig : AmuletApp -> AmuletConfig Unit.USD -> AmuletConfig Unit.USD -> Script ()
+setAmuletConfig app baseConfig newConfig = do
+ Some (amuletRulesCid, _) <- queryAmuletRulesByKey app.dso
+ void $ submit app.dso $
+ exerciseCmd amuletRulesCid AmuletRules_SetConfig with
+ newConfig
+ baseConfig
+
+allocateDevelopmentFundCoupon
+ : AmuletApp -> Party -> Party -> Decimal -> Time -> Text -> [ContractId UnclaimedDevelopmentFundCoupon]
+ -> Script AmuletRules_AllocateDevelopmentFundCouponResult
+allocateDevelopmentFundCoupon app fundManager beneficiary amount expiresAt reason unclaimedDevelopmentFundCouponCids = do
+ submitExerciseAmuletRulesByKey app [fundManager] [] AmuletRules_AllocateDevelopmentFundCoupon with
+ unclaimedDevelopmentFundCouponCids
+ beneficiary
+ amount
+ expiresAt
+ reason
+ fundManager
+
-- Metadata verification
------------------------
@@ -693,13 +726,16 @@ genericCheckTxMetadata extractMeta extractSummary app expectedKind sender body =
mint <- case extractSummary result of
None -> pure 0.0
Some summary -> do
- let inputUnclaimedActivityRecordAmount = fromOptional 0.0 summary.inputUnclaimedActivityRecordAmount
+ let
+ inputUnclaimedActivityRecordAmount = fromOptional 0.0 summary.inputUnclaimedActivityRecordAmount
+ inputDevelopmentFundAmountAmount = fromOptional 0.0 summary.inputDevelopmentFundAmount
expectUnlessZero svRewardAmountMetaKey summary.inputSvRewardAmount
expectUnlessZero appRewardAmountMetaKey summary.inputAppRewardAmount
expectUnlessZero validatorRewardAmountMetaKey summary.inputValidatorRewardAmount
expectUnlessZero unclaimedActivityRecordAmountMetaKey inputUnclaimedActivityRecordAmount
+ expectUnlessZero developmentFundAmountMetaKey inputDevelopmentFundAmountAmount
pure $ summary.inputAppRewardAmount + summary.inputValidatorRewardAmount + summary.inputSvRewardAmount +
- inputUnclaimedActivityRecordAmount
+ inputUnclaimedActivityRecordAmount + inputDevelopmentFundAmountAmount
let expectedBurn = totalHoldingsBefore + mint - totalHoldingsAfter
-- mints are inferred, and show here as a negative burn
if (expectedBurn < 0.0)
diff --git a/daml/splice-amulet/daml.yaml b/daml/splice-amulet/daml.yaml
index d0edcb5cbb..72d3f38f18 100644
--- a/daml/splice-amulet/daml.yaml
+++ b/daml/splice-amulet/daml.yaml
@@ -6,7 +6,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-amulet
source: daml
-version: 0.1.14
+version: 0.1.15
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-amulet/daml/Splice/Amulet.daml b/daml/splice-amulet/daml/Splice/Amulet.daml
index d3366d4190..3891cc5af5 100644
--- a/daml/splice-amulet/daml/Splice/Amulet.daml
+++ b/daml/splice-amulet/daml/Splice/Amulet.daml
@@ -81,9 +81,18 @@ data SvRewardCoupon_ArchiveAsBeneficiaryResult = SvRewardCoupon_ArchiveAsBenefic
data UnclaimedActivityRecord_ArchiveAsBeneficiaryResult = UnclaimedActivityRecord_ArchiveAsBeneficiaryResult
-data UnclaimedActivityRecord_DsoExpireResult = UnclaimedActivityRecord_DsoExpireResult with
+data UnclaimedActivityRecord_DsoExpireResult = UnclaimedActivityRecord_DsoExpireResult with
unclaimedRewardCid : ContractId UnclaimedReward
+data DevelopmentFundCoupon_WithdrawResult = DevelopmentFundCoupon_WithdrawResult with
+ unclaimedDevelopmentFundCouponCid : ContractId UnclaimedDevelopmentFundCoupon
+
+data DevelopmentFundCoupon_RejectResult = DevelopmentFundCoupon_RejectResult with
+ unclaimedDevelopmentFundCouponCid : ContractId UnclaimedDevelopmentFundCoupon
+
+data DevelopmentFundCoupon_DsoExpireResult = DevelopmentFundCoupon_DsoExpireResult with
+ unclaimedDevelopmentFundCouponCid : ContractId UnclaimedDevelopmentFundCoupon
+
-- | A amulet, which can be locked and whose amount expires over time.
--
-- The expiry serves to charge an inactivity fee, and thereby ensures that the
@@ -383,6 +392,65 @@ template SvRewardCoupon with
do return SvRewardCoupon_ArchiveAsBeneficiaryResult
+-- | A coupon recording an emission for the Development Fund from CIP-0082 that
+-- was not yet assigned to a specific beneficiary.
+template UnclaimedDevelopmentFundCoupon
+ with
+ dso : Party
+ amount : Decimal -- ^ The total amount of `Amulet` to mint on collection.
+ where
+ signatory dso
+ ensure amount > 0.0
+
+-- | A coupon recording an emission for the Development Fund under CIP-0082,
+-- which can be collected by the designated beneficiary.
+template DevelopmentFundCoupon
+ with
+ dso : Party
+ beneficiary : Party -- ^ The owner of the `Amulet` to be minted.
+ fundManager : Party
+ -- ^ The party that executed the assignment of the coupon to the beneficiary
+ -- so they can mint from the development fund.
+ amount : Decimal -- ^ The total amount of `Amulet` to mint on collection.
+ expiresAt : Time -- ^ Until when the minting can be completed.
+ reason : Text -- ^ Reason for the emission of the coupon.
+ where
+ ensure amount > 0.0
+
+ signatory dso
+
+ -- The beneficiary is an observer of the coupon, as they need to be able to claim it.
+ observer beneficiary
+
+ -- The fundManager is an observer so they can see both the transaction creating and the one archiving the coupon.
+ observer fundManager
+
+ choice DevelopmentFundCoupon_Withdraw : DevelopmentFundCoupon_WithdrawResult
+ with
+ reason : Text -- ^ Reason for withdrawing the coupon.
+ controller fundManager
+ do
+ assertWithinDeadline "DevelopmentFundCoupon.expiresAt" expiresAt
+ unclaimedDevelopmentFundCouponCid <- create UnclaimedDevelopmentFundCoupon with dso; amount
+ return DevelopmentFundCoupon_WithdrawResult with unclaimedDevelopmentFundCouponCid
+
+ choice DevelopmentFundCoupon_Reject : DevelopmentFundCoupon_RejectResult
+ with
+ reason : Text -- ^ Reason for rejecting the coupon.
+ controller beneficiary
+ do
+ assertWithinDeadline "DevelopmentFundCoupon.expiresAt" expiresAt
+ unclaimedDevelopmentFundCouponCid <- create UnclaimedDevelopmentFundCoupon with dso; amount
+ return DevelopmentFundCoupon_RejectResult with unclaimedDevelopmentFundCouponCid
+
+ choice DevelopmentFundCoupon_DsoExpire : DevelopmentFundCoupon_DsoExpireResult
+ controller dso
+ do
+ assertDeadlineExceeded "DevelopmentFundCoupon.expiresAt" expiresAt
+ unclaimedDevelopmentFundCouponCid <- create UnclaimedDevelopmentFundCoupon with dso; amount
+ pure DevelopmentFundCoupon_DsoExpireResult with unclaimedDevelopmentFundCouponCid
+
+
-- | Rewards that have not been claimed and are thus at the disposal of the foundation.
template UnclaimedReward with
dso : Party
@@ -392,29 +460,29 @@ template UnclaimedReward with
signatory dso
--- | A record of activity that can be minted by the beneficiary.
--- Note that these do not come out of the per-round issuance but are instead created by burning
--- UnclaimedRewardCoupon as defined through a vote by the SVs. That's also why expiry is a separate
+-- | A record of activity that can be minted by the beneficiary.
+-- Note that these do not come out of the per-round issuance but are instead created by burning
+-- UnclaimedRewardCoupon as defined through a vote by the SVs. That's also why expiry is a separate
-- time-based expiry instead of being tied to a round like the other activity records.
template UnclaimedActivityRecord
with
dso : Party
beneficiary : Party -- ^ The owner of the `Amulet` to be minted.
amount : Decimal -- ^ The amount of `Amulet` to be minted.
- reason : Text -- ^ A reason to mint the `Amulet`.
- expiresAt : Time -- ^ Selected timestamp defining the lifetime of the contract.
- where
+ reason : Text -- ^ A reason to mint the `Amulet`.
+ expiresAt : Time -- ^ Selected timestamp defining the lifetime of the contract.
+ where
signatory dso
observer beneficiary
ensure amount > 0.0
choice UnclaimedActivityRecord_DsoExpire : UnclaimedActivityRecord_DsoExpireResult
controller dso
- do
+ do
assertDeadlineExceeded "UnclaimedActivityRecord.expiresAt" expiresAt
unclaimedRewardCid <- create UnclaimedReward with dso; amount
pure UnclaimedActivityRecord_DsoExpireResult with unclaimedRewardCid
-
+
requireAmuletExpiredForAllOpenRounds : ContractId OpenMiningRound -> Amulet -> Update ()
requireAmuletExpiredForAllOpenRounds roundCid amulet = do
@@ -456,4 +524,13 @@ instance HasCheckedFetch FeaturedAppActivityMarker ForDso where
contractGroupId FeaturedAppActivityMarker {..} = ForDso with dso
instance HasCheckedFetch UnclaimedActivityRecord ForOwner where
- contractGroupId UnclaimedActivityRecord{..} = ForOwner with dso; owner = beneficiary
\ No newline at end of file
+ contractGroupId UnclaimedActivityRecord{..} = ForOwner with dso; owner = beneficiary
+
+instance HasCheckedFetch UnclaimedDevelopmentFundCoupon ForDso where
+ contractGroupId UnclaimedDevelopmentFundCoupon{..} = ForDso with dso
+
+instance HasCheckedFetch DevelopmentFundCoupon ForOwner where
+ contractGroupId DevelopmentFundCoupon{..} = ForOwner with dso; owner = beneficiary
+
+instance HasCheckedFetch DevelopmentFundCoupon ForDso where
+ contractGroupId DevelopmentFundCoupon{..} = ForDso with dso
diff --git a/daml/splice-amulet/daml/Splice/Amulet/TokenApiUtils.daml b/daml/splice-amulet/daml/Splice/Amulet/TokenApiUtils.daml
index 1b8f738c20..bd339412e3 100644
--- a/daml/splice-amulet/daml/Splice/Amulet/TokenApiUtils.daml
+++ b/daml/splice-amulet/daml/Splice/Amulet/TokenApiUtils.daml
@@ -43,7 +43,7 @@ nonZeroMetadata k n m
-- | Add an metadata entry for an optional value if it is non-zero number.
optionalNonZeroMetadata : (Eq a, Additive a, Show a) => Text -> Optional a -> TextMap Text -> TextMap Text
-optionalNonZeroMetadata k optN m =
+optionalNonZeroMetadata k optN m =
case optN of
None -> m
Some n -> nonZeroMetadata k n m
@@ -75,6 +75,9 @@ appRewardBeneficiariesMetaKey = amuletPrefix <> "app-reward-beneficiaries"
appRewardBeneficiaryWeightsMetaKey : Text
appRewardBeneficiaryWeightsMetaKey = amuletPrefix <> "app-reward-beneficiary-weights"
+developmentFundAmountMetaKey : Text
+developmentFundAmountMetaKey = amuletPrefix <> "development-fund"
+
-- Splice API Metadata keys
---------------------------
diff --git a/daml/splice-amulet/daml/Splice/AmuletConfig.daml b/daml/splice-amulet/daml/Splice/AmuletConfig.daml
index abb4dc9af0..c5c9f1bf4d 100644
--- a/daml/splice-amulet/daml/Splice/AmuletConfig.daml
+++ b/daml/splice-amulet/daml/Splice/AmuletConfig.daml
@@ -45,6 +45,8 @@ data AmuletConfig unit = AmuletConfig with
-- that should be used for command submissions.
transferPreapprovalFee : Optional Decimal -- ^ Fee for keeping a transfer pre-approval around.
featuredAppActivityMarkerAmount : Optional Decimal -- ^ $-amount used for the conversion from FeaturedAppActivityMarker -> AppRewardCoupon
+ optDevelopmentFundManager : Optional Party
+ -- ^ Party authorized to manage and allocate minting rights from the Development Fund.
deriving (Eq, Show)
-- $1/year specified as a daily rate
@@ -120,6 +122,7 @@ instance Patchable (AmuletConfig USD) where
packageConfig = patch new.packageConfig base.packageConfig current.packageConfig
transferPreapprovalFee = patch new.transferPreapprovalFee base.transferPreapprovalFee current.transferPreapprovalFee
featuredAppActivityMarkerAmount = patch new.featuredAppActivityMarkerAmount base.featuredAppActivityMarkerAmount current.featuredAppActivityMarkerAmount
+ optDevelopmentFundManager = patch new.optDevelopmentFundManager base.optDevelopmentFundManager current.optDevelopmentFundManager
instance Patchable (TransferConfig USD) where
patch new base current = TransferConfig with
diff --git a/daml/splice-amulet/daml/Splice/AmuletRules.daml b/daml/splice-amulet/daml/Splice/AmuletRules.daml
index 392e25fa9f..9ff3a16ea4 100644
--- a/daml/splice-amulet/daml/Splice/AmuletRules.daml
+++ b/daml/splice-amulet/daml/Splice/AmuletRules.daml
@@ -1,6 +1,7 @@
-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
+{-# LANGUAGE MultiWayIf #-}
module Splice.AmuletRules where
import Prelude hiding (forA)
@@ -64,6 +65,7 @@ data AmuletRules_AdvanceOpenMiningRoundsResult = AmuletRules_AdvanceOpenMiningRo
data AmuletRules_MiningRound_StartIssuingResult = AmuletRules_MiningRound_StartIssuingResult with
issuingRoundCid : ContractId IssuingMiningRound
+ unclaimedDevelopmentFundCouponCid : Optional (ContractId UnclaimedDevelopmentFundCoupon)
data AmuletRules_MiningRound_CloseResult = AmuletRules_MiningRound_CloseResult with
closedRoundCid : ContractId ClosedMiningRound
@@ -76,6 +78,13 @@ data AmuletRules_ClaimExpiredRewardsResult = AmuletRules_ClaimExpiredRewardsResu
data AmuletRules_MergeUnclaimedRewardsResult = AmuletRules_MergeUnclaimedRewardsResult with
unclaimedRewardCid : ContractId UnclaimedReward
+data AmuletRules_MergeUnclaimedDevelopmentFundCouponsResult = AmuletRules_MergeUnclaimedDevelopmentFundCouponsResult with
+ unclaimedDevelopmentFundCouponCid : ContractId UnclaimedDevelopmentFundCoupon
+
+data AmuletRules_AllocateDevelopmentFundCouponResult = AmuletRules_AllocateDevelopmentFundCouponResult with
+ developmentFundCouponCid : ContractId DevelopmentFundCoupon
+ optUnclaimedDevelopmentFundCouponCid : Optional (ContractId UnclaimedDevelopmentFundCoupon)
+
data AmuletRules_SetConfigResult = AmuletRules_SetConfigResult with
newAmuletRules : ContractId AmuletRules
@@ -450,6 +459,12 @@ template AmuletRules
dso
amount = totalUnclaimedRewards
+ -- record unclaimed development fund coupon contract
+ unclaimedDevelopmentFundCouponCid <-
+ case params.optAmuletsToIssueToDevelopmentFund of
+ Some amount | amount > 0.0 -> Some <$> create UnclaimedDevelopmentFundCoupon with dso; amount
+ _ -> pure None
+
-- create issuing round
now <- getTime
let tickDuration = miningRound.tickDuration
@@ -579,6 +594,68 @@ template AmuletRules
return AmuletRules_MergeUnclaimedRewardsResult with ..
+ nonconsuming choice AmuletRules_MergeUnclaimedDevelopmentFundCoupons : AmuletRules_MergeUnclaimedDevelopmentFundCouponsResult
+ -- ^ Batch merge of unclaimed development fund coupons
+ with
+ unclaimedDevelopmentFundCouponCids : [ContractId UnclaimedDevelopmentFundCoupon]
+ controller dso
+ do
+ require "More than one unclaimed development fund coupon contract" (length unclaimedDevelopmentFundCouponCids > 1)
+
+ -- archive all given coupons
+ archivedAmounts <- forA unclaimedDevelopmentFundCouponCids $ \unclaimedDevelopmentFundCouponCid -> do
+ unclaimedDevelopmentFundCoupon <- fetchAndArchive (ForDso with dso) unclaimedDevelopmentFundCouponCid
+ pure unclaimedDevelopmentFundCoupon.amount
+
+ -- create a new unclaimed development fund coupon over the total
+ unclaimedDevelopmentFundCouponCid <- create UnclaimedDevelopmentFundCoupon with
+ dso
+ amount = sum archivedAmounts
+
+ return AmuletRules_MergeUnclaimedDevelopmentFundCouponsResult with ..
+
+ nonconsuming choice AmuletRules_AllocateDevelopmentFundCoupon : AmuletRules_AllocateDevelopmentFundCouponResult
+ -- ^ Allows the Development Fund manager to allocate a specified amount from a
+ -- collection of UnclaimedDevelopmentFundCoupons to a beneficiary, generating a
+ -- DevelopmentFundCoupon and producing a leftover unclaimed coupon if applicable.
+ with
+ unclaimedDevelopmentFundCouponCids : [ContractId UnclaimedDevelopmentFundCoupon]
+ beneficiary : Party
+ amount : Decimal
+ expiresAt : Time
+ reason : Text
+ fundManager : Party
+ controller fundManager
+ do
+ -- Verify expiry
+ assertWithinDeadline "DevelopmentFundCoupon.expiresAt" expiresAt
+
+ -- Verify fundManager
+ configUsd <- getValueAsOfLedgerTime configSchedule
+ case configUsd.optDevelopmentFundManager of
+ None -> abort "DevelopmentFundCoupon cannot be allocated without a Development Fund manager configured"
+ Some developmentFundManager ->
+ require ("controller is the configured Development Fund manager: " <> show developmentFundManager) $
+ developmentFundManager == fundManager
+
+ -- Verify the amount and create a leftover UnclaimedDevelopmentFundCoupon if needed
+ totalUnclaimedDevelopmentFundCoupons <- sum <$> forA unclaimedDevelopmentFundCouponCids \cid -> do
+ coupon <- fetchAndArchive (ForDso with dso) cid
+ pure coupon.amount
+ let leftover = totalUnclaimedDevelopmentFundCoupons - amount
+ optUnclaimedDevelopmentFundCouponCid <-
+ if | leftover < 0.0 -> abort $ "insufficient amount to cover the requested allocation: " <> show (negate leftover)
+ | leftover == 0.0 -> pure None
+ | otherwise -> Some <$> create UnclaimedDevelopmentFundCoupon with dso; amount = leftover
+
+ -- record development fund coupon contract
+ developmentFundCouponCid <- create DevelopmentFundCoupon with
+ dso; beneficiary; fundManager; amount; expiresAt; reason
+
+ return AmuletRules_AllocateDevelopmentFundCouponResult with
+ developmentFundCouponCid
+ optUnclaimedDevelopmentFundCouponCid
+
-- This allows fetchByKey-style fetches if you have readAs
-- but not actAs claims.
nonconsuming choice AmuletRules_Fetch: AmuletRules
@@ -749,6 +826,7 @@ executeTransfer config context dso t = do
nonZeroMetadata appRewardAmountMetaKey summary.inputAppRewardAmount $
nonZeroMetadata validatorRewardAmountMetaKey summary.inputValidatorRewardAmount $
optionalNonZeroMetadata unclaimedActivityRecordAmountMetaKey summary.inputUnclaimedActivityRecordAmount $
+ optionalNonZeroMetadata developmentFundAmountMetaKey summary.inputDevelopmentFundAmount $
TextMap.empty
return TransferResult with
@@ -778,6 +856,9 @@ data TransferInputsSummary = TransferInputsSummary with
totalUnclaimedActivityRecordAmount : Optional Decimal
-- ^ Note: Made optional as the addition of this field is checked by the upgrade checker
-- on package upload because `TransferInputsSummary` is serializable.
+ totalDevelopmentFundAmount : Optional Decimal
+ -- ^ Note: Same rationale as above — made optional to ensure compatibility with
+ -- the upgrade checker on package upload because `TransferInputsSummary` is serializable.
deriving (Eq, Show)
type TransferOutputsSummary = [PreprocessedTransferOutput]
@@ -836,6 +917,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
foldlA (summarizeAndConsumeInput csum.openRound.round) initialSummary inps
where
forOwner = ForOwner with dso; owner = sender
+ forDso = ForDso with dso
initialSummary = TransferInputsSummary with
totalAmuletAmount = 0.0
@@ -847,6 +929,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
amountArchivedAsOfRoundZero = 0.0
changeToHoldingFeesRate = 0.0
totalUnclaimedActivityRecordAmount = Some 0.0
+ totalDevelopmentFundAmount = Some 0.0
summarizeAndConsumeInput _round s (InputAmulet amuletCid) = do
amulet <- fetchAndArchive forOwner amuletCid
@@ -863,6 +946,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero + getValueAsOfRound0 amulet.amount
changeToHoldingFeesRate = s.changeToHoldingFeesRate - amulet.amount.ratePerRound.rate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
summarizeAndConsumeInput _round s (InputAppRewardCoupon couponCid) = do
coupon <- fetchAndArchive forOwner couponCid
@@ -881,10 +965,11 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
summarizeAndConsumeInput _round s (InputValidatorRewardCoupon couponCid) = do
-- we must and do use the validator right to archive the coupon of the user
- coupon <- fetchButArchiveLater (ForDso with dso) couponCid
+ coupon <- fetchButArchiveLater forDso couponCid
do
rightCid <- getValidatorRight csum coupon.user
exercise couponCid ValidatorRewardCoupon_ArchiveAsValidator with
@@ -902,6 +987,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
summarizeAndConsumeInput _round s (InputSvRewardCoupon couponCid) = do
-- we use the SvRewardCoupon_ArchiveAsBeneficiary choice to signal the archival of the coupon
@@ -920,6 +1006,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
summarizeAndConsumeInput _round s (InputValidatorLivenessActivityRecord recordCid) = do
record <- fetchAndArchive forOwner recordCid
@@ -936,6 +1023,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
summarizeAndConsumeInput _round s (ExtTransferInput _dummyUnitField optInputValidatorFaucetCoupon) = do
optional (pure s) (summarizeAndConsumeValidatorFaucetInput s) optInputValidatorFaucetCoupon
@@ -954,6 +1042,22 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
+
+ summarizeAndConsumeInput _round s (InputDevelopmentFundCoupon couponCid) = do
+ coupon <- fetchAndArchive forOwner couponCid
+ assertWithinDeadline "DevelopmentFundCoupon.expiresAt" coupon.expiresAt
+ return TransferInputsSummary with
+ totalAmuletAmount = s.totalAmuletAmount
+ totalAppRewardAmount = s.totalAppRewardAmount
+ totalValidatorRewardAmount = s.totalValidatorRewardAmount
+ totalUnclaimedActivityRecordAmount = s.totalUnclaimedActivityRecordAmount
+ totalValidatorFaucetAmount = s.totalValidatorFaucetAmount
+ totalSvRewardAmount = s.totalSvRewardAmount
+ totalHoldingFees = s.totalHoldingFees
+ amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
+ changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = (+ coupon.amount) <$> s.totalDevelopmentFundAmount
summarizeAndConsumeValidatorFaucetInput s couponCid = do
coupon <- fetchAndArchive forOwner couponCid
@@ -970,6 +1074,7 @@ summarizeAndConsumeInputs csum dso sender inps = do
totalHoldingFees = s.totalHoldingFees
amountArchivedAsOfRoundZero = s.amountArchivedAsOfRoundZero
changeToHoldingFeesRate = s.changeToHoldingFeesRate
+ totalDevelopmentFundAmount = s.totalDevelopmentFundAmount
-- | Deduplicate lock-holders to store them and charge for them at most once
dedupOutputLockHolders : TransferOutput -> TransferOutput
@@ -1029,6 +1134,7 @@ summarizeTransfer sender openRound transferConfigAmulet inp preprocessedOutputs
+ fromOptional 0.0 inp.totalUnclaimedActivityRecordAmount
+ inp.totalValidatorFaucetAmount
+ inp.totalSvRewardAmount
+ + fromOptional 0.0 inp.totalDevelopmentFundAmount
- totalOutputAmount - sum outputFees
senderChangeFee = min transferConfigAmulet.createFee.fee leftOverAmount
senderChangeAmount = leftOverAmount - senderChangeFee
@@ -1054,6 +1160,7 @@ summarizeTransfer sender openRound transferConfigAmulet inp preprocessedOutputs
inputUnclaimedActivityRecordAmount = inp.totalUnclaimedActivityRecordAmount
inputValidatorFaucetAmount = Some inp.totalValidatorFaucetAmount
inputSvRewardAmount = inp.totalSvRewardAmount
+ inputDevelopmentFundAmount = inp.totalDevelopmentFundAmount
holdingFees = inp.totalHoldingFees
outputFees
senderChangeFee
@@ -1245,6 +1352,7 @@ data TransferInput
-- ^ Added in CIP-3. Optional validator faucet coupon input into this transfer.
| InputValidatorLivenessActivityRecord (ContractId ValidatorLivenessActivityRecord)
| InputUnclaimedActivityRecord (ContractId UnclaimedActivityRecord)
+ | InputDevelopmentFundCoupon (ContractId DevelopmentFundCoupon)
deriving (Eq, Ord, Show)
-- | Smart constructor for inputing validator faucet coupons into a transfer.
@@ -1348,6 +1456,9 @@ data TransferSummary = TransferSummary with
inputUnclaimedActivityRecordAmount : Optional Decimal
-- ^ Total amount of unclaimed activity record issuance input into this transfer.
-- Note: Made optional as the addition of this field is checked by the upgrade checker.
+ inputDevelopmentFundAmount : Optional Decimal
+ -- ^ Total amount of development fund coupon issuance input into this transfer.
+ -- Note: Made optional as the addition of this field is checked by the upgrade checker.
deriving (Show, Eq)
data BalanceChange = BalanceChange with
diff --git a/daml/splice-amulet/daml/Splice/Issuance.daml b/daml/splice-amulet/daml/Splice/Issuance.daml
index 91da289785..d14d8f07b0 100644
--- a/daml/splice-amulet/daml/Splice/Issuance.daml
+++ b/daml/splice-amulet/daml/Splice/Issuance.daml
@@ -25,6 +25,8 @@ data IssuanceConfig = IssuanceConfig with
optValidatorFaucetCap : Optional Decimal
-- ^ Maximal amount in $ for the per-validator issuance of validator faucet coupons;
-- Introduced as part of CIP-3. Defaults to 2.85 USD.
+ optDevelopmentFundPercentage : Optional Decimal
+ -- ^ Percentage of each mint emission allocated to the Development Fund under CIP-0082.
deriving (Eq, Show)
-- | Getter with the right default value for the validator faucet cap.
@@ -45,6 +47,7 @@ validIssuanceConfig this@IssuanceConfig{..} =
&& featuredAppRewardCap >= 0.0
&& unfeaturedAppRewardCap >= 0.0
&& getValidatorFaucetCap this >= 0.0
+ && optional True (\pct -> pct >= 0.0 && pct <= 1.0) optDevelopmentFundPercentage
-- computation of issuance per round
@@ -73,6 +76,9 @@ data IssuingRoundParameters = IssuingRoundParameters with
unclaimedValidatorRewards : Decimal
unclaimedSvRewards : Decimal -- ^ Can be non-zero due to rounding, or no SV having had the chance to claim their coupons.
issuancePerValidatorFaucetCoupon : Decimal
+ optAmuletsToIssueToDevelopmentFund : Optional Decimal
+ -- ^ Note: Made optional as the addition of this field is checked by the upgrade checker
+ -- on package upload because `IssuingRoundParameters` is serializable.
deriving (Eq, Show)
validateOpenMiningRoundSummary : CanAssert m => OpenMiningRoundSummary -> m ()
@@ -94,9 +100,14 @@ computeIssuingRoundParameters tickDuration amuletPrice config summary =
unclaimedAppRewards = featuredAppIssuance.unclaimedRewards
unclaimedSvRewards
issuancePerValidatorFaucetCoupon = validatorFaucetIssuance.issuancePerCoupon
+ optAmuletsToIssueToDevelopmentFund = Some amuletsToIssueToDevelopmentFund
where
+ developmentFundPercentage = fromOptional 0.05 config.optDevelopmentFundPercentage
+
amuletsToIssueToSvs =
- amuletsToIssueInRound - validatorRewardIssuance.rewardsToIssue - unfeaturedAppIssuance.rewardsToIssue
+ adjustedAmuletsToIssueInRound
+ - validatorRewardIssuance.rewardsToIssue
+ - unfeaturedAppIssuance.rewardsToIssue
issuancePerSvRewardCoupon =
if summary.totalSvRewardWeight == 0
@@ -114,8 +125,11 @@ computeIssuingRoundParameters tickDuration amuletPrice config summary =
intToDecimal (convertRelTimeToMicroseconds tickDuration)
amuletsToIssueInRound = config.amuletToIssuePerYear / roundsPerYear
+ amuletsToIssueToDevelopmentFund = amuletsToIssueInRound * developmentFundPercentage
+ adjustedAmuletsToIssueInRound = amuletsToIssueInRound - amuletsToIssueToDevelopmentFund
+
validatorRewardIssuance = computeIssuanceTranche
- (amuletsToIssueInRound * config.validatorRewardPercentage)
+ (adjustedAmuletsToIssueInRound * config.validatorRewardPercentage)
config.validatorRewardCap
summary.totalValidatorRewardCoupons
@@ -126,7 +140,7 @@ computeIssuingRoundParameters tickDuration amuletPrice config summary =
(intToDecimal $ getTotalValidatorFaucetCoupons summary)
unfeaturedAppIssuance = computeIssuanceTranche
- (amuletsToIssueInRound * config.appRewardPercentage)
+ (adjustedAmuletsToIssueInRound * config.appRewardPercentage)
config.unfeaturedAppRewardCap
(summary.totalFeaturedAppRewardCoupons + summary.totalUnfeaturedAppRewardCoupons)
@@ -179,3 +193,4 @@ instance Patchable IssuanceConfig where
featuredAppRewardCap = patch new.featuredAppRewardCap base.featuredAppRewardCap current.featuredAppRewardCap
unfeaturedAppRewardCap = patch new.unfeaturedAppRewardCap base.unfeaturedAppRewardCap current.unfeaturedAppRewardCap
optValidatorFaucetCap = patch new.optValidatorFaucetCap base.optValidatorFaucetCap current.optValidatorFaucetCap
+ optDevelopmentFundPercentage = patch new.optDevelopmentFundPercentage base.optDevelopmentFundPercentage current.optDevelopmentFundPercentage
diff --git a/daml/splice-dso-governance-test/daml.yaml b/daml/splice-dso-governance-test/daml.yaml
index e70244196f..428dc24cc7 100644
--- a/daml/splice-dso-governance-test/daml.yaml
+++ b/daml/splice-dso-governance-test/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-dso-governance-test
source: daml
-version: 0.1.24
+version: 0.1.25
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-dso-governance-test/daml/Splice/Scripts/DsoTestUtils.daml b/daml/splice-dso-governance-test/daml/Splice/Scripts/DsoTestUtils.daml
index 17abe747c3..8187ce50ad 100644
--- a/daml/splice-dso-governance-test/daml/Splice/Scripts/DsoTestUtils.daml
+++ b/daml/splice-dso-governance-test/daml/Splice/Scripts/DsoTestUtils.daml
@@ -9,12 +9,14 @@ import DA.Assert
import DA.Foldable (forA_)
import DA.List
import qualified DA.Map as Map
+import DA.Optional (fromOptional)
import qualified DA.Set as Set
import qualified DA.Text as T
import Daml.Script
import DA.Time
import Splice.Amulet
+import Splice.AmuletConfig (AmuletConfig(..), USD)
import Splice.AmuletRules
import Splice.Issuance
import Splice.Round
@@ -38,24 +40,26 @@ bpsMultiplier : Int
bpsMultiplier = 10000
initMainNet : Script (AmuletApp, Party, (Party, Party, Party, Party))
-initMainNet = initDecentralizedSynchronizer False
+initMainNet = initDecentralizedSynchronizer False None
initMainNetWithAmuletPrice : Decimal -> Script (AmuletApp, Party, (Party, Party, Party, Party))
-initMainNetWithAmuletPrice = initDecentralizedSynchronizerWithAmuletPrice False 0
+initMainNetWithAmuletPrice amuletPrice = initDecentralizedSynchronizerWithAmuletPrice False 0 amuletPrice None
initDevNet : Script (AmuletApp, Party, (Party, Party, Party, Party))
-initDevNet = initDecentralizedSynchronizer True
+initDevNet = initDecentralizedSynchronizer True None
+initDevNetWithAmuletConfig : AmuletConfig USD -> Script (AmuletApp, Party, (Party, Party, Party, Party))
+initDevNetWithAmuletConfig amuletConfig = initDecentralizedSynchronizer True (Some amuletConfig)
-initDecentralizedSynchronizer : Bool -> Script (AmuletApp, Party, (Party, Party, Party, Party))
-initDecentralizedSynchronizer isDevNet = initDecentralizedSynchronizerWithAmuletPrice isDevNet 0 1.0
+initDecentralizedSynchronizer : Bool -> Optional (AmuletConfig USD) -> Script (AmuletApp, Party, (Party, Party, Party, Party))
+initDecentralizedSynchronizer isDevNet optAmuletConfig = initDecentralizedSynchronizerWithAmuletPrice isDevNet 0 1.0 optAmuletConfig
initDecentralizedSynchronizerWithNonZeroRound : Bool -> Int -> Script (AmuletApp, Party, (Party, Party, Party, Party))
-initDecentralizedSynchronizerWithNonZeroRound isDevNet initialRound = initDecentralizedSynchronizerWithAmuletPrice isDevNet initialRound 1.0
+initDecentralizedSynchronizerWithNonZeroRound isDevNet initialRound = initDecentralizedSynchronizerWithAmuletPrice isDevNet initialRound 1.0 None
-initDecentralizedSynchronizerWithAmuletPrice : Bool -> Int -> Decimal -> Script (AmuletApp, Party, (Party, Party, Party, Party))
-initDecentralizedSynchronizerWithAmuletPrice isDevNet initialRound amuletPrice = do
+initDecentralizedSynchronizerWithAmuletPrice : Bool -> Int -> Decimal -> Optional (AmuletConfig USD) -> Script (AmuletApp, Party, (Party, Party, Party, Party))
+initDecentralizedSynchronizerWithAmuletPrice isDevNet initialRound amuletPrice optAmuletConfig = do
[sv1, sv2, sv3, sv4] <- forA ["sv1", "sv2", "sv3", "sv4"] allocateParty
dso <- allocateParty "dso-party"
@@ -82,7 +86,7 @@ initDecentralizedSynchronizerWithAmuletPrice isDevNet initialRound amuletPrice =
decentralizedSynchronizer = initialDsoDecentralizedSynchronizerConfig
nextScheduledSynchronizerUpgrade = None
voteCooldownTime = None -- use default value of 1 minute
- let amuletConfig = defaultAmuletConfig
+ let amuletConfig = fromOptional defaultAmuletConfig optAmuletConfig
let ansRulesConfig = defaultAnsRulesConfig
now <- getTime
diff --git a/daml/splice-dso-governance-test/daml/Splice/Scripts/TestDecentralizedAutomation.daml b/daml/splice-dso-governance-test/daml/Splice/Scripts/TestDecentralizedAutomation.daml
index 5d08b8d107..045a1e36b3 100644
--- a/daml/splice-dso-governance-test/daml/Splice/Scripts/TestDecentralizedAutomation.daml
+++ b/daml/splice-dso-governance-test/daml/Splice/Scripts/TestDecentralizedAutomation.daml
@@ -3,6 +3,7 @@
module Splice.Scripts.TestDecentralizedAutomation where
+import DA.Action (replicateA_)
import DA.Assert
import DA.Foldable (forA_)
import DA.List()
@@ -26,6 +27,7 @@ import Splice.Scripts.DsoTestUtils
import Splice.Ans
import Splice.Scripts.AnsRulesParameters
import Splice.Scripts.TestTransferPreapproval
+import Splice.Testing.Registries.AmuletRegistry.Parameters
import Splice.Wallet.Subscriptions
@@ -53,6 +55,96 @@ testUnclaimedRewardsMerging = do
pure ()
+-- Development Fund
+--------------------
+
+testUnclaimedDevelopmentFundCouponsMerging : Script ()
+testUnclaimedDevelopmentFundCouponsMerging = do
+ let
+ -- 5% Development Fund
+ amuletConfig =
+ defaultAmuletConfig with
+ issuanceCurve = defaultAmuletConfig.issuanceCurve with
+ initialValue = defaultAmuletConfig.issuanceCurve.initialValue with
+ optDevelopmentFundPercentage = Some 0.05
+ (app, _, (sv1, _, _, _)) <- initDevNetWithAmuletConfig amuletConfig
+
+ -- Mint 5 unclaimed development fund coupons
+ replicateA_ 5 $ runNextIssuanceD app 1.0
+
+ [(amuletRulesCid, _)] <- query @AmuletRules app.dso
+ unclaimedDevelopmentFundCouponCids@(cid1 :: _) <- fmap fst <$> query @UnclaimedDevelopmentFundCoupon app.dso
+ length unclaimedDevelopmentFundCouponCids === 5
+
+ -- Unhappy path - requires more than one development fund coupon contract.
+ dsoDelegateSubmitsMustFail app $ \cid -> exerciseCmd cid $
+ DsoRules_MergeUnclaimedDevelopmentFundCoupons with
+ amuletRulesCid
+ choiceArg = AmuletRules_MergeUnclaimedDevelopmentFundCoupons with
+ unclaimedDevelopmentFundCouponCids = [cid1]
+ sv = sv1
+
+ -- Happy path
+ dsoDelegateSubmits app $ \cid -> exerciseCmd cid $
+ DsoRules_MergeUnclaimedDevelopmentFundCoupons with
+ amuletRulesCid
+ choiceArg = AmuletRules_MergeUnclaimedDevelopmentFundCoupons with
+ unclaimedDevelopmentFundCouponCids
+ sv = sv1
+
+ unclaimedDevelopmentFundCoupons <- query @UnclaimedDevelopmentFundCoupon app.dso
+ length unclaimedDevelopmentFundCoupons === 1
+
+ pure ()
+
+testDevelopmentFundCouponExpiry : Script ()
+testDevelopmentFundCouponExpiry = do
+ fundManager <- allocateParty "FundManager"
+ let
+ -- 5% Development Fund
+ amuletConfig =
+ defaultAmuletConfig with
+ optDevelopmentFundManager = Some fundManager
+ issuanceCurve = defaultAmuletConfig.issuanceCurve with
+ initialValue = defaultAmuletConfig.issuanceCurve.initialValue with
+ optDevelopmentFundPercentage = Some 0.05
+ (app, _, (sv1, _, _, _)) <- initDevNetWithAmuletConfig amuletConfig
+ [(dsoRulesCid, _)] <- query @DsoRules app.dso
+
+ -- Mint 1 unclaimed development fund coupon
+ runNextIssuanceD app 1.0
+ [(unclaimedDevelopmentFundCouponCid, unclaimedDevelopmentFundCoupon)] <- query @UnclaimedDevelopmentFundCoupon app.dso
+
+ -- Allocate a development fund coupon
+ now <- getTime
+ let
+ expiresAt = addRelTime now (hours 1)
+ amount = unclaimedDevelopmentFundCoupon.amount
+ reason = "Alice fixed issue XXX"
+ alice <- setupUser app "alice" app.dso
+ developmentFundCouponCid <- (.developmentFundCouponCid) <$>
+ allocateDevelopmentFundCoupon app fundManager alice.primaryParty amount expiresAt reason [unclaimedDevelopmentFundCouponCid]
+
+ -- Unhappy - expiresAt has not been reached
+ submitMultiMustFail [sv1] [app.dso] do
+ exerciseCmd dsoRulesCid DsoRules_ExpireDevelopmentFundCoupon with
+ developmentFundCouponCid
+ sv = sv1
+
+ -- Happy
+ setTime $ addRelTime expiresAt (minutes 1)
+ unclaimedDevelopmentFundCouponCid <- (.unclaimedDevelopmentFundCouponCid) . (.result) <$>
+ submitMulti [sv1] [app.dso] do
+ exerciseCmd dsoRulesCid DsoRules_ExpireDevelopmentFundCoupon with
+ developmentFundCouponCid
+ sv = sv1
+ Some unclaimedDevelopmentFundCoupon <- queryContractId app.dso unclaimedDevelopmentFundCouponCid
+ unclaimedDevelopmentFundCoupon === UnclaimedDevelopmentFundCoupon with
+ dso = app.dso
+ amount
+
+ pure ()
+
-- Testing confirmations
------------------------
diff --git a/daml/splice-dso-governance-test/daml/Splice/Scripts/TestGovernance.daml b/daml/splice-dso-governance-test/daml/Splice/Scripts/TestGovernance.daml
index c71be96ff0..da4495b672 100644
--- a/daml/splice-dso-governance-test/daml/Splice/Scripts/TestGovernance.daml
+++ b/daml/splice-dso-governance-test/daml/Splice/Scripts/TestGovernance.daml
@@ -730,6 +730,7 @@ testAmuletRulesTickDurationChange = do
testAmuletRulesConfigChange : Script ()
testAmuletRulesConfigChange = do
(app, dso, (sv1, sv2, sv3, _)) <- initMainNet
+ let Some fundManager = partyFromText "FundManager"
[(dsoRulesCid, _)] <- query @DsoRules dso
@@ -766,6 +767,7 @@ testAmuletRulesConfigChange = do
amuletToIssuePerYear = 40e9
validatorRewardPercentage = 0.05
appRewardPercentage = 0.15
+ optDevelopmentFundPercentage = Some 0.05
let defaultIssuanceCurve2 = Schedule with
initialValue = issuanceConfig_0p5_1p5_2
@@ -778,6 +780,7 @@ testAmuletRulesConfigChange = do
transferConfig = defaultTransferConfig2
issuanceCurve = defaultIssuanceCurve2
tickDuration = seconds 200
+ optDevelopmentFundManager = Some fundManager
-- second config that changes the other half of the parameters
let newConfig2 = amuletRules.configSchedule.initialValue with
diff --git a/daml/splice-dso-governance/daml.yaml b/daml/splice-dso-governance/daml.yaml
index ee90977197..f8e314e1c9 100644
--- a/daml/splice-dso-governance/daml.yaml
+++ b/daml/splice-dso-governance/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-dso-governance
source: daml
-version: 0.1.20
+version: 0.1.21
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-dso-governance/daml/Splice/DsoRules.daml b/daml/splice-dso-governance/daml/Splice/DsoRules.daml
index 71990a32ea..207923b9f7 100644
--- a/daml/splice-dso-governance/daml/Splice/DsoRules.daml
+++ b/daml/splice-dso-governance/daml/Splice/DsoRules.daml
@@ -253,6 +253,12 @@ data DsoRules_ClaimExpiredRewardsResult = DsoRules_ClaimExpiredRewardsResult wit
data DsoRules_MergeUnclaimedRewardsResult = DsoRules_MergeUnclaimedRewardsResult with
unclaimedReward: ContractId UnclaimedReward
+data DsoRules_MergeUnclaimedDevelopmentFundCouponsResult = DsoRules_MergeUnclaimedDevelopmentFundCouponsResult with
+ result : AmuletRules_MergeUnclaimedDevelopmentFundCouponsResult
+
+data DsoRules_ExpireDevelopmentFundCouponResult = DsoRules_ExpireDevelopmentFundCouponResult with
+ result : DevelopmentFundCoupon_DsoExpireResult
+
data DsoRules_MiningRound_CloseResult = DsoRules_MiningRound_CloseResult with
closedRound : ContractId ClosedMiningRound
@@ -1318,6 +1324,29 @@ template DsoRules with
return DsoRules_MergeUnclaimedRewardsResult with
unclaimedReward = result.unclaimedRewardCid
+ nonconsuming choice DsoRules_MergeUnclaimedDevelopmentFundCoupons : DsoRules_MergeUnclaimedDevelopmentFundCouponsResult
+ -- ^ Batch merge of of development fund coupons.
+ with
+ amuletRulesCid : ContractId AmuletRules
+ choiceArg : AmuletRules_MergeUnclaimedDevelopmentFundCoupons
+ sv : Party
+ controller sv
+ do
+ _ <- getAndValidateSvParty this (Some sv)
+ result <- exercise amuletRulesCid choiceArg
+ return DsoRules_MergeUnclaimedDevelopmentFundCouponsResult with result
+
+ nonconsuming choice DsoRules_ExpireDevelopmentFundCoupon : DsoRules_ExpireDevelopmentFundCouponResult
+ -- ^ Expires a DevelopmentFundCoupon and produces an UnclaimedDevelopmentFundCoupon with the same amount.
+ with
+ developmentFundCouponCid : ContractId DevelopmentFundCoupon
+ sv : Party
+ controller sv
+ do
+ _ <- getAndValidateSvParty this (Some sv)
+ result <- exercise developmentFundCouponCid DevelopmentFundCoupon_DsoExpire
+ pure $ DsoRules_ExpireDevelopmentFundCouponResult with result
+
nonconsuming choice DsoRules_MiningRound_Close : DsoRules_MiningRound_CloseResult
with
amuletRulesCid : ContractId AmuletRules
diff --git a/daml/splice-util-featured-app-proxies-test/daml.yaml b/daml/splice-util-featured-app-proxies-test/daml.yaml
index 63dbbf1d78..26e7d2b487 100644
--- a/daml/splice-util-featured-app-proxies-test/daml.yaml
+++ b/daml/splice-util-featured-app-proxies-test/daml.yaml
@@ -11,7 +11,7 @@ description: |
are normal .dar files and can be shared by copying the .dars.
(TODO(#594): remove this limitation)
source: daml
-version: 1.0.6
+version: 1.0.7
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-util-token-standard-wallet-test/daml.yaml b/daml/splice-util-token-standard-wallet-test/daml.yaml
index f48fa48e42..2570b91a1b 100644
--- a/daml/splice-util-token-standard-wallet-test/daml.yaml
+++ b/daml/splice-util-token-standard-wallet-test/daml.yaml
@@ -4,7 +4,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-util-token-standard-wallet-test
source: daml
-version: 1.0.1
+version: 1.0.2
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-util/daml.yaml b/daml/splice-util/daml.yaml
index a65870adc0..49c43d09f9 100644
--- a/daml/splice-util/daml.yaml
+++ b/daml/splice-util/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-util
source: daml
-version: 0.1.4
+version: 0.1.5
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-util/daml/Splice/Util.daml b/daml/splice-util/daml/Splice/Util.daml
index 80ee7987cb..aa9a17a0cd 100644
--- a/daml/splice-util/daml/Splice/Util.daml
+++ b/daml/splice-util/daml/Splice/Util.daml
@@ -206,6 +206,9 @@ instance Patchable RelTime where
instance Patchable Time where
patch = patchScalar
+instance Patchable Party where
+ patch = patchScalar
+
mapDifference : Ord k => Map k a -> Map k a -> Map k k
mapDifference = Map.merge (\_ _ -> None) (\k _ -> Some k) (\_ _ _ -> None)
diff --git a/daml/splice-validator-lifecycle-test/daml.yaml b/daml/splice-validator-lifecycle-test/daml.yaml
index c4b607ca5c..a4287301a2 100644
--- a/daml/splice-validator-lifecycle-test/daml.yaml
+++ b/daml/splice-validator-lifecycle-test/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-validator-lifecycle-test
source: daml
-version: 0.1.5
+version: 0.1.6
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-validator-lifecycle/daml.yaml b/daml/splice-validator-lifecycle/daml.yaml
index a5193b1b7d..de03e7f857 100644
--- a/daml/splice-validator-lifecycle/daml.yaml
+++ b/daml/splice-validator-lifecycle/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-validator-lifecycle
source: daml
-version: 0.1.5
+version: 0.1.6
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-wallet-payments/daml.yaml b/daml/splice-wallet-payments/daml.yaml
index ba6e3a65b0..3c101ac4a2 100644
--- a/daml/splice-wallet-payments/daml.yaml
+++ b/daml/splice-wallet-payments/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-wallet-payments
source: daml
-version: 0.1.14
+version: 0.1.15
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-wallet-test/daml.yaml b/daml/splice-wallet-test/daml.yaml
index 4426c9813a..5e65c83e29 100644
--- a/daml/splice-wallet-test/daml.yaml
+++ b/daml/splice-wallet-test/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-wallet-test
source: daml
-version: 0.1.17
+version: 0.1.18
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splice-wallet/daml.yaml b/daml/splice-wallet/daml.yaml
index 77310ac0a6..cf1971b83f 100644
--- a/daml/splice-wallet/daml.yaml
+++ b/daml/splice-wallet/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splice-wallet
source: daml
-version: 0.1.14
+version: 0.1.15
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splitwell-test/daml.yaml b/daml/splitwell-test/daml.yaml
index dde8ced5ea..ce9caec535 100644
--- a/daml/splitwell-test/daml.yaml
+++ b/daml/splitwell-test/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splitwell-test
source: daml
-version: 0.1.17
+version: 0.1.18
dependencies:
- daml-prim
- daml-stdlib
diff --git a/daml/splitwell/daml.yaml b/daml/splitwell/daml.yaml
index e1e7d4f729..762e112666 100644
--- a/daml/splitwell/daml.yaml
+++ b/daml/splitwell/daml.yaml
@@ -1,7 +1,7 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: splitwell
source: daml
-version: 0.1.14
+version: 0.1.15
dependencies:
- daml-prim
- daml-stdlib
diff --git a/docs/src/release_notes.rst b/docs/src/release_notes.rst
index 498b23db4b..811f412128 100644
--- a/docs/src/release_notes.rst
+++ b/docs/src/release_notes.rst
@@ -9,7 +9,97 @@
.. release-notes:: 0.5.6
- - Sequencer
+ .. important::
+
+ **Action recommended from app devs:**
+
+ **App devs whose app's Daml code statically depends on** ``splice-amulet < 0.1.15`` should recompile their Daml code
+ to link against ``splice-amulet >= 0.1.15`` in order to be ready to consume the two new fields introduced in `AmuletConfig`
+ (`optDevelopmentFundManager`) and `IssuanceConfig` (`optDevelopmentFundPercentage`) once either of them is set.
+
+ This is required because once the new fields are set, downgrades of `AmuletRules` will fail.
+ At the moment, this recompilation is not strictly required, as setting these fields is not planned immediately.
+
+ No change is required for apps that build against the :ref:`token_standard`
+ or :ref:`featured_app_activity_markers_api`.
+
+ - Daml
+
+ - Implement Daml changes for `CIP-0082 - Establish a 5% Development Fund (Foundation-Governed) `__:
+
+ - New templates:
+
+ - **UnclaimedDevelopmentFundCoupon**: Represents unallocated Development Fund entitlements created per issuance round.
+ Coupons are owned by the DSO, have no expiry, and serve as accounting instruments.
+ ACS size is managed through merging rather than expiration.
+
+ - **DevelopmentFundCoupon**: Represents an allocated portion of the Development Fund for a specific beneficiary.
+ Coupons can be withdrawn by the Development Fund Manager or expired by the DSO, in both cases restoring the
+ amount to an unclaimed coupon.
+
+ - Configuration extensions:
+
+ - ``IssuanceConfig`` is extended with an optional ``optDevelopmentFundPercentage``, defining the fraction of each
+ mint allocated to the Development Fund (validated to be within ``[0.0, 1.0]``).
+
+ - ``AmuletConfig`` is extended with an optional ``optDevelopmentFundManager``, designating the party authorized
+ to allocate Development Fund entitlements.
+
+ - AmuletRules updates:
+
+ - Modify ``AmuletRules_MiningRound_StartIssuing``: Issuance logic now deducts the Development Fund share
+ before distributing rewards. When a nonzero allocation is configured, a new
+ ``UnclaimedDevelopmentFundCoupon`` is created per round. If ``optDevelopmentFundPercentage`` is ``None``,
+ a default value of **0.05** is applied.
+ The accrual of ``UnclaimedDevelopmentFundCoupon`` contracts thus starts
+ as soon as the new Daml models are voted in.
+
+ - A new choice ``AmuletRules_MergeUnclaimedDevelopmentFundCoupons``: Adds a batch merge operation to combine
+ multiple ``UnclaimedDevelopmentFundCoupon`` contracts into a single one for ACS size control.
+
+ - A new choice ``AmuletRules_AllocateDevelopmentFundCoupon``: Allows the Development Fund Manager to allocate
+ unclaimed entitlements to beneficiaries, creating ``DevelopmentFundCoupon`` contracts and returning any
+ remaining unclaimed amount.
+
+ - Modify ``AmuletRules_Transfer``: Transfers now accept ``DevelopmentFundCoupon`` as a valid input when
+ the sender matches the beneficiary and report the total Development Fund amount consumed.
+
+ - DsoRules updates:
+
+ - A a new choice ``DsoRules_MergeUnclaimedDevelopmentFundCoupons``: Enables the DSO to trigger unclaimed coupon
+ merges via governance.
+
+ - Add a new ``DsoRules_ExpireDevelopmentFundCoupon``: Allows the DSO to expire an allocated
+ ``DevelopmentFundCoupon``, restoring its amount to an ``UnclaimedDevelopmentFundCoupon``.
+
+ Note that the UI changes in the Wallet app required to allocate funds are not yet implemented and will be delivered in a later release. Please refer to
+ this issue: `Tracking - CIP-0082 - 5% Development Fund `.
+
+ These Daml changes require an upgrade to the following Daml versions **before**
+ voting to set the transfer fees to zero:
+
+ ================== =======
+ name version
+ ================== =======
+ amulet 0.1.15
+ amuletNameService 0.1.16
+ dsoGovernance 0.1.21
+ splitwell 0.1.15
+ validatorLifecycle 0.1.6
+ wallet 0.1.15
+ walletPayments 0.1.15
+ ================== =======
+
+ - SV app
+
+ - Add a new trigger, `MergeUnclaimedDevelopmentFundCouponsTrigger`` that automatically monitors ``UnclaimedDevelopmentFundCoupon`` and,
+ once their number reaches at least twice the configured threshold, merges the smallest coupons into a single one.
+ This approach keeps contract-ids of larger coupons stable to minimize contention with externally prepared transactions which reference these ids.
+
+ - Add a new config field to ``SvOnboardingConfig`` named ``unclaimedDevelopmentFundCouponsThreshold`` defining the
+ threshold above which ``UnclaimedDevelopmentFundCoupon`` s are merged. The default value is set to 10.
+
+ - Sequencer
- Includes a number of performance improvements that should improve the stability of the sequencer under higher load.
diff --git a/scripts/scan-txlog/scan_txlog.py b/scripts/scan-txlog/scan_txlog.py
index 3cd836e406..cca4c243bb 100755
--- a/scripts/scan-txlog/scan_txlog.py
+++ b/scripts/scan-txlog/scan_txlog.py
@@ -3909,6 +3909,8 @@ def handle_root_exercised_event(self, transaction, event):
return HandleTransactionResult.empty()
case "DsoRules_MergeValidatorLicense":
return HandleTransactionResult.empty()
+ case "DsoRules_MergeUnclaimedDevelopmentFundCoupons":
+ return HandleTransactionResult.empty()
case "ExternalPartyAmuletRules_CreateTransferCommand":
return HandleTransactionResult.empty()
case "FeaturedAppRight_CreateActivityMarker":
diff --git a/test-full-class-names.log b/test-full-class-names.log
index 68442c0270..2179905b69 100644
--- a/test-full-class-names.log
+++ b/test-full-class-names.log
@@ -9,6 +9,7 @@ org.lfdecentralizedtrust.splice.integration.tests.BootstrapPackageConfigDarUploa
org.lfdecentralizedtrust.splice.integration.tests.BootstrapTest
org.lfdecentralizedtrust.splice.integration.tests.CombinedDumpDirectoryExportIntegrationTest
org.lfdecentralizedtrust.splice.integration.tests.ConfigurationProvidedBftScanConnectionIntegrationTest
+org.lfdecentralizedtrust.splice.integration.tests.DevelopmentFundCouponIntegrationTest
org.lfdecentralizedtrust.splice.integration.tests.DirectoryPeriodicBackupIntegrationTest
org.lfdecentralizedtrust.splice.integration.tests.DistributedDomainIntegrationTest
org.lfdecentralizedtrust.splice.integration.tests.ExternalPartySetupProposalIntegrationTest
diff --git a/token-standard/splice-token-standard-test/daml.yaml b/token-standard/splice-token-standard-test/daml.yaml
index 2cc5e6b52a..13765be412 100644
--- a/token-standard/splice-token-standard-test/daml.yaml
+++ b/token-standard/splice-token-standard-test/daml.yaml
@@ -16,7 +16,7 @@ description: |
as Daml script code can currently not be shared via .dars across SDKs. The dependencies
are normal .dar files and can be shared by copying the .dars.
(TODO(#594): remove this limitation)
-version: 1.0.8
+version: 1.0.9
source: daml
dependencies:
- daml-prim
diff --git a/token-standard/splice-token-standard-test/daml/Splice/Testing/Registries/AmuletRegistry/Parameters.daml b/token-standard/splice-token-standard-test/daml/Splice/Testing/Registries/AmuletRegistry/Parameters.daml
index a806f1c567..f70b864461 100644
--- a/token-standard/splice-token-standard-test/daml/Splice/Testing/Registries/AmuletRegistry/Parameters.daml
+++ b/token-standard/splice-token-standard-test/daml/Splice/Testing/Registries/AmuletRegistry/Parameters.daml
@@ -88,6 +88,8 @@ defaultAmuletConfig = AmuletConfig with
-- Amount of the AppRewardCoupon contract that a FeaturedAppActivityMarker is converted to.
featuredAppActivityMarkerAmount = Some defaultFeaturedAppActivityMarkerAmount
+ optDevelopmentFundManager = None
+
-- | Default configuration schedule with single current amulet config
defaultAmuletConfigSchedule : Schedule Time (AmuletConfig USD)
defaultAmuletConfigSchedule = Schedule with
@@ -131,6 +133,7 @@ issuanceConfig_10plus = IssuanceConfig with
featuredAppRewardCap = 100.0
unfeaturedAppRewardCap = 0.6
optValidatorFaucetCap = None -- We use the default of 2.85 USD introduced in the upgrade for CIP-3
+ optDevelopmentFundPercentage = Some 0.0
defaultIssuanceCurve : Schedule RelTime IssuanceConfig