@@ -36,16 +36,21 @@ import {
3636 CosmosChainAddressShape ,
3737} from '@agoric/orchestration' ;
3838import type { AccountId } from '@agoric/orchestration/src/orchestration-api.js' ;
39- import { parseAccountIdArg } from '@agoric/orchestration/src/utils/address.js' ;
39+ import {
40+ chainOfAccount ,
41+ parseAccountId ,
42+ parseAccountIdArg ,
43+ } from '@agoric/orchestration/src/utils/address.js' ;
4044import type { ZoeTools } from '@agoric/orchestration/src/utils/zoe-tools.js' ;
45+ import { M , mustMatch } from '@agoric/store' ;
4146import { pickFacet } from '@agoric/vat-data' ;
4247import type { VowTools } from '@agoric/vow' ;
4348import { VowShape } from '@agoric/vow' ;
4449import type { ZCF , ZCFSeat } from '@agoric/zoe/src/zoeService/zoe.js' ;
4550import type { Zone } from '@agoric/zone' ;
4651import { Fail , q } from '@endo/errors' ;
4752import { E } from '@endo/far' ;
48- import { M , mustMatch } from '@agoric/store ' ;
53+ import { makeSupportsCctp } from '../utils/cctp.ts ' ;
4954import type { LiquidityPoolKit } from './liquidity-pool.js' ;
5055import type { SettlerKit } from './settler.js' ;
5156import type { StatusManager } from './status-manager.js' ;
@@ -134,7 +139,7 @@ export const prepareAdvancerKit = (
134139 log = makeTracer ( 'Advancer' , true ) ,
135140 statusManager,
136141 usdc,
137- vowTools : { watch, when } ,
142+ vowTools : { asVow , watch, when } ,
138143 zcf,
139144 zoeTools : { localTransfer, withdrawToSeat } ,
140145 } : AdvancerKitPowers ,
@@ -149,6 +154,8 @@ export const prepareAdvancerKit = (
149154 const feeTools = makeFeeTools ( feeConfig ) ;
150155 const toAmount = ( value : bigint ) => AmountMath . make ( usdc . brand , value ) ;
151156
157+ const supportsCctp = makeSupportsCctp ( chainHub ) ;
158+
152159 return zone . exoClassKit (
153160 'Fast USDC Advancer' ,
154161 AdvancerKitI ,
@@ -178,8 +185,7 @@ export const prepareAdvancerKit = (
178185 *
179186 * @param {EvidenceWithRisk } evidenceWithRisk
180187 */
181- async handleTransactionEvent ( { evidence, risk } : EvidenceWithRisk ) {
182- await null ;
188+ handleTransactionEvent ( { evidence, risk } : EvidenceWithRisk ) {
183189 try {
184190 if ( statusManager . hasBeenObserved ( evidence ) ) {
185191 log ( 'txHash already seen:' , evidence . txHash ) ;
@@ -200,8 +206,20 @@ export const prepareAdvancerKit = (
200206 }
201207 const { EUD } = decoded . query ;
202208 log ( `decoded EUD: ${ EUD } ` ) ;
203- // throws if the bech32 prefix is not found
209+
210+ // throws if it's neither CAIP-10 nor bare bech32.
204211 const destination = chainHub . resolveAccountId ( EUD ) ;
212+ const accountId = parseAccountId ( destination ) ;
213+
214+ // Dest must be a Cosmos account or support CCTP
215+ if (
216+ ! ( accountId . namespace === 'cosmos' || supportsCctp ( destination ) )
217+ ) {
218+ const destChain = chainOfAccount ( destination ) ;
219+ statusManager . skipAdvance ( evidence , [
220+ `Transfer to ${ destChain } not supported.` ,
221+ ] ) ;
222+ }
205223
206224 const fullAmount = toAmount ( evidence . tx . amount ) ;
207225 const { borrower, notifier, poolAccount } = this . state ;
@@ -258,28 +276,56 @@ export const prepareAdvancerKit = (
258276 result : undefined ,
259277 ctx : AdvancerVowCtx & { tmpSeat : ZCFSeat } ,
260278 ) {
261- const { poolAccount, settlementAddress } = this . state ;
262- const { destination, advanceAmount, tmpSeat, ...detail } = ctx ;
263- tmpSeat . exit ( ) ;
264- const amount = harden ( {
265- denom : usdc . denom ,
266- value : advanceAmount . value ,
267- } ) ;
268- const intermediateRecipient = getNobleICA ( ) . getAddress ( ) ;
269- const accountId = parseAccountIdArg ( destination ) ;
279+ return asVow ( async ( ) => {
280+ const { poolAccount, settlementAddress } = this . state ;
281+ const { tmpSeat, ...vowContext } = ctx ;
282+ const { destination, advanceAmount, ...detail } = vowContext ;
283+ tmpSeat . exit ( ) ;
284+ const amount = harden ( {
285+ denom : usdc . denom ,
286+ value : advanceAmount . value ,
287+ } ) ;
288+ const accountId = parseAccountIdArg ( destination ) ;
289+ await null ;
270290
271- assert . equal ( accountId . namespace , 'cosmos' ) ;
291+ const intermediaryAccount = getNobleICA ( ) ;
292+ const intermediaryAddress = intermediaryAccount . getAddress ( ) ;
272293
273- const transferOrSendV =
274- accountId . reference === settlementAddress . chainId
275- ? E ( poolAccount ) . send ( destination , amount )
276- : E ( poolAccount ) . transfer ( destination , amount , {
277- forwardOpts : { intermediateRecipient } ,
278- } ) ;
279- return watch ( transferOrSendV , this . facets . transferHandler , {
280- destination,
281- advanceAmount,
282- ...detail ,
294+ let transferOrSendV ;
295+ if (
296+ accountId . namespace === 'cosmos' &&
297+ accountId . reference === settlementAddress . chainId
298+ ) {
299+ // send to recipient on Agoric
300+ transferOrSendV = E ( poolAccount ) . send ( destination , amount ) ;
301+ } else if ( accountId . namespace === 'cosmos' ) {
302+ // send via IBC
303+
304+ transferOrSendV = E ( poolAccount ) . transfer ( destination , amount , {
305+ forwardOpts : {
306+ intermediateRecipient : intermediaryAddress ,
307+ } ,
308+ } ) ;
309+ } else if ( supportsCctp ( destination ) ) {
310+ // send USDC via CCTP
311+
312+ await E ( poolAccount ) . transfer ( intermediaryAddress , amount ) ;
313+ // assets are on noble, transfer to dest.
314+
315+ transferOrSendV = intermediaryAccount . depositForBurn (
316+ destination ,
317+ amount ,
318+ ) ;
319+ } else {
320+ // This is supposed to be caught in handleTransactionEvent()
321+ Fail `🚨 can only transfer to Agoric addresses, via IBC, or via CCTP` ;
322+ }
323+
324+ return watch (
325+ transferOrSendV ,
326+ this . facets . transferHandler ,
327+ vowContext ,
328+ ) ;
283329 } ) ;
284330 } ,
285331 /**
0 commit comments