Skip to content

Commit

Permalink
Merge branch 'master' into 8606-invite-proposals
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Jan 7, 2025
2 parents f7b444c + 4c2be7c commit cfb3a62
Show file tree
Hide file tree
Showing 12 changed files with 991 additions and 251 deletions.
18 changes: 12 additions & 6 deletions packages/fast-usdc/src/exos/advancer.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ const AdvancerKitI = harden({
onRejected: M.call(M.error(), AdvancerVowCtxShape).returns(),
}),
transferHandler: M.interface('TransferHandlerI', {
// TODO confirm undefined, and not bigint (sequence)
onFulfilled: M.call(M.undefined(), AdvancerVowCtxShape).returns(
M.undefined(),
),
Expand Down Expand Up @@ -152,9 +151,7 @@ export const prepareAdvancerKit = (
statusManager.skipAdvance(evidence, risk.risksIdentified);
return;
}

const { borrowerFacet, poolAccount, settlementAddress } =
this.state;
const { settlementAddress } = this.state;
const { recipientAddress } = evidence.aux;
const decoded = decodeAddressHook(recipientAddress);
mustMatch(decoded, AddressHookShape);
Expand All @@ -167,6 +164,14 @@ export const prepareAdvancerKit = (
const destination = chainHub.makeChainAddress(EUD);

const fullAmount = toAmount(evidence.tx.amount);
const { borrowerFacet, notifyFacet, poolAccount } = this.state;
// do not advance if we've already received a mint/settlement
const mintedEarly = notifyFacet.checkMintedEarly(
evidence,
destination,
);
if (mintedEarly) return;

// throws if requested does not exceed fees
const advanceAmount = feeTools.calculateAdvance(fullAmount);

Expand Down Expand Up @@ -208,7 +213,7 @@ export const prepareAdvancerKit = (
*/
onFulfilled(result, ctx) {
const { poolAccount, intermediateRecipient } = this.state;
const { destination, advanceAmount, ...detail } = ctx;
const { destination, advanceAmount, tmpSeat: _, ...detail } = ctx;
const transferV = E(poolAccount).transfer(
destination,
{ denom: usdc.denom, value: advanceAmount.value },
Expand Down Expand Up @@ -273,7 +278,8 @@ export const prepareAdvancerKit = (
onRejected(error, ctx) {
const { notifyFacet } = this.state;
log('Advance transfer rejected', error);
notifyFacet.notifyAdvancingResult(ctx, false);
const { advanceAmount: _, ...restCtx } = ctx;
notifyFacet.notifyAdvancingResult(restCtx, false);
},
},
},
Expand Down
124 changes: 73 additions & 51 deletions packages/fast-usdc/src/exos/settler.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { M } from '@endo/patterns';
import { decodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
import { PendingTxStatus } from '../constants.js';
import { makeFeeTools } from '../utils/fees.js';
import { EvmHashShape } from '../type-guards.js';
import {
CctpTxEvidenceShape,
EvmHashShape,
makeNatAmountShape,
} from '../type-guards.js';

/**
* @import {FungibleTokenPacketData} from '@agoric/cosmic-proto/ibc/applications/transfer/v2/packet.js';
Expand All @@ -18,7 +22,7 @@ import { EvmHashShape } from '../type-guards.js';
* @import {Zone} from '@agoric/zone';
* @import {HostOf, HostInterface} from '@agoric/async-flow';
* @import {TargetRegistration} from '@agoric/vats/src/bridge-target.js';
* @import {NobleAddress, LiquidityPoolKit, FeeConfig, EvmHash, LogFn} from '../types.js';
* @import {NobleAddress, LiquidityPoolKit, FeeConfig, EvmHash, LogFn, CctpTxEvidence} from '../types.js';
* @import {StatusManager} from './status-manager.js';
*/

Expand All @@ -31,6 +35,15 @@ import { EvmHashShape } from '../type-guards.js';
const makeMintedEarlyKey = (addr, amount) =>
`pendingTx:${JSON.stringify([addr, String(amount)])}`;

/** @param {Brand<'nat'>} USDC */
export const makeAdvanceDetailsShape = USDC =>
harden({
destination: ChainAddressShape,
forwardingAddress: M.string(),
fullAmount: makeNatAmountShape(USDC),
txHash: EvmHashShape,
});

/**
* @param {Zone} zone
* @param {object} caps
Expand Down Expand Up @@ -69,24 +82,21 @@ export const prepareSettler = (
}),
notify: M.interface('SettlerNotifyI', {
notifyAdvancingResult: M.call(
M.record(), // XXX fill in details TODO
makeAdvanceDetailsShape(USDC),
M.boolean(),
).returns(),
checkMintedEarly: M.call(
CctpTxEvidenceShape,
ChainAddressShape,
).returns(M.boolean()),
}),
self: M.interface('SettlerSelfI', {
disburse: M.call(EvmHashShape, M.string(), M.nat()).returns(
M.promise(),
),
forward: M.call(
M.opt(EvmHashShape),
M.string(),
M.nat(),
M.string(),
).returns(),
disburse: M.call(EvmHashShape, M.nat()).returns(M.promise()),
forward: M.call(EvmHashShape, M.nat(), M.string()).returns(),
}),
transferHandler: M.interface('SettlerTransferI', {
onFulfilled: M.call(M.any(), M.record()).returns(),
onRejected: M.call(M.any(), M.record()).returns(),
onFulfilled: M.call(M.undefined(), M.string()).returns(),
onRejected: M.call(M.error(), M.string()).returns(),
}),
},
/**
Expand Down Expand Up @@ -174,20 +184,24 @@ export const prepareSettler = (
log('dequeued', found, 'for', nfa, amount);
switch (found?.status) {
case PendingTxStatus.Advanced:
return self.disburse(found.txHash, nfa, amount);
return self.disburse(found.txHash, amount);

case PendingTxStatus.Advancing:
log('⚠️ tap: minted while advancing', nfa, amount);
this.state.mintedEarly.add(makeMintedEarlyKey(nfa, amount));
return;

case PendingTxStatus.Observed:
case PendingTxStatus.AdvanceSkipped:
case PendingTxStatus.AdvanceFailed:
return self.forward(found.txHash, nfa, amount, EUD);
return self.forward(found.txHash, amount, EUD);

case undefined:
default:
log('⚠️ tap: no status for ', nfa, amount);
log('⚠️ tap: minted before observed', nfa, amount);
// XXX consider capturing in vstorage
// we would need a new key, as this does not have a txHash
this.state.mintedEarly.add(makeMintedEarlyKey(nfa, amount));
}
},
},
Expand All @@ -210,16 +224,12 @@ export const prepareSettler = (
const key = makeMintedEarlyKey(forwardingAddress, fullValue);
if (mintedEarly.has(key)) {
mintedEarly.delete(key);
statusManager.advanceOutcomeForMintedEarly(txHash, success);
if (success) {
void this.facets.self.disburse(
txHash,
forwardingAddress,
fullValue,
);
void this.facets.self.disburse(txHash, fullValue);
} else {
void this.facets.self.forward(
txHash,
forwardingAddress,
fullValue,
destination.value,
);
Expand All @@ -228,14 +238,39 @@ export const prepareSettler = (
statusManager.advanceOutcome(forwardingAddress, fullValue, success);
}
},
/**
* @param {CctpTxEvidence} evidence
* @param {ChainAddress} destination
* @returns {boolean}
* @throws {Error} if minted early, so advancer doesn't advance
*/
checkMintedEarly(evidence, destination) {
const {
tx: { forwardingAddress, amount },
txHash,
} = evidence;
const key = makeMintedEarlyKey(forwardingAddress, amount);
const { mintedEarly } = this.state;
if (mintedEarly.has(key)) {
log(
'matched minted early key, initiating forward',
forwardingAddress,
amount,
);
mintedEarly.delete(key);
statusManager.advanceOutcomeForUnknownMint(evidence);
void this.facets.self.forward(txHash, amount, destination.value);
return true;
}
return false;
},
},
self: {
/**
* @param {EvmHash} txHash
* @param {NobleAddress} nfa
* @param {NatValue} fullValue
*/
async disburse(txHash, nfa, fullValue) {
async disburse(txHash, fullValue) {
const { repayer, settlementAccount } = this.state;
const received = AmountMath.make(USDC, fullValue);
const { zcfSeat: settlingSeat } = zcf.makeEmptySeatKit();
Expand All @@ -260,56 +295,43 @@ export const prepareSettler = (
);
repayer.repay(settlingSeat, split);

// update status manager, marking tx `SETTLED`
// update status manager, marking tx `DISBURSED`
statusManager.disbursed(txHash, split);
},
/**
* @param {EvmHash} txHash
* @param {NobleAddress} nfa
* @param {NatValue} fullValue
* @param {string} EUD
*/
forward(txHash, nfa, fullValue, EUD) {
forward(txHash, fullValue, EUD) {
const { settlementAccount, intermediateRecipient } = this.state;

const dest = chainHub.makeChainAddress(EUD);

// TODO? statusManager.forwarding(txHash, sender, amount);
const txfrV = E(settlementAccount).transfer(
dest,
AmountMath.make(USDC, fullValue),
{ forwardOpts: { intermediateRecipient } },
);
void vowTools.watch(txfrV, this.facets.transferHandler, {
txHash,
nfa,
fullValue,
});
void vowTools.watch(txfrV, this.facets.transferHandler, txHash);
},
},
transferHandler: {
/**
* @param {unknown} _result
* @param {SettlerTransferCtx} ctx
*
* @typedef {{
* txHash: EvmHash;
* nfa: NobleAddress;
* fullValue: NatValue;
* }} SettlerTransferCtx
* @param {EvmHash} txHash
*/
onFulfilled(_result, ctx) {
const { txHash, nfa, fullValue } = ctx;
statusManager.forwarded(txHash, nfa, fullValue);
onFulfilled(_result, txHash) {
// update status manager, marking tx `FORWARDED` without fee split
statusManager.forwarded(txHash, true);
},
/**
* @param {unknown} reason
* @param {SettlerTransferCtx} ctx
* @param {EvmHash} txHash
*/
onRejected(reason, ctx) {
log('⚠️ transfer rejected!', reason, ctx);
// const { txHash, nfa, amount } = ctx;
// TODO(#10510): statusManager.forwardFailed(txHash, nfa, amount);
onRejected(reason, txHash) {
log('⚠️ forward transfer rejected!', reason, txHash);
// update status manager, flagging a terminal state that needs to be
// manual intervention or a code update to remediate
statusManager.forwarded(txHash, false);
},
},
},
Expand Down
Loading

0 comments on commit cfb3a62

Please sign in to comment.