Skip to content

Commit 09b8ac9

Browse files
cip-82: add allocateDevelopmentFundCoupon to Wallet API
Signed-off-by: Jose Velasco <jose.velasco@intellecteu.com>
1 parent c905351 commit 09b8ac9

File tree

4 files changed

+215
-1
lines changed

4 files changed

+215
-1
lines changed

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/console/WalletAppReference.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.lfdecentralizedtrust.splice.codegen.java.splice.wallet.{
1616
import org.lfdecentralizedtrust.splice.environment.SpliceConsoleEnvironment
1717
import org.lfdecentralizedtrust.splice.http.v0.definitions.{
1818
AllocateAmuletResponse,
19+
AllocateDevelopmentFundCouponResponse,
1920
GetBuyTrafficRequestStatusResponse,
2021
GetTransferOfferStatusResponse,
2122
TransferInstructionResultResponse,
@@ -600,6 +601,34 @@ abstract class WalletAppReference(
600601
httpCommand(HttpWalletAppClient.TokenStandard.RejectAllocationRequest(id))
601602
}
602603
}
604+
605+
@Help.Summary("Allocate a DevelopmentFundCoupon")
606+
@Help.Description(
607+
"Allocates development-fund resources by consuming UnclaimedDevelopmentFundCoupons and creating a new " +
608+
"DevelopmentFundCoupon for the specified beneficiary and amount."
609+
)
610+
def allocateDevelopmentFundCoupon(
611+
unclaimedDevelopmentFundCouponContractIds: Seq[
612+
amuletCodegen.UnclaimedDevelopmentFundCoupon.ContractId
613+
],
614+
beneficiary: PartyId,
615+
amount: BigDecimal,
616+
expiresAt: CantonTimestamp,
617+
reason: String,
618+
fundManager: PartyId,
619+
): AllocateDevelopmentFundCouponResponse =
620+
consoleEnvironment.run {
621+
httpCommand(
622+
HttpWalletAppClient.AllocateDevelopmentFundCouponRequest(
623+
unclaimedDevelopmentFundCouponContractIds,
624+
beneficiary,
625+
amount,
626+
expiresAt,
627+
reason,
628+
fundManager,
629+
)
630+
)
631+
}
603632
}
604633

605634
/** Client (aka remote) reference to a wallet app in the style of ParticipantClientReference, i.e.,

apps/wallet/src/main/openapi/wallet-internal.yaml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,31 @@ paths:
871871
"500":
872872
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/500"
873873

874+
/v0/wallet/development-fund-coupons/allocate:
875+
post:
876+
tags: [ wallet ]
877+
x-jvm-package: wallet
878+
operationId: "allocateDevelopmentFundCoupon"
879+
requestBody:
880+
required: true
881+
content:
882+
application/json:
883+
schema:
884+
"$ref": "#/components/schemas/AllocateDevelopmentFundCouponRequest"
885+
responses:
886+
"200":
887+
description: ok
888+
content:
889+
application/json:
890+
schema:
891+
"$ref": "#/components/schemas/AllocateDevelopmentFundCouponResponse"
892+
"400":
893+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/400"
894+
"404":
895+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/404"
896+
"500":
897+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/500"
898+
874899

875900
components:
876901
schemas:
@@ -1612,3 +1637,39 @@ components:
16121637
type: object
16131638
additionalProperties:
16141639
type: string
1640+
1641+
AllocateDevelopmentFundCouponRequest:
1642+
type: object
1643+
required:
1644+
- unclaimed_development_fund_coupon_contract_ids
1645+
- beneficiary
1646+
- amount
1647+
- expiresAt
1648+
- reason
1649+
- fundManager
1650+
properties:
1651+
unclaimed_development_fund_coupon_contract_ids:
1652+
type: array
1653+
items:
1654+
type: string
1655+
beneficiary:
1656+
type: string
1657+
amount:
1658+
type: string
1659+
expiresAt:
1660+
type: integer
1661+
format: int64
1662+
reason:
1663+
type: string
1664+
fundManager:
1665+
type: string
1666+
1667+
AllocateDevelopmentFundCouponResponse:
1668+
type: object
1669+
required:
1670+
- development_fund_coupon_contract_id
1671+
properties:
1672+
development_fund_coupon_contract_id:
1673+
type: string
1674+
unclaimed_development_fund_coupon_contract_id:
1675+
type: string

apps/wallet/src/main/scala/org/lfdecentralizedtrust/splice/wallet/admin/api/client/commands/HttpWalletAppClient.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,4 +1351,44 @@ object HttpWalletAppClient {
13511351
}
13521352
}
13531353

1354+
final case class AllocateDevelopmentFundCouponRequest(
1355+
unclaimedDevelopmentFundCouponContractIds: Seq[
1356+
amuletCodegen.UnclaimedDevelopmentFundCoupon.ContractId
1357+
],
1358+
beneficiary: PartyId,
1359+
amount: BigDecimal,
1360+
expiresAt: CantonTimestamp,
1361+
reason: String,
1362+
fundManager: PartyId,
1363+
) extends InternalBaseCommand[
1364+
http.AllocateDevelopmentFundCouponResponse,
1365+
definitions.AllocateDevelopmentFundCouponResponse,
1366+
] {
1367+
override def submitRequest(
1368+
client: WalletClient,
1369+
headers: List[HttpHeader],
1370+
): EitherT[Future, Either[
1371+
Throwable,
1372+
HttpResponse,
1373+
], http.AllocateDevelopmentFundCouponResponse] = client.allocateDevelopmentFundCoupon(
1374+
body = definitions.AllocateDevelopmentFundCouponRequest(
1375+
unclaimedDevelopmentFundCouponContractIds.map(_.contractId).toVector,
1376+
Codec.encode(beneficiary),
1377+
Codec.encode(amount),
1378+
Codec.encode(expiresAt),
1379+
reason,
1380+
Codec.encode(fundManager),
1381+
)
1382+
)
1383+
1384+
override protected def handleOk()(implicit
1385+
decoder: TemplateJsonDecoder
1386+
): PartialFunction[http.AllocateDevelopmentFundCouponResponse, Either[
1387+
String,
1388+
definitions.AllocateDevelopmentFundCouponResponse,
1389+
]] = { case http.AllocateDevelopmentFundCouponResponse.OK(value) =>
1390+
Right(value)
1391+
}
1392+
}
1393+
13541394
}

apps/wallet/src/main/scala/org/lfdecentralizedtrust/splice/wallet/admin/http/HttpWalletHandler.scala

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import org.apache.pekko.stream.Materializer
77
import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet as amuletCodegen
88
import org.lfdecentralizedtrust.splice.codegen.java.splice.amuletallocation as amuletAllocationCodegen
99
import org.lfdecentralizedtrust.splice.codegen.java.splice.validatorlicense as validatorLicenseCodegen
10-
import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet.{Amulet, LockedAmulet}
10+
import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet.{
11+
Amulet,
12+
LockedAmulet,
13+
UnclaimedDevelopmentFundCoupon,
14+
}
1115
import org.lfdecentralizedtrust.splice.codegen.java.splice.wallet.install.amuletoperationoutcome.COO_AcceptedAppPayment
1216
import org.lfdecentralizedtrust.splice.codegen.java.splice.wallet.install.{
1317
AmuletOperationOutcome,
@@ -77,8 +81,12 @@ import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.transferins
7781
}
7882
import org.lfdecentralizedtrust.splice.http.v0.definitions.{
7983
AllocateAmuletRequest,
84+
AllocateDevelopmentFundCouponRequest,
85+
AllocateDevelopmentFundCouponResponse,
8086
CreateTokenStandardTransferRequest,
8187
}
88+
import org.lfdecentralizedtrust.splice.util.SpliceUtil.damlDecimal
89+
8290
import org.lfdecentralizedtrust.splice.wallet.admin.http.UserWalletAuthExtractor.WalletUserRequest
8391

8492
import java.math.RoundingMode as JRM
@@ -1278,4 +1286,80 @@ class HttpWalletHandler(
12781286
)
12791287
}
12801288
}
1289+
1290+
override def allocateDevelopmentFundCoupon(
1291+
respond: WalletResource.AllocateDevelopmentFundCouponResponse.type
1292+
)(body: AllocateDevelopmentFundCouponRequest)(
1293+
extracted: WalletUserRequest
1294+
): Future[WalletResource.AllocateDevelopmentFundCouponResponse] = {
1295+
implicit val WalletUserRequest(user, userWallet, traceContext) = extracted
1296+
withSpan(s"$workflowId.rejectAllocationRequest") { implicit traceContext => _ =>
1297+
val store = userWallet.store
1298+
for {
1299+
domain <- scanConnection.getAmuletRulesDomain()(traceContext)
1300+
amuletRules <- scanConnection.getAmuletRulesWithState()
1301+
amuletRulesCt = amuletRules.toAssignedContract.getOrElse(
1302+
throw Status.Code.FAILED_PRECONDITION.toStatus
1303+
.withDescription(
1304+
s"AmuletRules contract is not assigned to a domain."
1305+
)
1306+
.asRuntimeException()
1307+
)
1308+
beneficiary = Codec.tryDecode(Codec.Party)(body.beneficiary)
1309+
fundManagerFromRequest = Codec.tryDecode(Codec.Party)(body.fundManager)
1310+
amount = Codec.tryDecode(Codec.BigDecimal)(body.amount)
1311+
expiresAt = Codec.tryDecode(Codec.Timestamp)(body.expiresAt)
1312+
unclaimedDevelopmentFundCouponContractIds = body.unclaimedDevelopmentFundCouponContractIds
1313+
.map(
1314+
Codec.tryDecodeJavaContractId(UnclaimedDevelopmentFundCoupon.COMPANION)(_)
1315+
)
1316+
optDevelopmentFundManager =
1317+
amuletRulesCt.contract.payload.configSchedule.initialValue.optDevelopmentFundManager
1318+
.map(Codec.tryDecode(Codec.Party)(_))
1319+
.toScala
1320+
// validate development fund manager
1321+
_ =
1322+
optDevelopmentFundManager match {
1323+
case None =>
1324+
throw Status.FAILED_PRECONDITION
1325+
.withDescription("Development fund manager has not been configured.")
1326+
.asRuntimeException()
1327+
case Some(developmentFundManager)
1328+
if developmentFundManager == fundManagerFromRequest &&
1329+
fundManagerFromRequest == store.key.endUserParty =>
1330+
()
1331+
case Some(_) =>
1332+
throw Status.PERMISSION_DENIED
1333+
.withDescription("Invalid fundManager")
1334+
.asRuntimeException()
1335+
}
1336+
cmd = amuletRulesCt
1337+
.exercise(
1338+
_.exerciseAmuletRules_AllocateDevelopmentFundCoupon(
1339+
unclaimedDevelopmentFundCouponContractIds.asJava,
1340+
beneficiary.toProtoPrimitive,
1341+
damlDecimal(amount),
1342+
expiresAt.toInstant,
1343+
fundManagerFromRequest.toProtoPrimitive,
1344+
body.reason,
1345+
)
1346+
)
1347+
result <- userWallet.connection
1348+
.submit(
1349+
Seq(store.key.validatorParty, store.key.endUserParty),
1350+
Seq.empty,
1351+
cmd,
1352+
)
1353+
.withSynchronizerId(domain)
1354+
.noDedup
1355+
.withDisclosedContracts(userWallet.connection.disclosedContracts(amuletRules))
1356+
.yieldResult()
1357+
} yield WalletResource.AllocateDevelopmentFundCouponResponseOK(
1358+
AllocateDevelopmentFundCouponResponse(
1359+
result.exerciseResult.developmentFundCouponCid.contractId,
1360+
result.exerciseResult.optUnclaimedDevelopmentFundCouponCid.toScala.map(_.contractId),
1361+
)
1362+
)
1363+
}
1364+
}
12811365
}

0 commit comments

Comments
 (0)