Skip to content

Commit 69740d2

Browse files
committed
chore: capture ForwardFailed state
- if Forward (slow transfer) fails, capture the state to distinguish from successful forwards
1 parent 3c4278d commit 69740d2

File tree

3 files changed

+83
-11
lines changed

3 files changed

+83
-11
lines changed

packages/fast-usdc/src/exos/settler.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,7 @@ export const prepareSettler = (
272272
*/
273273
forward(txHash, fullValue, EUD) {
274274
const { settlementAccount, intermediateRecipient } = this.state;
275-
276275
const dest = chainHub.makeChainAddress(EUD);
277-
278-
// TODO? statusManager.forwarding(txHash, sender, amount);
279276
const txfrV = E(settlementAccount).transfer(
280277
dest,
281278
AmountMath.make(USDC, fullValue),
@@ -290,15 +287,18 @@ export const prepareSettler = (
290287
* @param {EvmHash} txHash
291288
*/
292289
onFulfilled(_result, txHash) {
293-
statusManager.forwarded(txHash);
290+
// update status manager, marking tx `FORWARDED` without fee split
291+
statusManager.forwarded(txHash, true);
294292
},
295293
/**
296294
* @param {unknown} reason
297295
* @param {EvmHash} txHash
298296
*/
299297
onRejected(reason, txHash) {
300-
log('⚠️ transfer rejected!', reason, txHash);
301-
// TODO(#10510): statusManager.forwardFailed(txHash, nfa, amount);
298+
log('⚠️ forward transfer rejected!', reason, txHash);
299+
// update status manager, flagging a terminal state that needs to be
300+
// manual intervention or a code update to remediate
301+
statusManager.forwarded(txHash, false);
302302
},
303303
},
304304
},

packages/fast-usdc/src/exos/status-manager.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ export const prepareStatusManager = (
216216
disbursed: M.call(EvmHashShape, AmountKeywordRecordShape).returns(
217217
M.undefined(),
218218
),
219-
forwarded: M.call(EvmHashShape).returns(M.undefined()),
219+
forwarded: M.call(EvmHashShape, M.boolean()).returns(M.undefined()),
220220
lookupPending: M.call(M.string(), M.bigint()).returns(
221221
M.arrayOf(PendingTxShape),
222222
),
@@ -340,12 +340,18 @@ export const prepareStatusManager = (
340340
},
341341

342342
/**
343-
* Mark a transaction as `FORWARDED`
343+
* Mark a transaction as `FORWARDED` or `FORWARD_FAILED`
344344
*
345-
* @param {EvmHash} txHash - undefined in case mint before observed
345+
* @param {EvmHash} txHash
346+
* @param {boolean} success
346347
*/
347-
forwarded(txHash) {
348-
void publishTxnRecord(txHash, harden({ status: TxStatus.Forwarded }));
348+
forwarded(txHash, success) {
349+
void publishTxnRecord(
350+
txHash,
351+
harden({
352+
status: success ? TxStatus.Forwarded : TxStatus.ForwardFailed,
353+
}),
354+
);
349355
},
350356

351357
/**

packages/fast-usdc/test/exos/settler.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,3 +581,69 @@ test('Settlement for Advancing transaction (advance fails)', async t => {
581581
{ status: 'FORWARDED' },
582582
]);
583583
});
584+
585+
test('slow path, and forward fails (terminal state)', async t => {
586+
const {
587+
common,
588+
makeSettler,
589+
statusManager,
590+
defaultSettlerParams,
591+
repayer,
592+
makeSimulate,
593+
accounts,
594+
peekCalls,
595+
storage,
596+
} = t.context;
597+
const { usdc } = common.brands;
598+
599+
const settler = makeSettler({
600+
repayer,
601+
settlementAccount: accounts.settlement.account,
602+
...defaultSettlerParams,
603+
});
604+
const simulate = makeSimulate(settler.notify);
605+
const cctpTxEvidence = simulate.observe();
606+
t.deepEqual(
607+
statusManager.lookupPending(
608+
cctpTxEvidence.tx.forwardingAddress,
609+
cctpTxEvidence.tx.amount,
610+
),
611+
[{ ...cctpTxEvidence, status: PendingTxStatus.Observed }],
612+
'statusManager shows this tx is only observed',
613+
);
614+
615+
t.log('Simulate incoming IBC settlement');
616+
void settler.tap.receiveUpcall(MockVTransferEvents.AGORIC_PLUS_OSMO());
617+
await eventLoopIteration();
618+
619+
t.log('funds are forwarded; no interaction with LP');
620+
t.like(accounts.settlement.callLog, [
621+
[
622+
'transfer',
623+
{
624+
value: 'osmo183dejcnmkka5dzcu9xw6mywq0p2m5peks28men',
625+
},
626+
usdc.units(150),
627+
{
628+
forwardOpts: {
629+
intermediateRecipient: {
630+
value: 'noble1test',
631+
},
632+
},
633+
},
634+
],
635+
]);
636+
637+
t.log('simulating forward failure (e.g. unknown route)');
638+
const mockE = Error('no connection info found');
639+
accounts.settlement.transferVResolver.reject(mockE);
640+
await eventLoopIteration();
641+
t.deepEqual(storage.getDeserialized(`fun.txns.${cctpTxEvidence.txHash}`), [
642+
{ evidence: cctpTxEvidence, status: 'OBSERVED' },
643+
{ status: 'FORWARD_FAILED' },
644+
]);
645+
});
646+
647+
test.todo('creator facet methods');
648+
649+
test.todo('ignored packets');

0 commit comments

Comments
 (0)