-
Notifications
You must be signed in to change notification settings - Fork 254
Description
Describe the bug
When using publicInvitationMaker with wallet.makeOffer(), Zoe internally calls Date.now() during invitation ID generation, causing a TypeError: secure mode Calling %SharedDate%.now() throws error in the SES environment. This prevents offers from executing despite successful transaction submission.
Impact: Offers fail silently - transaction is submitted (fee charged), but funds are not transferred. This completely blocks contract execution when using dynamic invitation creation patterns.
To Reproduce
1. Create a contract with public invitation maker:
// qstn-survey.contract.js
import { Far } from '@endo/far';
import { atomicRearrange } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
import '@agoric/zoe/exported.js';
export const start = async zcf => {
const istBrand = zcf.getTerms().brands.IST;
const proceeds = zcf.makeEmptySeatKit().zcfSeat;
const fundSurveyHandler = funderSeat => {
const { give } = funderSeat.getProposal();
const fundingAmount = give.IST;
atomicRearrange(
zcf,
harden([[funderSeat, proceeds, { IST: fundingAmount }]]),
);
funderSeat.exit(true);
return undefined;
};
const makeSurveyFundingInvitation = () => {
return zcf.makeInvitation(
fundSurveyHandler,
'fund survey',
undefined,
undefined,
);
};
const publicFacet = Far('Public Facet', {
makeSurveyFundingInvitation,
});
return harden({ publicFacet });
};2. Deploy contract to chain:
# Using agoric-3-proposals Docker image
docker compose up -d
cd contract && npm start3. In frontend, submit offer using wallet.makeOffer():
import { makeAgoricWalletConnection } from '@agoric/wallet-connection';
const wallet = await makeAgoricWalletConnection(watcher, chainStorageWatcher);
const istAmount = AmountMath.make(brands.IST, 1_000_000n); // 1 IST
// This triggers the error:
await wallet.makeOffer(
{
source: 'contract',
instance: contractInstance,
publicInvitationMaker: 'makeSurveyFundingInvitation',
},
{
give: { IST: istAmount },
want: {},
},
undefined,
(update) => console.log('Status:', update)
);4. Observe the error:
In chain logs:
SwingSet: vat: v43: wallet <address> IMMEDIATE OFFER ERROR: (RemoteTypeError(error:liveSlots:v9#70082)#156)
SwingSet: vat: v43: wallet <address> offerStatus {
error: 'TypeError: secure mode Calling %SharedDate%.now() throws',
id: 1763091046542,
invitationSpec: {
instance: Object [Alleged: InstanceHandle] {},
publicInvitationMaker: 'makeSurveyFundingInvitation',
source: 'contract'
},
proposal: {
give: { IST: { brand: Object [Alleged: IST brand] {}, value: 1_000_000n } },
want: {}
}
}
Result:
- ✅ Transaction submitted successfully
- ✅ Transaction fee charged (~0.2 IST)
- ❌ Offer fails with Date.now() error
- ❌ Funds NOT transferred (balance unchanged except for fee)
Expected behavior
When wallet.makeOffer() is called with publicInvitationMaker:
- Frontend calls
wallet.makeOffer()with invitation specification - Wallet requests contract to create invitation via
publicInvitationMaker - Contract creates invitation using
zcf.makeInvitation()without Date.now() errors - Invitation is serialized and passed to wallet
- Offer is executed and funds are transferred
Expected result:
- Transaction submitted
- Fee charged
- Offer executes successfully
- Funds transferred to contract
Platform Environment
- OS: macOS (darwin 23.6.0)
- Node.js: v21.7.2
- Docker: Version 28.5.1
- Agoric SDK Version:
0.35.0-u22.1(fromghcr.io/agoric/agoric-3-proposals:latest) - Chain: Local devnet (
agoriclocal) - Wallet: Keplr
- Frontend Library:
@agoric/wallet-connection
Verification:
$ docker exec $(docker ps -q -f name=agd) agd version
0.35.0-u22.1Special Notes:
- Using Docker-based local chain
- Contract has zero
Date.now()calls (verified) - Contract has zero
console.*calls (verified) - Error occurs even with minimal invitation pattern (no parameters, no customDetails)
Additional context
Root Cause
The error occurs inside Zoe's internal invitation creation code, not in application code. When zcf.makeInvitation() is called, Zoe internally generates a unique invitation ID using Date.now(), which violates SES (Secure EcmaScript) rules that prohibit non-deterministic operations.
Evidence
I've confirmed this is a framework issue, not application code:
- ✅ Contract code has zero
Date.now()calls (grep verified) - ✅ Contract code has zero
console.*calls (grep verified) - ✅ Simplified invitation creation to absolute minimum - still fails
- ✅ Removed all
customDetailsandproposalShape- still fails - ✅ Matched exact pattern from working
dapp-offer-upexample - still fails - ✅ Used empty callback functions - still fails
- ✅ Removed
invitationArgsfrom frontend - still fails
Error occurs regardless of:
- Invitation parameters
- Custom details
- Proposal shapes
- Callback functions
- Frontend patterns
Full Error Stack
2025-11-14T03:30:49.763Z SwingSet: vat: v43: TypeError: secure mode Calling %SharedDate%.now() throws
at makeError (/bundled-source/.../ses/src/error/assert.js:352)
at decodeErrorCommon (/bundled-source/.../marshal/src/marshal.js:309)
2025-11-14T03:30:49.765Z SwingSet: vat: v43: wallet <address> offerStatus {
error: 'TypeError: secure mode Calling %SharedDate%.now() throws',
id: 1763091046542,
invitationSpec: {
instance: Object [Alleged: InstanceHandle] {},
invitationArgs: [ [ 'osmosis' ], null, null ],
publicInvitationMaker: 'makeSurveyFundingInvitation',
source: 'contract'
},
offerArgs: undefined,
proposal: {
give: { IST: { brand: Object [Alleged: IST brand] {}, value: 1_000_000n } },
want: {}
}
}
2025-11-14T03:30:49.783Z SwingSet: xsnap: v43: RemoteTypeError(error:liveSlots:v9#70082)#156
2025-11-14T03:30:49.798Z SwingSet: xsnap: v10: UnhandledPromiseRejectionWarning: (RemoteTypeError(error:liveSlots:v43#70078)#88)
Attempted Workarounds
All of the following were attempted without success:
- Remove Date.now() from contract - No effect (contract doesn't use it)
- Simplify invitation creation - Still fails
- Remove invitationArgs - Still fails
- Use empty callbacks - Prevents callback errors but offer still fails
- Match dapp-offer-up pattern exactly - Still fails
- Remove all customDetails/proposalShape - Still fails