Skip to content

Commit ce6ed3e

Browse files
willemnealclaude
andcommitted
feat: add incremental fixture pull and fetch from sub-registries
Adds `npm run fixtures:pull` to incrementally append new testnet events without wiping existing fixtures. Both pull and refresh now also fetch events from all sub-registry contracts discovered via sub_reg rows. Fixes RPC retention window issue where startLedger fell outside the ~10k-ledger window, causing the RPC to silently return no events. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ee1ba6e commit ce6ed3e

5 files changed

Lines changed: 287 additions & 23 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"test:integration": "jest --selectProjects integration --runInBand",
1313
"validate:pipelines": "bash scripts/validate-pipelines.sh",
1414
"fixtures:refresh": "ts-node scripts/fetch-event-fixtures.ts",
15+
"fixtures:pull": "ts-node scripts/fetch-event-fixtures.ts pull",
1516
"cdk": "cdk"
1617
},
1718
"devDependencies": {

scripts/fetch-event-fixtures.ts

Lines changed: 161 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
* them to test/fixtures/soroban-events-real/<symbol>.json in the shape expected
44
* by the pipeline's transform_2 output.
55
*
6-
* Invoked via `npm run fixtures:refresh`. Not part of CI — requires network.
7-
* Re-run when the event schema changes or a new event symbol is introduced.
6+
* Usage:
7+
* npm run fixtures:refresh — wipe & replace all real fixtures
8+
* npm run fixtures:pull — fetch only events newer than what we have
9+
*
10+
* Not part of CI — requires network.
811
*
912
* The script only fetches events actually emitted by the current root. Soroban
1013
* testnet RPC only retains ~10-20k ledgers of event history, so we scan a
@@ -72,26 +75,24 @@ async function fetchFor(contractId: string, startLedger: number) {
7275
return resp.events;
7376
}
7477

75-
async function main(): Promise<void> {
76-
const server = new SorobanRpc.Server(RPC_URL);
77-
const latest = await server.getLatestLedger();
78-
const startLedger = Math.max(latest.sequence - LOOKBACK_LEDGERS, 1);
79-
80-
console.log(
81-
`fetching events for ${CURRENT_ROOT} from ledger ${startLedger}${latest.sequence}`,
82-
);
83-
84-
const events = await fetchFor(CURRENT_ROOT, startLedger);
85-
console.log(`decoded ${events.length} raw events`);
86-
if (events.length === 0) {
87-
console.log('no events for the current root in the retention window');
88-
console.log('fixtures will be empty; real-data test suite will skip');
89-
}
78+
interface FixtureRow {
79+
id: string;
80+
transaction_hash: string;
81+
ledger_sequence: number;
82+
created_at: string;
83+
command: string;
84+
channel: string;
85+
emitter_contract_id: string;
86+
data: string;
87+
topics: string;
88+
}
9089

91-
const bySymbol: Record<string, unknown[]> = {};
92-
const toScVal = (raw: unknown): xdr.ScVal =>
93-
raw instanceof xdr.ScVal ? raw : xdr.ScVal.fromXDR(raw as string, 'base64');
90+
const toScVal = (raw: unknown): xdr.ScVal =>
91+
raw instanceof xdr.ScVal ? raw : xdr.ScVal.fromXDR(raw as string, 'base64');
9492

93+
/** Convert raw RPC events into fixture rows grouped by command symbol. */
94+
function eventsToRows(events: Awaited<ReturnType<typeof fetchFor>>): Record<string, FixtureRow[]> {
95+
const bySymbol: Record<string, FixtureRow[]> = {};
9596
for (const ev of events) {
9697
const topicsJson = (ev.topic as unknown[]).map((t) => scvalToGoldsky(toScVal(t)));
9798
const dataJson = scvalToGoldsky(toScVal(ev.value));
@@ -101,7 +102,7 @@ async function main(): Promise<void> {
101102
const rawContractId = (ev as { contractId?: unknown }).contractId;
102103
const emitter =
103104
typeof rawContractId === 'string' ? rawContractId : String(rawContractId ?? '');
104-
const row = {
105+
const row: FixtureRow = {
105106
id: ev.id,
106107
transaction_hash: ev.txHash,
107108
ledger_sequence: Number(ev.ledger),
@@ -114,10 +115,75 @@ async function main(): Promise<void> {
114115
};
115116
(bySymbol[symbol] ??= []).push(row);
116117
}
118+
return bySymbol;
119+
}
120+
121+
/** Read existing fixture files and return the highest ledger_sequence seen. */
122+
function loadExistingFixtures(): { bySymbol: Record<string, FixtureRow[]>; maxLedger: number } {
123+
const bySymbol: Record<string, FixtureRow[]> = {};
124+
let maxLedger = 0;
125+
if (!fs.existsSync(FIXTURE_DIR)) return { bySymbol, maxLedger };
126+
for (const file of fs.readdirSync(FIXTURE_DIR).filter((f) => f.endsWith('.json'))) {
127+
const symbol = path.basename(file, '.json');
128+
const rows: FixtureRow[] = JSON.parse(fs.readFileSync(path.join(FIXTURE_DIR, file), 'utf8'));
129+
bySymbol[symbol] = rows;
130+
for (const r of rows) {
131+
if (r.ledger_sequence > maxLedger) maxLedger = r.ledger_sequence;
132+
}
133+
}
134+
return { bySymbol, maxLedger };
135+
}
136+
137+
/** Extract sub-registry contract IDs from sub_reg fixture rows. */
138+
function extractSubRegistryIds(rows: FixtureRow[]): string[] {
139+
const ids: string[] = [];
140+
for (const row of rows) {
141+
const data = JSON.parse(row.data) as {
142+
map?: { key: { symbol?: string }; val: { address?: string } }[];
143+
};
144+
for (const entry of data.map ?? []) {
145+
if (entry.key.symbol === 'contract_id' && entry.val.address) {
146+
ids.push(entry.val.address);
147+
}
148+
}
149+
}
150+
return ids;
151+
}
117152

153+
/** Fetch events for the root contract and all known sub-registries. */
154+
async function fetchAllContracts(
155+
startLedger: number,
156+
existingSubRegRows?: FixtureRow[],
157+
): Promise<Awaited<ReturnType<typeof fetchFor>>> {
158+
// Fetch root events first.
159+
const rootEvents = await fetchFor(CURRENT_ROOT, startLedger);
160+
console.log(` root: ${rootEvents.length} events`);
161+
162+
// Discover sub-registry contract IDs from both freshly fetched and
163+
// previously existing sub_reg rows.
164+
const freshRows = eventsToRows(rootEvents);
165+
const allSubRegRows = [
166+
...(freshRows['sub_reg'] ?? []),
167+
...(existingSubRegRows ?? []),
168+
];
169+
const subIds = [...new Set(extractSubRegistryIds(allSubRegRows))]
170+
.filter((id) => id !== CURRENT_ROOT);
171+
172+
// Fetch events from each sub-registry in parallel.
173+
const subResults = await Promise.all(
174+
subIds.map(async (id) => {
175+
const events = await fetchFor(id, startLedger);
176+
console.log(` sub-registry ${id}: ${events.length} events`);
177+
return events;
178+
}),
179+
);
180+
181+
return [...rootEvents, ...subResults.flat()];
182+
}
183+
184+
/** Write grouped fixture rows to disk. */
185+
function writeFixtures(bySymbol: Record<string, FixtureRow[]>): void {
118186
fs.mkdirSync(FIXTURE_DIR, { recursive: true });
119-
// Clear stale per-symbol files so a shrinking event set doesn't leave
120-
// leftovers from a previous refresh.
121187
for (const existing of fs.readdirSync(FIXTURE_DIR)) {
122188
if (existing.endsWith('.json')) {
123189
fs.unlinkSync(path.join(FIXTURE_DIR, existing));
@@ -130,6 +196,78 @@ async function main(): Promise<void> {
130196
}
131197
}
132198

199+
/** Wipe and replace: fetch everything in the lookback window. */
200+
async function refresh(): Promise<void> {
201+
const server = new SorobanRpc.Server(RPC_URL);
202+
const latest = await server.getLatestLedger();
203+
const startLedger = Math.max(latest.sequence - LOOKBACK_LEDGERS, 1);
204+
205+
console.log(
206+
`fetching events from ledger ${startLedger}${latest.sequence}`,
207+
);
208+
209+
const events = await fetchAllContracts(startLedger);
210+
console.log(`decoded ${events.length} total events`);
211+
if (events.length === 0) {
212+
console.log('no events in the retention window');
213+
console.log('fixtures will be empty; real-data test suite will skip');
214+
}
215+
216+
writeFixtures(eventsToRows(events));
217+
}
218+
219+
/** Incremental pull: keep existing fixtures and append only newer events. */
220+
async function pull(): Promise<void> {
221+
const { bySymbol: existing, maxLedger } = loadExistingFixtures();
222+
const existingIds = new Set<string>();
223+
for (const rows of Object.values(existing)) {
224+
for (const r of rows) existingIds.add(r.id);
225+
}
226+
227+
const server = new SorobanRpc.Server(RPC_URL);
228+
const latest = await server.getLatestLedger();
229+
// Start one ledger after the last one we already have, but never older
230+
// than the RPC retention window (the RPC silently returns nothing if the
231+
// startLedger is outside its ~10k-ledger window).
232+
const retentionFloor = Math.max(latest.sequence - LOOKBACK_LEDGERS, 1);
233+
const startLedger = maxLedger > 0
234+
? Math.max(maxLedger + 1, retentionFloor)
235+
: retentionFloor;
236+
237+
console.log(
238+
`pulling new events from ledger ${startLedger}${latest.sequence}` +
239+
(maxLedger > 0 ? ` (existing max ledger: ${maxLedger})` : ' (no existing fixtures, full fetch)'),
240+
);
241+
242+
const events = await fetchAllContracts(startLedger, existing['sub_reg']);
243+
const newRows = eventsToRows(events);
244+
245+
// Deduplicate by event id and merge into existing fixtures.
246+
let added = 0;
247+
for (const [symbol, rows] of Object.entries(newRows)) {
248+
if (!existing[symbol]) existing[symbol] = [];
249+
for (const row of rows) {
250+
if (!existingIds.has(row.id)) {
251+
existing[symbol].push(row);
252+
existingIds.add(row.id);
253+
added++;
254+
}
255+
}
256+
}
257+
258+
console.log(`fetched ${events.length} events, ${added} new`);
259+
writeFixtures(existing);
260+
}
261+
262+
async function main(): Promise<void> {
263+
const mode = process.argv[2] ?? 'refresh';
264+
if (mode === 'pull') {
265+
await pull();
266+
} else {
267+
await refresh();
268+
}
269+
}
270+
133271
main().catch((err) => {
134272
console.error(err);
135273
process.exit(1);

test/fixtures/soroban-events-real/register.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,60 @@
6464
"emitter_contract_id": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
6565
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_id\"},\"val\":{\"address\":\"CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3\"}},{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"defindex\"}},{\"key\":{\"symbol\":\"sac\"},\"val\":{\"bool\":false}},{\"key\":{\"symbol\":\"wasm_hash\"},\"val\":{\"bytes\":\"b038a31da054b4a174b4d5b81b52691f67c21f6004cf233c37175e63a87304df\"}}]}",
6666
"topics": "[{\"symbol\":\"register\"}]"
67+
},
68+
{
69+
"id": "0008975983432478720-0000000000",
70+
"transaction_hash": "2d60aecbc82811d6082801ed7027113f544a2765a36cff791d369891f25519a5",
71+
"ledger_sequence": 2089884,
72+
"created_at": "2026-04-17T19:31:13Z",
73+
"command": "register",
74+
"channel": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
75+
"emitter_contract_id": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
76+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_id\"},\"val\":{\"address\":\"CDSCWE4GLNBYYTES2OCYDFQA2LLY4RBIAX6ZI32VSUXD7GO6HRPO4A32\"}},{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"factory\"}},{\"key\":{\"symbol\":\"sac\"},\"val\":{\"bool\":false}},{\"key\":{\"symbol\":\"wasm_hash\"},\"val\":{\"bytes\":\"baba1e5e12e7f83b49f6f2b6b17f7bc672e38c08dca74f77d66d569c522cfec8\"}}]}",
77+
"topics": "[{\"symbol\":\"register\"}]"
78+
},
79+
{
80+
"id": "0008975983432478720-0000000001",
81+
"transaction_hash": "2d60aecbc82811d6082801ed7027113f544a2765a36cff791d369891f25519a5",
82+
"ledger_sequence": 2089884,
83+
"created_at": "2026-04-17T19:31:13Z",
84+
"command": "register",
85+
"channel": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
86+
"emitter_contract_id": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
87+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_id\"},\"val\":{\"address\":\"CBMVK2JK6NTOT2O4HNQAIQFJY232BHKGLIMXDVQVHIIZKDACXDFZDWHN\"}},{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"usdc-vault\"}},{\"key\":{\"symbol\":\"sac\"},\"val\":{\"bool\":false}},{\"key\":{\"symbol\":\"wasm_hash\"},\"val\":{\"bytes\":\"f345228dca59c6605789620e9ec62ff4847a0927c33dac7581a955fe746016be\"}}]}",
88+
"topics": "[{\"symbol\":\"register\"}]"
89+
},
90+
{
91+
"id": "0008975983432478720-0000000002",
92+
"transaction_hash": "2d60aecbc82811d6082801ed7027113f544a2765a36cff791d369891f25519a5",
93+
"ledger_sequence": 2089884,
94+
"created_at": "2026-04-17T19:31:13Z",
95+
"command": "register",
96+
"channel": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
97+
"emitter_contract_id": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
98+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_id\"},\"val\":{\"address\":\"CCLV4H7WTLJQ7ATLHBBQV2WW3OINF3FOY5XZ7VPHZO7NH3D2ZS4GFSF6\"}},{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"xlm-vault\"}},{\"key\":{\"symbol\":\"sac\"},\"val\":{\"bool\":false}},{\"key\":{\"symbol\":\"wasm_hash\"},\"val\":{\"bytes\":\"f345228dca59c6605789620e9ec62ff4847a0927c33dac7581a955fe746016be\"}}]}",
99+
"topics": "[{\"symbol\":\"register\"}]"
100+
},
101+
{
102+
"id": "0008975983432478720-0000000003",
103+
"transaction_hash": "2d60aecbc82811d6082801ed7027113f544a2765a36cff791d369891f25519a5",
104+
"ledger_sequence": 2089884,
105+
"created_at": "2026-04-17T19:31:13Z",
106+
"command": "register",
107+
"channel": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
108+
"emitter_contract_id": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
109+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_id\"},\"val\":{\"address\":\"CBIS5TEMTNNOTBE3WXPQUAGUEDYZZVIWAKTXEQCOUJ34OJJ3FJ5NLF2P\"}},{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"cetes-vault\"}},{\"key\":{\"symbol\":\"sac\"},\"val\":{\"bool\":false}},{\"key\":{\"symbol\":\"wasm_hash\"},\"val\":{\"bytes\":\"f345228dca59c6605789620e9ec62ff4847a0927c33dac7581a955fe746016be\"}}]}",
110+
"topics": "[{\"symbol\":\"register\"}]"
111+
},
112+
{
113+
"id": "0008975983432478720-0000000004",
114+
"transaction_hash": "2d60aecbc82811d6082801ed7027113f544a2765a36cff791d369891f25519a5",
115+
"ledger_sequence": 2089884,
116+
"created_at": "2026-04-17T19:31:13Z",
117+
"command": "register",
118+
"channel": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
119+
"emitter_contract_id": "CBJIBSIHQ4SDVIKH4BFUNDPI47IXIGW3ETGYCH3MMA4C4MDGUXJRDAW3",
120+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_id\"},\"val\":{\"address\":\"CCP4RBDWPRNO2LWO23XFU4BBLGA73J5N3BK7EHRJUHVN33YEMMFB2MBE\"}},{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"cetes-blend-strategy\"}},{\"key\":{\"symbol\":\"sac\"},\"val\":{\"bool\":false}},{\"key\":{\"symbol\":\"wasm_hash\"},\"val\":{\"bytes\":\"7ddf1f6ac5705be937fd10ba3c199e21ab8e46c6fad322e10004d4a49b504769\"}}]}",
121+
"topics": "[{\"symbol\":\"register\"}]"
67122
}
68123
]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[
2+
{
3+
"id": "0009184134727512064-0000000000",
4+
"transaction_hash": "333713d606c4c5d81e3e4c2d937f3493524eacdd087d8fc8882056c79e57c114",
5+
"ledger_sequence": 2138348,
6+
"created_at": "2026-04-20T14:56:46Z",
7+
"command": "rename",
8+
"channel": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
9+
"emitter_contract_id": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
10+
"data": "{\"map\":[{\"key\":{\"symbol\":\"new_name\"},\"val\":{\"string\":\"ununverified\"}},{\"key\":{\"symbol\":\"old_name\"},\"val\":{\"string\":\"unverified\"}}]}",
11+
"topics": "[{\"symbol\":\"rename\"}]"
12+
},
13+
{
14+
"id": "0009184147612450816-0000000000",
15+
"transaction_hash": "83209b70d021d080a11ae75dcd705f44c2d68e4e8b8ed421bc9d8665e7dd657c",
16+
"ledger_sequence": 2138351,
17+
"created_at": "2026-04-20T14:57:01Z",
18+
"command": "rename",
19+
"channel": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
20+
"emitter_contract_id": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
21+
"data": "{\"map\":[{\"key\":{\"symbol\":\"new_name\"},\"val\":{\"string\":\"unverified\"}},{\"key\":{\"symbol\":\"old_name\"},\"val\":{\"string\":\"ununverified\"}}]}",
22+
"topics": "[{\"symbol\":\"rename\"}]"
23+
},
24+
{
25+
"id": "0009184744612904960-0000000000",
26+
"transaction_hash": "d0490851e964b0112735a63137c6fd234a92af6045efcad16c583cbfc20ab49e",
27+
"ledger_sequence": 2138490,
28+
"created_at": "2026-04-20T15:08:38Z",
29+
"command": "rename",
30+
"channel": "CB7V4U6KMQQIR5WXUST4IAMNVKMHMNXXFP7OL6DEYHSOQSA5ESOX7OLJ",
31+
"emitter_contract_id": "CB7V4U6KMQQIR5WXUST4IAMNVKMHMNXXFP7OL6DEYHSOQSA5ESOX7OLJ",
32+
"data": "{\"map\":[{\"key\":{\"symbol\":\"new_name\"},\"val\":{\"string\":\"unverifed\"}},{\"key\":{\"symbol\":\"old_name\"},\"val\":{\"string\":\"usdc\"}}]}",
33+
"topics": "[{\"symbol\":\"rename\"}]"
34+
},
35+
{
36+
"id": "0009184757497774080-0000000000",
37+
"transaction_hash": "61a267dc484e6b7c81d56b7a68329335e0e53b6bbae63964943b89aa71269156",
38+
"ledger_sequence": 2138493,
39+
"created_at": "2026-04-20T15:08:53Z",
40+
"command": "rename",
41+
"channel": "CB7V4U6KMQQIR5WXUST4IAMNVKMHMNXXFP7OL6DEYHSOQSA5ESOX7OLJ",
42+
"emitter_contract_id": "CB7V4U6KMQQIR5WXUST4IAMNVKMHMNXXFP7OL6DEYHSOQSA5ESOX7OLJ",
43+
"data": "{\"map\":[{\"key\":{\"symbol\":\"new_name\"},\"val\":{\"string\":\"usdc\"}},{\"key\":{\"symbol\":\"old_name\"},\"val\":{\"string\":\"unverifed\"}}]}",
44+
"topics": "[{\"symbol\":\"rename\"}]"
45+
}
46+
]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"id": "0009184160497328128-0000000000",
4+
"transaction_hash": "01b50de58fcb0bc898a32e80229ffd7777ee2ca3f3d9d5a2cf1d0baa98fe44b3",
5+
"ledger_sequence": 2138354,
6+
"created_at": "2026-04-20T14:57:16Z",
7+
"command": "update_owner",
8+
"channel": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
9+
"emitter_contract_id": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
10+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"unverified\"}},{\"key\":{\"symbol\":\"new_owner\"},\"val\":{\"address\":\"GA7Y5MOUJAKWQYR3VD4V5CXILOR2XI3LLJJ4S36YFT557HG5AA5KPXRE\"}}]}",
11+
"topics": "[{\"symbol\":\"update_owner\"}]"
12+
},
13+
{
14+
"id": "0009184173382213632-0000000000",
15+
"transaction_hash": "e6031ec1b42f7dc14d9621ed57394aa0e79e4e6c8ab9e1e98e4b523132cf4ab2",
16+
"ledger_sequence": 2138357,
17+
"created_at": "2026-04-20T14:57:31Z",
18+
"command": "update_owner",
19+
"channel": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
20+
"emitter_contract_id": "CBNBQND6EMYTTRTCUWUJ3VIKF7RUUISK5T4GAKTXRVIQRHGP4XQY4ID7",
21+
"data": "{\"map\":[{\"key\":{\"symbol\":\"contract_name\"},\"val\":{\"string\":\"unverified\"}},{\"key\":{\"symbol\":\"new_owner\"},\"val\":{\"address\":\"GAMPJROHOAW662FINQ4XQOY2ULX5IEGYXCI4SMZYE75EHQBR6PSTJG3M\"}}]}",
22+
"topics": "[{\"symbol\":\"update_owner\"}]"
23+
}
24+
]

0 commit comments

Comments
 (0)