Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 45 additions & 12 deletions src/abi/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,15 +382,29 @@ export const ACCOUNTS_SET_ORACLE_PRICE_CAP: readonly AccountSpec[] = [
] as const;

/**
* ResolveMarket: 4 accounts.
* v12.19 wrapper at src/percolator.rs:9748 calls accounts::expect_len(4).
* Layout: [admin(s+w), slab(w), clock, oracle].
* ResolveMarket (tag 19): 2 accounts (inferred — see confidence note below).
*
* The v12.19 4-account layout this constant previously documented
* ([admin(s+w), slab(w), clock, oracle], src/percolator.rs:9748) is stale: its
* paired encoder `encodeResolveMarket` was confirmed rewritten for v17
* (instructions.ts) — "v17 wire: tag(1) only... BREAKING vs v12.x ... the mode
* byte has been REMOVED" — but this account spec was never updated to match,
* unlike every other v17-rewritten spec in this file.
*
* INFERENCE, not a confirmed v17 wrapper read (no percolator-prog source is
* available in this repo to verify directly): every one of the 13 other specs
* in this file carrying a "v17 wire account layout" comment explicitly drops
* `clock` and `oracle` as standalone accounts with no exception (e.g.
* ACCOUNTS_TRADE_NOCPI: "v12 stale accounts removed: lp, clock, oracle";
* ACCOUNTS_CLOSE_PORTFOLIO: "...clock, oracle"). The closest structural analog,
* ACCOUNTS_RESTART_ASSET_ORACLE — also an admin-gated, market-level,
* no-token-movement instruction — is exactly [authority(signer), market(w)].
* Before relying on this in production, verify against an actual v17
* `handle_resolve_market` decode (devnet dry-run or program source).
*/
export const ACCOUNTS_RESOLVE_MARKET: readonly AccountSpec[] = [
{ name: "admin", signer: true, writable: true },
{ name: "slab", signer: false, writable: true },
{ name: "clock", signer: false, writable: false },
{ name: "oracle", signer: false, writable: false },
{ name: "market", signer: false, writable: true },
] as const;

/**
Expand Down Expand Up @@ -496,14 +510,33 @@ export const ACCOUNTS_DEPOSIT_FEE_CREDITS: readonly AccountSpec[] = [
] as const;

/**
* ConvertReleasedPnl (tag 28): 4 accounts. Owner only.
* Wrapper: src/percolator.rs:10636.
* ConvertReleasedPnl (tag 28): 3 accounts (inferred — see confidence note below).
* Owner only. No token movement (internal PnL-bucket conversion within the
* same portfolio).
*
* The v12.19 4-account layout this constant previously documented
* ([user(s+w), slab(w), clock, oracle], src/percolator.rs:10636) is stale.
* Its paired encoder, `encodeConvertReleasedPnl`, was confirmed rewritten for
* v17 (instructions.ts): "BREAKING vs v12.x: userIdx(u16) removed... v17
* portfolios are identified by account key alone" — and that same comment
* points back to this exact constant ("Accounts: see
* ACCOUNTS_CONVERT_RELEASED_PNL"), but this spec was never updated to add the
* account-key-identified portfolio the encoder's own doc describes.
*
* INFERENCE, not a confirmed v17 wrapper read (no percolator-prog source is
* available in this repo to verify directly). Basis: ConvertReleasedPnl is
* owner-initiated and moves no real tokens, the exact same category as
* ACCOUNTS_INIT_USER (InitPortfolio) and ACCOUNTS_CLOSE_ACCOUNT
* (ClosePortfolio) — both confirmed v17-rewritten to the identical 3-account
* shape [owner(signer,w), market(w), portfolio(w)], with v12 clock/oracle/userIdx
* dropped for the same "account-key, not index" reason this encoder's doc cites.
* Before relying on this in production, verify against an actual v17
* `handle_convert_released_pnl` decode (devnet dry-run or program source).
*/
export const ACCOUNTS_CONVERT_RELEASED_PNL: readonly AccountSpec[] = [
{ name: "user", signer: true, writable: true },
{ name: "slab", signer: false, writable: true },
{ name: "clock", signer: false, writable: false },
{ name: "oracle", signer: false, writable: false },
{ name: "owner", signer: true, writable: true },
{ name: "market", signer: false, writable: true },
{ name: "portfolio", signer: false, writable: true },
] as const;

/**
Expand Down
28 changes: 28 additions & 0 deletions test/accounts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
ACCOUNTS_UPDATE_CONFIG,
ACCOUNTS_SET_MAINTENANCE_FEE,
ACCOUNTS_RESOLVE_MARKET,
ACCOUNTS_CONVERT_RELEASED_PNL,
ACCOUNTS_WITHDRAW_INSURANCE,
ACCOUNTS_WITHDRAW_INSURANCE_LIMITED_LIVE,
ACCOUNTS_WITHDRAW_INSURANCE_LIMITED_RESOLVED,
Expand Down Expand Up @@ -264,6 +265,33 @@ describe("Signer / writable invariants", () => {
}
});

it("ACCOUNTS_RESOLVE_MARKET matches its v17-rewritten encoder, not the stale v12.19 layout", () => {
// encodeResolveMarket was confirmed rewritten for v17 (tag-only, no mode byte).
// Every other v17-rewritten spec in this file drops standalone clock/oracle
// accounts with no exception — this spec must not still carry them.
expect(ACCOUNTS_RESOLVE_MARKET).toHaveLength(2);
expect(ACCOUNTS_RESOLVE_MARKET.find((a) => a.name === "clock")).toBeUndefined();
expect(ACCOUNTS_RESOLVE_MARKET.find((a) => a.name === "oracle")).toBeUndefined();
expect(ACCOUNTS_RESOLVE_MARKET[0]).toMatchObject({ name: "admin", signer: true, writable: true });
const marketSlab = ACCOUNTS_RESOLVE_MARKET.find((a) => a.name === "slab" || a.name === "market");
expect(marketSlab, "market/slab account missing").toBeDefined();
expect(marketSlab!.writable).toBe(true);
});

it("ACCOUNTS_CONVERT_RELEASED_PNL matches its v17-rewritten encoder, not the stale v12.19 layout", () => {
// encodeConvertReleasedPnl's own doc says "v17 portfolios are identified by
// account key alone" (userIdx removed) and points back to this constant —
// it must include a portfolio account, not the old userIdx-era clock/oracle pair.
expect(ACCOUNTS_CONVERT_RELEASED_PNL).toHaveLength(3);
expect(ACCOUNTS_CONVERT_RELEASED_PNL.find((a) => a.name === "clock")).toBeUndefined();
expect(ACCOUNTS_CONVERT_RELEASED_PNL.find((a) => a.name === "oracle")).toBeUndefined();
expect(ACCOUNTS_CONVERT_RELEASED_PNL.find((a) => a.name === "portfolio")).toBeDefined();
expect(ACCOUNTS_CONVERT_RELEASED_PNL[0]).toMatchObject({ name: "owner", signer: true, writable: true });
const marketSlab2 = ACCOUNTS_CONVERT_RELEASED_PNL.find((a) => a.name === "slab" || a.name === "market");
expect(marketSlab2, "market/slab account missing").toBeDefined();
expect(marketSlab2!.writable).toBe(true);
});

});

// ============================================================================
Expand Down