Skip to content
Merged
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
7 changes: 4 additions & 3 deletions contracts/risk-core/DEPLOYED_ADDRESSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Artifacts: `deployments/8453/MARGIN_ENGINE_V2.json`, `MARGIN_ENGINE_V2_VAULT_ACT
the liquidation keeper, the pager and SM coverage, and markets-service listing a second series (#228) under a
`cNGN-<expiry>` symbol, not `USDC-cNGN-30NOV26`, which v1's series holds.

### EURC-30NOV26 on MarginEngine v2 — DEPLOYED 2026-10-30 (closed), setup pending
### EURC-30NOV26 on MarginEngine v2 — DEPLOYED and SET UP 2026-10-30 (closed)

A second v2 series, on v2's market 2 (EUR): USDC per EURC, 1,000 EURC a contract, VM in USDC through pool #37, expiry
1796054400 (2026-11-30 16:00 UTC; last trade 15:00), IM 6% / MM 4%, carry -1.5% (an assumption), cap 0 until enable
Expand All @@ -194,8 +194,9 @@ two owner-only engine setters in the vault's setup batch.
are nominated to the vault (`pendingOwner`); the cap is 0. Artifacts: `EURC_FUTURE_V2.json`,
`EURC_FUTURE_V2_VAULT_ACTIONS.json`, `EURC_FUTURE_V2_ENABLE_VAULT_ACTIONS.json`. The deployer sends nothing else
until setup lands.
2. `scripts/ops/propose_eurc_future_v2_batch.py eurc-future-setup`: accept both; `engineV2.whitelistAsset(future, 2,
Future)`; `engineV2.setFutureMarginRequirements(2, 0.04e18, 0.06e18)`.
2. **Setup landed 2026-10-30 05:35 UTC** (`eurc-future-setup`, proposed from the box's exchange-ops at 23e5037 and
approved by hand in MPCVault): the vault owns both; `engineV2.whitelistAsset(future, 2, Future)`;
`engineV2.setFutureMarginRequirements(2, 0.04e18, 0.06e18)`. The deployer is free again.
3. Off chain: forward publisher for this feed, pager and v2 keeper reading the future, markets-service listing
`EURC-30NOV26`.
4. `eurc-future-enable`: `future.setTotalPositionCap(v2, 18e18)`, `tradeModule.setDatedFutureAsset(future, true)`.
Expand Down
7 changes: 6 additions & 1 deletion contracts/risk-core/scripts/ops/check_perp_pager.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,10 @@ def future_v2_artifacts() -> list[tuple[Path, dict]]:


def series_label(future: dict) -> str:
"""cNGN-30NOV26-style, for messages: the artifact's base (default cNGN, MARGIN_ENGINE_V2.json names none) and expiry."""
"""cNGN-30NOV26-style, for messages: the artifact's symbol (EURC_FUTURE_V2.json names one), else its base (default
cNGN, MARGIN_ENGINE_V2.json names none) and expiry."""
if future.get("symbol"):
return future["symbol"]
return f"{future.get('baseSymbol', 'cNGN')}-{time.strftime('%d%b%y', time.gmtime(int(future['expiry']))).upper()}"


Expand Down Expand Up @@ -1119,6 +1122,8 @@ def self_test() -> int:
assert abs(parts["EURC-30NOV26"] - 10 * 1_000 * 1.1625 * 0.15) < 1e-6
assert series_label({"expiry": 1796047200}) == "cNGN-30NOV26"
assert series_label({"expiry": 1796047200, "baseSymbol": "EURC"}) == "EURC-30NOV26"
# EURC_FUTURE_V2.json names its series in `symbol` and has no baseSymbol: read as cNGN it would share cNGN's label
assert series_label({"expiry": 1796054400, "symbol": "EURC-30NOV26"}) == "EURC-30NOV26"
# What the run read goes to the state file: each manager once, each future asset (v1's, then every v2 series).
watched: dict = {}
record_watched(watched, {"manager": "0xv1", "future": "0xf1"})
Expand Down
7 changes: 4 additions & 3 deletions contracts/risk-core/scripts/ops/engine-keeper-v2.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# from SSM in the unit's ExecStart. Addresses from risk-core deployments/8453/MARGIN_ENGINE_V2.json. v1's engine keeper
# (engine-keeper.env) moved here that day and is disabled: v1's USDC-cNGN-30NOV26 has no liquidator until it settles.
#
# Per engine, not per series: a v2 series (EURC-30NOV26 once listed) is one more address in FUTURE.
# Per engine, not per series: a v2 series is one more address in FUTURE.
CHAIN_ID=8453
# The keeper's funding account under v2, created by its EOA 0x753f…f83b (SubAccounts.createAccount(eoa, 0x0705…0950))
# and funded with v1 #33's 5,000 USDC cash, 2026-10-28.
Expand All @@ -12,8 +12,9 @@ SUB_ACCOUNTS=0x7019244E25FA416e6Ca2ed2F3cA25277aef72843
MANAGER=0x0705219a1594d5Fc5dC2573da4082cAf3e1B0950
AUCTION=0xd07176B006cE8246e930d37b8bc44B1154f8c4cE
CASH=0xA74E49b4Ed7cb176bc02ef4D8a1A3240C9aD4272
# No PERP: v2 lists none. Comma-separated, one per series.
FUTURE=0xcACEe66ad41fD1472a47D7D5Fe372FFbFac9AA75
# No PERP: v2 lists none. Comma-separated, one per series: cNGN-30NOV26, then EURC-30NOV26 (EURC_FUTURE_V2.json,
# added 2026-10-30 after its setup landed).
FUTURE=0xcACEe66ad41fD1472a47D7D5Fe372FFbFac9AA75,0x6Ff7084D6F1760a57b9475a41F54b12279cD7744
POOL_ACCOUNT=37
SECURITY_MODULE_ACCOUNT=21
# v2's deploy (2026-10-27, blocks 53164124–53164160): no account was under it before.
Expand Down
35 changes: 35 additions & 0 deletions contracts/risk-core/scripts/ops/future-feeds-v2-eurc-30nov26.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# /etc/numo/future-feeds-v2-eurc-30nov26.env ([email protected]), for EURC-30NOV26 on
# MarginEngine v2: from risk-core/deployments/8453/EURC_FUTURE_V2.json (deployed 2026-10-30, setup landed the same
# day). The box's copy adds DRY_RUN. A change here is copied to the box and recorded in OPS_BOX_CHANGES.md the same
# day. Holds no secret.
CHAIN_ID=8453
FUTURE_VERSION=v2
BASE_SYMBOL=EURC
DATA_SUBMITTER=0xe0C06DD245f1e8C8bC516c66C66e64648987F912
# The EURC spot feed (USD per EURC, Chainlink EURC/USD), v2 market 2's spot feed: read as is.
INDEX_FEED=0xaaE62Ae5df3747B8f43F895561043FB77D97C435
FORWARD_FEED=0xB67fCd9A66efc3C12Be3Ea2bBB97385004394d14
FUTURE_ASSET=0x6Ff7084D6F1760a57b9475a41F54b12279cD7744
# The expiry, 2026-11-30 16:00 UTC: two hours after cNGN-30NOV26, so the two series do not share a sub id.
FUTURE_SUB_ID=1796054400
# MarginEngine v2. Its VM pool is account #37, cash only, shared with cNGN-30NOV26.
FUTURE_MANAGER=0x0705219a1594d5Fc5dC2573da4082cAf3e1B0950
# Annual EUR-over-USD carry, 18dp, signed: -1.5% (an assumption, ECB ~2% against fed funds ~3.5%), a forward
# premium. The figure the deploy's initial mark was checked against (EURC_FUTURE_V2.json carry).
CARRY=-15000000000000000
# The forward KMS key (0xd65F…3650), the feed's only signer: each feed's EIP-712 domain binds signatures to it.
FORWARD_SIGNER_KMS_KEY_ID=arn:aws:kms:us-east-1:957261948743:key/4312f074-ddcb-4db3-89d5-02d042beaacd
# A relayer of this instance's own (run-with-ssm-forward.sh): sharing cNGN's would race it for nonces.
FORWARD_RELAYER_SSM_NAME=forward_relayer_key_v2_eurc
MARKETS_SERVICE_URL=https://api.numofx.com
FORWARD_TICK_MS=60000
# Each side of the futures book must fill this much (USDC) before it sets the mark; else carry. A contract is 1,000
# EURC, ~$1,120: about 2 contracts a side.
FORWARD_MIN_BOOK_DEPTH_USDC=2000
# The contract's own band (3% narrowing to 0.5% over 14 days) is read from the chain and clamps every mark.
FORWARD_MAX_BASIS_BPS=300
FORWARD_SAFETY_BPS=50
FORWARD_MARK_LATE_MS=7200000
FIXING_SAMPLE_INTERVAL_MS=300000
CLOSE_OUT_BATCH=20
SETTLE_PER_TICK=10
2 changes: 2 additions & 0 deletions contracts/risk-core/scripts/ops/perp-pager.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ KEEPER_ACCOUNT=23
# keeper-unhealthy-v2 / insolvent-account-v2 for it. v2's futures are read from
# deployments/8453/MARGIN_ENGINE_V2.json by default (FUTURE_V2_ARTIFACTS, comma-separated, adds a series).
ENGINE_V2_KEEPER_HEALTH_URL=http://127.0.0.1:9466/health
# Every v2 series, one artifact each, from the pager's own checkout: cNGN-30NOV26 and EURC-30NOV26 (2026-10-30).
FUTURE_V2_ARTIFACTS=/home/ec2-user/exchange-pager/contracts/risk-core/deployments/8453/MARGIN_ENGINE_V2.json,/home/ec2-user/exchange-pager/contracts/risk-core/deployments/8453/EURC_FUTURE_V2.json
6 changes: 5 additions & 1 deletion contracts/risk-core/test/fork/EurcFutureV2Fork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,12 @@ contract EurcFutureV2Fork is Test {
uint nonce;
IEMatching matching;

// The block before EURC-30NOV26's deploy (2026-10-30, 53273869): its setup has since landed, and the deploy refuses an
// engine whose EUR market already lists a future, so the suite rehearses from just before it.
uint constant FORK_BLOCK = 53_273_868;

function setUp() public {
vm.createSelectFork(vm.envString("BASE_RPC_URL"));
vm.createSelectFork(vm.envString("BASE_RPC_URL"), FORK_BLOCK);
script = new DeployEurcFutureV2();
params = script.loadChainParams();
params.forwardSigner = vm.addr(forwardPk);
Expand Down
10 changes: 10 additions & 0 deletions infra/aws/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,16 @@ variable "future_series" {
base_asset = "cNGN"
contract_size = "1000000"
amount_scale = "1000000000000000" # 1,000 cNGN: 1e18 per 1,000,000 cNGN contract
}, {
# EURC-30NOV26 (risk-core EURC_FUTURE_V2.json), listed closed 2026-10-30: USDC per EURC, 1,000 EURC a contract,
# 16:00 UTC so its sub id is not cNGN's.
asset_address = "0x6Ff7084D6F1760a57b9475a41F54b12279cD7744"
expiry = 1796054400
manager_address = "0x0705219a1594d5Fc5dC2573da4082cAf3e1B0950"
kind = "linear"
base_asset = "EURC"
contract_size = "1000"
amount_scale = "1000000000000000" # 1 EURC: 1e18 per 1,000 EURC contract
}]

validation {
Expand Down
Loading