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
15 changes: 11 additions & 4 deletions ccip-sdk/src/canton/explicit-disclosures/eds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ export interface EdsExecutor {
export interface EdsTokenTransfer {
token: EdsInstrumentId
amount: string
holdingContractIds: string[]
}

/** CCIP message shape accepted by the global and external EDS endpoints. */
export interface EdsMessage {
destinationChainSelector: string
sender: string
receiver: string
payload: string
tokenTransfer: EdsTokenTransfer | null
Expand Down Expand Up @@ -306,13 +308,16 @@ export class EdsDisclosureProvider {
}

/** Fetch global execute disclosures for an encoded CCIP message. */
async fetchExecutionDisclosures(encodedMessage: string): Promise<EdsExecuteResult> {
async fetchExecutionDisclosures(
encodedMessage: string,
receiver: string,
): Promise<EdsExecuteResult> {
const resp = await post<EdsGlobalExecuteResponse>(
this.edsBaseUrl,
'/ccip/v1/global/message/execute',
EDS_HEADERS,
this.timeoutMs,
{ encodedMessage },
{ encodedMessage, receiver },
)
return {
contextData: contextDataOrEmpty(resp.contextData),
Expand All @@ -325,13 +330,14 @@ export class EdsDisclosureProvider {
async fetchTokenPoolExecuteDisclosure(
address: string,
encodedMessage: string,
receiver: string,
): Promise<EdsTokenPoolDisclosureResult> {
const resp = await post<EdsTokenPoolDisclosureResponse>(
this.externalBaseUrlFor(address),
`/ccip/v1/external/tokenPool/${encodeURIComponent(address)}/execute`,
EDS_HEADERS,
this.timeoutMs,
{ encodedMessage },
{ encodedMessage, receiver },
)
return this.rawTokenPoolResult(resp)
}
Expand All @@ -340,13 +346,14 @@ export class EdsDisclosureProvider {
async fetchCcvExecuteDisclosure(
address: string,
encodedMessage: string,
receiver: string,
): Promise<EdsExternalDisclosureResult> {
const resp = await post<EdsExternalDisclosureResponse>(
this.externalBaseUrlFor(address),
`/ccip/v1/external/ccv/${encodeURIComponent(address)}/execute`,
EDS_HEADERS,
this.timeoutMs,
{ encodedMessage },
{ encodedMessage, receiver },
)
return this.rawExternalResult(resp)
}
Expand Down
50 changes: 34 additions & 16 deletions ccip-sdk/src/canton/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,9 +721,18 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {

const tokenInstrument = parseCantonInstrumentId(tokenAmount.token)
const tokenAmountDecimal = formatCantonDecimalAmountUnits(tokenAmount.amount)

const tokenHoldings = await this.resolveTokenTransferHoldings({
party: sender,
instrumentId: tokenInstrument,
explicitHoldingCids: cantonArgs.tokenTransferHoldingCids,
feeTokenHoldingCids,
requiredAmount: tokenAmount.amount,
})
messageTokenTransfer = {
token: { admin: tokenInstrument.admin, id: tokenInstrument.id },
amount: tokenAmountDecimal,
holdingContractIds: tokenHoldings.map((holding) => holding.contractId),
}

const tokenPoolAddress = await this.edsDisclosureProvider.lookupTokenPool(
Expand All @@ -747,25 +756,17 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {

const tokenPoolEdsMessage = buildEdsMessage({
destChainSelector,
senderParty: sender,
receiverHex,
payloadHex,
feeInstrument,
tokenTransfer: messageTokenTransfer,
})

const [tokenHoldings, tokenPoolSend] = await Promise.all([
this.resolveTokenTransferHoldings({
party: sender,
instrumentId: tokenInstrument,
explicitHoldingCids: cantonArgs.tokenTransferHoldingCids,
feeTokenHoldingCids,
requiredAmount: tokenAmount.amount,
}),
this.edsDisclosureProvider.fetchTokenPoolSendDisclosure(
tokenPoolAddress,
tokenPoolEdsMessage,
),
])
const tokenPoolSend = await this.edsDisclosureProvider.fetchTokenPoolSendDisclosure(
tokenPoolAddress,
tokenPoolEdsMessage,
)

tokenPoolRequiredCCVs = tokenPoolSend.requiredCCVs
tokenTransferInput = {
Expand All @@ -781,6 +782,7 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {

const edsMessage = buildEdsMessage({
destChainSelector,
senderParty: sender,
receiverHex,
payloadHex,
feeInstrument,
Expand Down Expand Up @@ -853,7 +855,12 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
message: {
receiver: receiverHex,
payload: payloadHex,
tokenTransfer: messageTokenTransfer,
tokenTransfer: messageTokenTransfer
? {
token: messageTokenTransfer.token,
amount: messageTokenTransfer.amount,
}
: null,
feeToken: { admin: feeInstrument.admin, id: feeInstrument.id },
extraArgs: {
tag: 'V3',
Expand Down Expand Up @@ -1061,7 +1068,10 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
const encodedMessageHex = stripHexPrefix(String(encodedMessage))

this.logger.debug('CantonChain.generateUnsignedExecute: fetching global EDS execute data...')
const edsResult = await this.edsDisclosureProvider.fetchExecutionDisclosures(encodedMessageHex)
const edsResult = await this.edsDisclosureProvider.fetchExecutionDisclosures(
encodedMessageHex,
payer,
)
// Step 2 — Fetch same-party disclosures (PerPartyRouter + CCIPReceiver)
// TODO: This should include receiverCid when provided. We need to figure out how to get that from the input or opts.
this.logger.debug(
Expand All @@ -1079,7 +1089,11 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
ccvAddress,
verifierDestAddress: v.destAddress,
})
return this.edsDisclosureProvider.fetchCcvExecuteDisclosure(ccvAddress, encodedMessageHex)
return this.edsDisclosureProvider.fetchCcvExecuteDisclosure(
ccvAddress,
encodedMessageHex,
payer,
)
}),
)

Expand All @@ -1101,6 +1115,7 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
const tokenPoolExecute = await this.edsDisclosureProvider.fetchTokenPoolExecuteDisclosure(
edsResult.tokenPool,
encodedMessageHex,
payer,
)
assertRequiredCcvsCovered(
tokenPoolExecute.requiredCCVs,
Expand Down Expand Up @@ -2361,19 +2376,22 @@ function formatInstrumentId(instrumentId: CantonInstrumentId): string {

function buildEdsMessage({
destChainSelector,
senderParty,
receiverHex,
payloadHex,
feeInstrument,
tokenTransfer,
}: {
destChainSelector: bigint
senderParty: string
receiverHex: string
payloadHex: string
feeInstrument: CantonInstrumentId
tokenTransfer: Record<string, unknown> | null
}): EdsMessage {
return {
destinationChainSelector: destChainSelector.toString(),
sender: senderParty,
receiver: receiverHex,
payload: payloadHex,
tokenTransfer: tokenTransfer as EdsMessage['tokenTransfer'],
Expand Down
Loading