Replies: 2 comments
-
LLM generated answer. not thoroughly reviewed How to properly poll Agoric wallet data: Working examplesHere are correct patterns that avoid the common pitfalls developers encounter when trying to use familiar React patterns ❌ Wrong: Expecting processed wallet JSON// This won't work - you get raw CapData, not processed objects
const response = await fetch(`/agoric/vstorage/data/published.wallet.${address}`);
const walletData = await response.json(); // Gets CapData strings, not wallet objects
✅ Right: Using chain storage watcher
import { makeAgoricChainStorageWatcher } from '@agoric/rpc';
const watcher = makeAgoricChainStorageWatcher(rpcEndpoint, chainId);
useEffect(() => {
const loadWalletData = async () => {
// Use the watcher's marshaller - it has the right state
const updates = await watcher.readFully(`wallet.${address}`);
const deserializedUpdates = updates.map(update =>
watcher.marshaller.fromCapData(JSON.parse(update))
);
setWalletUpdates(deserializedUpdates);
};
loadWalletData();
}, [address, watcher]);
❌ Wrong: Creating your own marshaller
// Don't do this - marshaller needs proper initialization
import { makeMarshal } from '@endo/marshal';
const marshaller = makeMarshal(); // Missing board ID mappings!
const data = marshaller.fromCapData(capData); // Will fail on board IDs
✅ Right: Using the watcher's marshaller
// The watcher's marshaller is already populated with board ID mappings
const deserializedData = watcher.marshaller.fromCapData(capData);
❌ Wrong: Treating board IDs as the actual objects
// Board IDs are just strings - you can't use them as brands
const boardId = "board12345";
const amount = { brand: boardId, value: 1000n }; // Wrong!
✅ Right: Let the marshaller resolve board IDs
// The marshaller turns board IDs into actual brand objects
const capData = {
body: '#{"brand": "$0.Alleged: IST brand"}',
slots: ["board12345"]
};
const resolved = watcher.marshaller.fromCapData(capData);
// Now resolved.brand is the actual brand object, not a string
❌ Wrong: Using wallet.marshaller
// This property doesn't exist
const data = wallet.marshaller.fromCapData(capData); // TypeError
✅ Right: Using the correct marshaller source
// Get marshaller from the watcher, not the wallet
const data = watcher.marshaller.fromCapData(capData);
## ❌ Wrong: Incorrect path for readFully
```javascript
// readFully needs the full path including 'published.'
const updates = await watcher.readFully(`wallet.${address}`); // Missing prefix!
✅ Right: Full path for readFully
// readFully expects the complete vstorage path
const updates = await watcher.readFully(`published.wallet.${address}`);
✅ Alternative: Use readPublished without prefix
// readPublished automatically adds the 'published.' prefix
const updates = await watcher.readPublished(`wallet.${address}`);;
Complete working example
```js
import { makeAgoricChainStorageWatcher } from '@agoric/rpc';
import { useEffect, useState } from 'react';
function WalletWatcher({ address, rpcEndpoint, chainId }) {
const [walletUpdates, setWalletUpdates] = useState([]);
const [watcher, setWatcher] = useState(null);
// Initialize watcher
useEffect(() => {
const w = makeAgoricChainStorageWatcher(rpcEndpoint, chainId);
setWatcher(w);
return () => {
// Clean up watcher if needed
};
}, [rpcEndpoint, chainId]);
// Watch wallet updates
useEffect(() => {
if (!watcher || !address) return;
const loadUpdates = async () => {
try {
const updates = await watcher.readFully(`wallet.${address}`);
const deserialized = updates.map(update => {
const capData = JSON.parse(update);
return watcher.marshaller.fromCapData(capData);
});
setWalletUpdates(deserialized);
} catch (error) {
console.error('Failed to load wallet updates:', error);
}
};
loadUpdates();
}, [watcher, address]);
return (
<div>
<h3>Wallet Updates for {address}</h3>
{walletUpdates.map((update, i) => (
<div key={i}>
<pre>{JSON.stringify(update, null, 2)}</pre>
</div>
))}
</div>
);
} Key takeaways 1 Use the watcher's marshaller - it has the board ID mappings you need |
Beta Was this translation helpful? Give feedback.
-
more LLM-generated stuff, after a long-ish chat. Why does Agoric use "notifiers" instead of normal REST endpoints?The short answer: Deterministic consensus makes traditional query patterns prohibitively expensive. The Fundamental ProblemMost blockchains separate "queries" from "transactions." You can cheaply read current state without going through This means a simple The Notifier SolutionInstead of expensive queries, Agoric uses notifiers: // Expensive: every call goes through consensus
const balance = await contract.getBalance(); // ❌ Too expensive
// Efficient: get a notifier once, watch for changes
const balanceNotifier = await contract.getBalanceNotifier(); // ✅ One-time cost
for await (const balance of subscribeLatest(balanceNotifier)) {
// Updates come automatically when balance changes
console.log('New balance:', balance);
} The notifier is an async iterator that advances when the underlying value changes. This amortizes the consensus cost - How This Extends Off-Chain On-chain notifiers publish their updates to vstorage (Agoric's key-value store). When a notifier advances, the new value Off-chain clients can then simulate the notifier pattern: // This looks like polling, but it's actually simulating a notifier // Under the hood: polls vstorage, advances iterator when values change The Object Reference Problem But here's where it gets tricky: the data isn't just values, it contains references to remote objects. This is like Unix // A brand reference in CapData Just like you can pass file descriptors between processes (via Unix domain sockets), you can pass board IDs between This is different from something like Docker image IDs. A Docker image ID like sha256:abc123... refers to data (a Why This Feels Weird Coming from traditional web development, this architecture feels backwards: • Web2: Server does computation, sends processed results The mental model shift is: you're not querying a database, you're participating in a distributed notification system The Async Iterator Pattern This is why Agoric APIs return async iterators everywhere: // Wallet purses // Contract state // Chain storage Each of these is simulating the on-chain notifier pattern where values advance when the underlying state changes, rather Why Not Just Use WebSockets? You might wonder: "Why not just use WebSockets for real-time updates?" The key insight is that the data contains object references. A WebSocket could send you JSON, but Agoric's data contains The Bigger Picture This architecture reflects Agoric's core philosophy: bring the expressiveness of JavaScript to blockchain without But the benefit is enormous: you can write complex smart contracts in JavaScript with proper object references, |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
working with an LLM, it tripped over this a bunch. looking at the aider commit log:
Marshaller confusion:
• "fix: deserialize wallet updates using marshaller in TransactionHistory"
• "feat: use marshaller to parse capData in getInvocationId"
• "fix: update wallet marshaller property from publicMarshaller to marshaller"
These show confusion about which marshaller to use and where to get it from.
VStorage path confusion:
• "fix: add published. prefix to wallet vstorage path"
• "feat: add missing walletAddress and vstorage client props to TransactionHistory"
This shows the trial-and-error around vstorage path conventions.
Data format confusion:
• "refactor: parse and deserialize wallet updates during readFully"
• "refactor: handle multiple wallet update data formats in watcher"
These indicate confusion about when data needs deserialization vs when it's already parsed.
Watcher lifecycle issues:
• "feat: add wallet state watcher for saved entries in Admin component"
• "refactor: Consolidate watcher management and add network selector to Admin page"
Shows struggles with properly setting up and managing the watcher lifecycle.
Board ID resolution:
• "fix: use public marshaller to find instance board IDs"
• Multiple commits around "board ID extraction" and "instance info parsing"
Classic confusion about working with board ID strings vs resolved objects.
These commits show the exact pattern: developers trying familiar web patterns, hitting Agoric's unique architecture, and
working through the marshalling/notifier complexity via trial-and-error.
Related questions:
board12345
strings and why can't I use them directly?Beta Was this translation helpful? Give feedback.
All reactions