Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 161 additions & 9 deletions core/ledger-client/src/token-standard-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
allocationInstructionRegistryTypes,
ExtraArgs,
Metadata,
FEATURED_APP_DELEGATE_PROXY_INTERFACE_ID,
Beneficiaries,
} from '@canton-network/core-token-standard'
import { Logger, PartyId } from '@canton-network/core-types'
import { LedgerClient } from './ledger-client.js'
Expand Down Expand Up @@ -837,6 +839,114 @@ class TransferService {
}
}

async exerciseDelegateProxyTransferInstructionAccept(
proxyCid: string,
transferInstructionCid: string,
registryUrl: URL,
featuredAppRightCid: string,
beneficiaries: Beneficiaries[]
): Promise<[ExerciseCommand, DisclosedContract[]]> {
const [acceptTransferInstructionContext, disclosedContracts] =
await this.createAcceptTransferInstruction(
transferInstructionCid,
registryUrl.href
)

const choiceArgs = {
cid: acceptTransferInstructionContext.contractId,
proxyArg: {
featuredAppRightCid: featuredAppRightCid,
beneficiaries: beneficiaries,
choiceArg: acceptTransferInstructionContext.choiceArgument,
},
}

return [
{
templateId: FEATURED_APP_DELEGATE_PROXY_INTERFACE_ID,
contractId: proxyCid,
choice: 'DelegateProxy_TransferInstruction_Accept',
choiceArgument: choiceArgs,
},
disclosedContracts,
]
}

async exerciseDelegateProxyTransferInstructionReject(
proxyCid: string,
transferInstructionCid: string,
registryUrl: URL,
featuredAppRightCid: string,
beneficiaries: Beneficiaries[]
): Promise<[ExerciseCommand, DisclosedContract[]]> {
const [rejectTransferInstructionContext, disclosedContracts] =
await this.createRejectTransferInstruction(
transferInstructionCid,
registryUrl.href
)

const choiceArgs = {
cid: rejectTransferInstructionContext.contractId,
proxyArg: {
featuredAppRightCid: featuredAppRightCid,
beneficiaries,
choiceArg: rejectTransferInstructionContext.choiceArgument,
},
}

return [
{
templateId: FEATURED_APP_DELEGATE_PROXY_INTERFACE_ID,
contractId: proxyCid,
choice: 'DelegateProxy_TransferInstruction_Reject',
choiceArgument: choiceArgs,
},
disclosedContracts,
]
}

async exerciseDelegateProxyTransferInstructioWithdraw(
proxyCid: string,
transferInstructionCid: string,
registryUrl: URL,
featuredAppRightCid: string,
beneficiaries: Beneficiaries[]
): Promise<[ExerciseCommand, DisclosedContract[]]> {
const [withdrawTransferInstructionContext, disclosedContracts] =
await this.createWithdrawTransferInstruction(
transferInstructionCid,
registryUrl.href
)

const sumOfWeights: number = beneficiaries.reduce(
(totalWeight, beneficiary) => totalWeight + beneficiary.weight,
0
)

if (sumOfWeights > 1.0) {
throw new Error('Sum of beneficiary weights is larger than 1.')
}

const choiceArgs = {
cid: withdrawTransferInstructionContext.contractId,
proxyArg: {
featuredAppRightCid: featuredAppRightCid,
beneficiaries,
choiceArg: withdrawTransferInstructionContext.choiceArgument,
},
}

return [
{
templateId: FEATURED_APP_DELEGATE_PROXY_INTERFACE_ID,
contractId: proxyCid,
choice: 'DelegateProxy_TransferInstruction_Withdraw',
choiceArgument: choiceArgs,
},
disclosedContracts,
]
}

async createAcceptTransferInstruction(
transferInstructionCid: string,
registryUrl: string,
Expand Down Expand Up @@ -1198,13 +1308,13 @@ export class TokenStandardService {
async createDelegateProxyTranfser(
sender: PartyId,
receiver: PartyId,
exchangeParty: PartyId,
amount: string,
instrumentAdmin: PartyId, // TODO (#907): replace with registry call
instrumentId: string,
registryUrl: string,
featuredAppRightCid: string,
proxyCid: string,
beneficiaries: Beneficiaries[],
inputUtxos?: string[],
memo?: string,
expiryDate?: Date,
Expand All @@ -1224,23 +1334,26 @@ export class TokenStandardService {
meta
)

const sumOfWeights: number = beneficiaries.reduce(
(totalWeight, beneficiary) => totalWeight + beneficiary.weight,
0
)

if (sumOfWeights > 1.0) {
throw new Error('Sum of beneficiary weights is larger than 1.')
}

const choiceArgs = {
cid: transferCommand.contractId,
proxyArg: {
featuredAppRightCid: featuredAppRightCid,
beneficiaries: [
{
beneficiary: exchangeParty,
weight: 1.0,
},
],
beneficiaries,
choiceArg: transferCommand.choiceArgument,
},
}

const exercise: ExerciseCommand = {
templateId:
'#splice-util-featured-app-proxies:Splice.Util.FeaturedApp.DelegateProxy:DelegateProxy',
templateId: FEATURED_APP_DELEGATE_PROXY_INTERFACE_ID,
contractId: proxyCid,
choice: 'DelegateProxy_TransferFactory_Transfer',
choiceArgument: choiceArgs,
Expand Down Expand Up @@ -1426,4 +1539,43 @@ export class TokenStandardService {

return [exercise, disclosed]
}

async exerciseDelegateProxyTransferInstructionAccept(
exchangeParty: PartyId,
proxyCid: string,
transferInstructionCid: string,
registryUrl: string,
featuredAppRightCid: string
): Promise<[ExerciseCommand, DisclosedContract[]]> {
const [acceptTransferInstructionContext, disclosedContracts] =
await this.transfer.createAcceptTransferInstruction(
transferInstructionCid,
registryUrl
)

const choiceArgs = {
cid: acceptTransferInstructionContext.contractId,
proxyArg: {
featuredAppRightCid: featuredAppRightCid,
beneficiaries: [
{
beneficiary: exchangeParty,
weight: 1.0,
},
],
choiceArg: acceptTransferInstructionContext.choiceArgument,
},
}

return [
{
templateId:
'#splice-util-featured-app-proxies:Splice.Util.FeaturedApp.DelegateProxy:DelegateProxy',
contractId: proxyCid,
choice: 'DelegateProxy_TransferInstruction_Accept',
choiceArgument: choiceArgs,
},
disclosedContracts,
]
}
}
2 changes: 2 additions & 0 deletions core/token-standard/src/interface-ids.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export const TRANSFER_FACTORY_INTERFACE_ID =
'#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory'
export const TRANSFER_INSTRUCTION_INTERFACE_ID =
'#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferInstruction'
export const FEATURED_APP_DELEGATE_PROXY_INTERFACE_ID =
'#splice-util-featured-app-proxies:Splice.Util.FeaturedApp.DelegateProxy:DelegateProxy'
7 changes: 7 additions & 0 deletions core/token-standard/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { PartyId } from '@canton-network/core-types'

// Mirrored types from token standard codegen
// With current rollup setup it's not possible to bundle types from codegen into this package /dist
// TODO(#614) adjust pipeline to bundle types reexported from codegen
Expand Down Expand Up @@ -94,3 +96,8 @@ export type Transfer = {
inputHoldingCids: string[]
meta: Metadata
}

export type Beneficiaries = {
beneficiary: PartyId
weight: number
}
1 change: 1 addition & 0 deletions docs/wallet-integration-guide/examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"run-12": "tsx ./scripts/12-integration-extensions.ts | pino-pretty",
"run-13": "tsx ./scripts/13-auth-tls.ts | pino-pretty",
"run-14": "tsx ./scripts/14-token-standard-preapproval-renew-cancel-localnet | pino-pretty",
"run-15": "tsx ./scripts/15-rewards-for-deposits.ts | pino-pretty",
"run-exch-01": "tsx ./scripts/exchange-integration/01-one-step-deposit.ts | pino-pretty",
"run-exch-02": "tsx ./scripts/exchange-integration/02-one-step-withdrawal.ts | pino-pretty",
"run-exch-03": "tsx ./scripts/exchange-integration/03-multi-step-deposit.ts | pino-pretty",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,23 @@ logger.info(featuredAppRights, 'featured app rights')

await sdk.setPartyId(treasuryParty?.partyId!)

const beneficiaries = [
{
beneficiary: exchangeParty!,
weight: 1.0,
},
]

const [transferCommand, disclosedContracts2] =
await sdk.tokenStandard!.createTransferUsingDelegateProxy(
exchangeParty!,
proxyCid!,
featuredAppRights?.contract_id!,
treasuryParty?.partyId!,
receiverParty?.partyId!,
'100',
'Amulet',
instrumentAdminPartyId,
beneficiaries,
[],
'memo-ref'
)
Expand Down
Loading
Loading