@@ -28,8 +28,11 @@ import { TransferRouteShape } from './chain-hub.js';
28
28
/**
29
29
* @import {HostOf} from '@agoric/async-flow';
30
30
* @import {LocalChain, LocalChainAccount} from '@agoric/vats/src/localchain.js';
31
- * @import {AmountArg, CosmosChainAddress, DenomAmount, IBCMsgTransferOptions, IBCConnectionInfo, OrchestrationAccountCommon, LocalAccountMethods, TransferRoute, AccountId, AccountIdArg} from '@agoric/orchestration';
32
- * @import {ContractMeta, Invitation, OfferHandler, ZCF, ZCFSeat} from '@agoric/zoe';
31
+ * @import {AmountArg, CosmosChainAddress, DenomAmount, IBCMsgTransferOptions, OrchestrationAccountCommon, LocalAccountMethods, TransferRoute, AccountIdArg, Denom, Bech32Address} from '@agoric/orchestration';
32
+ * @import {OfferHandler, ZCF, ZCFSeat} from '@agoric/zoe';
33
+ * @import {IBCEvent} from '@agoric/vats';
34
+ * @import {QueryDenomHashResponse} from '@agoric/cosmic-proto/ibc/applications/transfer/v1/query.js';
35
+ * @import {FungibleTokenPacketData} from '@agoric/cosmic-proto/ibc/applications/transfer/v2/packet.js';
33
36
* @import {RecorderKit, MakeRecorderKit} from '@agoric/zoe/src/contractSupport/recorder.js'.
34
37
* @import {Zone} from '@agoric/zone';
35
38
* @import {Remote} from '@agoric/internal';
@@ -78,6 +81,14 @@ const HolderI = M.interface('holder', {
78
81
. returns ( EVow$ ( M . string ( ) ) ) ,
79
82
matchFirstPacket : M . call ( M . any ( ) ) . returns ( EVow$ ( M . any ( ) ) ) ,
80
83
monitorTransfers : M . call ( M . remotable ( 'TargetApp' ) ) . returns ( EVow$ ( M . any ( ) ) ) ,
84
+ parseInboundTransfer : M . call ( M . recordOf ( M . string ( ) , M . any ( ) ) ) . returns (
85
+ Vow$ ( {
86
+ amount : DenomAmountShape ,
87
+ fromAccount : M . string ( ) ,
88
+ toAccount : M . string ( ) ,
89
+ extra : M . recordOf ( M . string ( ) , M . any ( ) ) ,
90
+ } ) ,
91
+ ) ,
81
92
} ) ;
82
93
83
94
/** @type {{ [name: string]: [description: string, valueShape: Matcher] } } */
@@ -167,6 +178,10 @@ export const prepareLocalOrchestrationAccountKit = (
167
178
M . arrayOf ( DenomAmountShape ) ,
168
179
) ,
169
180
} ) ,
181
+ parseInboundTransferWatcher : M . interface ( 'parseInboundTransferWatcher' , {
182
+ onFulfilled : M . call ( M . record ( ) , M . record ( ) ) . returns ( M . record ( ) ) ,
183
+ onRejected : M . call ( M . any ( ) , M . record ( ) ) . returns ( M . record ( ) ) ,
184
+ } ) ,
170
185
invitationMakers : M . interface ( 'invitationMakers' , {
171
186
CloseAccount : M . call ( ) . returns ( M . promise ( ) ) ,
172
187
Delegate : M . call ( M . string ( ) , AmountShape ) . returns ( M . promise ( ) ) ,
@@ -481,6 +496,40 @@ export const prepareLocalOrchestrationAccountKit = (
481
496
return harden ( balances . map ( toDenomAmount ) ) ;
482
497
} ,
483
498
} ,
499
+ parseInboundTransferWatcher : {
500
+ /**
501
+ * @param {QueryDenomHashResponse } localDenomHash
502
+ * @param {Awaited<
503
+ * ReturnType<LocalAccountMethods['parseInboundTransfer']>
504
+ * >} naiveResult
505
+ */
506
+ onFulfilled ( localDenomHash , naiveResult ) {
507
+ const localDenom = `ibc/${ localDenomHash . hash } ` ;
508
+ const { amount, ...rest } = naiveResult ;
509
+ const { denom : _ , ...amountRest } = amount ;
510
+ return harden ( {
511
+ ...rest ,
512
+ amount : { ...amountRest , denom : localDenom } ,
513
+ } ) ;
514
+ } ,
515
+ /**
516
+ * @param {unknown } reason
517
+ * @param {Awaited<
518
+ * ReturnType<LocalAccountMethods['parseInboundTransfer']>
519
+ * >} naiveResult
520
+ */
521
+ onRejected ( reason , naiveResult ) {
522
+ if (
523
+ reason instanceof Error &&
524
+ String ( reason . message ) . includes ( 'denomination trace not found' )
525
+ ) {
526
+ // Looks like the trace was not found; The naive result is good enough.
527
+ return naiveResult ;
528
+ }
529
+ // Propagate other errors upwards.
530
+ throw reason ;
531
+ } ,
532
+ } ,
484
533
holder : {
485
534
/** @type {HostOf<OrchestrationAccountCommon['asContinuingOffer']> } */
486
535
asContinuingOffer ( ) {
@@ -716,6 +765,76 @@ export const prepareLocalOrchestrationAccountKit = (
716
765
return resultV ;
717
766
} ) ;
718
767
} ,
768
+ /**
769
+ * @type {HostOf<LocalAccountMethods['parseInboundTransfer']> }
770
+ */
771
+ parseInboundTransfer ( record ) {
772
+ trace ( 'parseInboundTransfer' , record ) ;
773
+ return asVow ( ( ) => {
774
+ const packet =
775
+ /** @type {IBCEvent<'writeAcknowledgement'>['packet'] } */ (
776
+ record
777
+ ) ;
778
+
779
+ /** @type {FungibleTokenPacketData } */
780
+ const ftPacketData = JSON . parse ( atob ( packet . data ) ) ;
781
+ const {
782
+ denom : transferDenom ,
783
+ sender,
784
+ receiver,
785
+ amount,
786
+ } = ftPacketData ;
787
+
788
+ /**
789
+ * @param {string } address
790
+ */
791
+ const resolveBech32Address = address =>
792
+ chainHub . resolveAccountId ( /** @type {Bech32Address } */ ( address ) ) ;
793
+
794
+ /**
795
+ * @param {Denom } localDenom
796
+ */
797
+ const buildReturnValue = localDenom =>
798
+ harden ( {
799
+ amount : /** @type {DenomAmount } */ ( {
800
+ value : BigInt ( amount ) ,
801
+ denom : localDenom ,
802
+ } ) ,
803
+ fromAccount : resolveBech32Address ( sender ) ,
804
+ toAccount : resolveBech32Address ( receiver ) ,
805
+ extra : {
806
+ ...ftPacketData ,
807
+ } ,
808
+ } ) ;
809
+
810
+ /**
811
+ * @type {Denom }
812
+ */
813
+ let denomOrTrace ;
814
+
815
+ const prefix = `${ packet . source_port } /${ packet . source_channel } /` ;
816
+ trace ( { transferDenom, prefix } ) ;
817
+ if ( transferDenom . startsWith ( prefix ) ) {
818
+ // Unwind, which may end up as a local denom.
819
+ denomOrTrace = transferDenom . slice ( prefix . length ) ;
820
+ } else {
821
+ // If the denom is not local, attach its source.
822
+ denomOrTrace = `${ packet . destination_port } /${ packet . destination_channel } /${ transferDenom } ` ;
823
+ }
824
+
825
+ // Find the local denom hash for the transferDenom, if there is one.
826
+ return watch (
827
+ E ( localchain ) . query (
828
+ typedJson (
829
+ '/ibc.applications.transfer.v1.QueryDenomHashRequest' ,
830
+ { trace : denomOrTrace } ,
831
+ ) ,
832
+ ) ,
833
+ this . facets . parseInboundTransferWatcher ,
834
+ buildReturnValue ( denomOrTrace ) ,
835
+ ) ;
836
+ } ) ;
837
+ } ,
719
838
/** @type {HostOf<OrchestrationAccountCommon['transferSteps']> } */
720
839
transferSteps ( amount , msg ) {
721
840
return asVow ( ( ) => {
0 commit comments