Skip to content

feat!: sync ic.did to spec 0.70.0 with pay-as-you-go pricing_version (5.0.0) - #17

Merged
Kamirus merged 6 commits into
masterfrom
chore/upstream-sync-2026-09-21
Oct 28, 2026
Merged

Kamirus merged 6 commits into
masterfrom
chore/upstream-sync-2026-09-21

Conversation

@cursor

@cursor cursor Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Exposes the IC's pay-as-you-go HTTPS outcall pricing to Motoko callers. IC.HttpRequestArgs now carries pricing_version, so a canister can opt into version 2. Version 2 charges for the bytes and round-trip time a call actually uses and refunds the rest. Version 1 charges for max_response_bytes, which is 2 MB when unset. Types.mo is regenerated from the upstream ic.did through interface spec 0.70.0.

Breaking: record literals need the new fields

Motoko record literals must name every field, so new fields on records that callers build break compilation even when they're optional in Candid.

// Before
let request : IC.HttpRequestArgs = { url; method; headers; body; max_response_bytes; transform; is_replicated };

// After: `null` keeps pricing version 1
let request : IC.HttpRequestArgs = { url; method; headers; body; max_response_bytes; transform; is_replicated; pricing_version = null };

The same applies to the following:

  • IC.CanisterSettings and IC.DefiniteCanisterSettings: minimum_incoming_canister_call_cycles, log_memory_limit, and status_visibility.
  • IC.FetchCanisterLogsArgs: filter.
  • CanisterStatusResult.memory_metrics: log_memory_store_size.

IC.StatusVisibility spells the Candid public case as #public_, because public is a reserved word.

EcdsaCurve gains #secp256r1, so an exhaustive switch on it must handle the new case.

Additive

  • ic.flexible_http_request, plus IC.FlexibleHttpRequestArgs, FlexibleHttpRequestErr, FlexibleHttpRequestResult, and HttpRequestResourceReport. Its method variant covers #get, #head, #post, #put, #delete, and #patch.
  • ic.subnet_metrics with SubnetMetricsArgs and SubnetMetricsResult.
  • canister_info is a query on Types.Self.

What is unchanged

Call.httpRequest still takes IC.HttpRequestArgs and still attaches the version 1 (ic0.cost_http_request) cost. Set pricing_version = ?2 only after you compute and attach a version 2 reservation yourself. moc 2.1.0 exposes Prim.costHttpRequestV2, but this package doesn't call it. #16 rewrites Call.httpRequest around that primitive and can build on this types bump.

Toolchain

  • [toolchain] moc goes to 2.1.0. [requirements] moc stays at 1.4.0.
  • [moc].args drops --default-persistent-actors, because moc 2 removed the flag.
  • REGENERATING.md installs didc 0.6.2 from the GitHub release.

Merging this publishes ic 5.0.0 to mops automatically.

🤖 Generated with Claude Code

cursoragent and others added 3 commits September 21, 2026 06:08
Regenerate Types.mo from the upstream management canister interface
through spec 0.68.0. New record fields are breaking in Motoko because
literals must name every field.

Call.httpRequest is unchanged and still attaches the version-1 cost.

Co-authored-by: Kamil Listopad <listopadkamil@gmail.com>
The 0.6.2 binary has Float32 support; stop telling people to build
from source. Also record the moc 1.16.1 toolchain pin in AGENTS.md.

Co-authored-by: Kamil Listopad <listopadkamil@gmail.com>
moc 1.16 warns M0269 on .vals(); the headers walk in Cost.httpRequest
now uses .values().

Co-authored-by: Kamil Listopad <listopadkamil@gmail.com>
Kamirus added a commit that referenced this pull request Oct 9, 2026
The next version bump will fail to publish. `mops publish` runs the test
suite, and `mops-publish.yml` still installs the latest mops (3.x),
which dropped the dfx replica these tests need:

```
Tool 'pocket-ic' is not defined in [toolchain] section in mops.toml
Run mops toolchain use pocket-ic 15.0.0 to install it
```

[#18](#18) pinned mops
2.24.0 in `mops-test.yml` only, so PR checks pass while the publish job
is still exposed. That job hasn't run with mops 3 yet because no version
bump has landed since;
[#16](#16) and
[#17](#17) (both 5.0.0)
would be the first to hit it.

Moving these tests to mops 3 needs mops-side changes: configurable
test-canister cycles, auto-progress for HTTP outcalls, and a
`TestThresholdKeys` subnet. Out of scope here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
cursoragent and others added 3 commits October 12, 2026 06:10
subnet_metrics and EcdsaCurve.#secp256r1 are additive on the unpublished
5.0.0. Call.Cost.signWithEcdsa encodes the new curve as 1.

Pin moc 2.0.0 and drop --default-persistent-actors, which that compiler
rejects. mops test passes with the dfx replica.

Co-authored-by: Kamil Listopad <listopadkamil@gmail.com>
moc 2.1.0 is a minor ahead of the 2.0.0 toolchain pin and adds
Prim.costHttpRequestV2. mops test passes. No package version bump;
Call.httpRequest still attaches the version-1 cost.

Co-authored-by: Kamil Listopad <listopadkamil@gmail.com>
@Kamirus Kamirus changed the title feat!: sync ic.did to spec 0.68.0 (5.0.0) feat!: sync ic.did to spec 0.70.0 with pay-as-you-go pricing_version (5.0.0) Oct 28, 2026
@Kamirus
Kamirus marked this pull request as ready for review October 28, 2026 12:07
@Kamirus
Kamirus requested a review from a team as a code owner October 28, 2026 12:07
@Kamirus
Kamirus merged commit 7137d90 into master Oct 28, 2026
4 checks passed
Kamirus added a commit that referenced this pull request Nov 2, 2026
`Call.httpRequest` now attaches a pricing version 2 ("pay-as-you-go")
reservation instead of the version 1 cost. Version 1 charges for
`max_response_bytes`, which is 2 MB when unset. Version 2 charges for
the bytes and round-trip time a call actually uses and refunds the rest.
Costs come from `Prim.costHttpRequestV2`, which `moc` 2.1.0 exposes.

Supersedes [#16](#16),
rebuilt on the 5.0.0 types from
[#17](#17). The builder
design is from #16.

## Before / After

```motoko
// Before (5.0.0): version 1 pricing
let response = await Call.httpRequest(args);

// After: same arguments, version 2 pricing
let response = await Call.httpRequestFromArgs(args).send();

// Or build in place, and narrow the reservation
let response = await Call.httpRequest("https://example.com/api")
  .withMaxResponseBytes(4_000)
  .withExpectedRoundtripTimeMs(300)
  .send();

// New: flexible outcalls
let result = await Call.flexibleHttpRequest("https://example.com/api")
  .withReplication({ min_responses = 2; max_responses = 3; total_requests = 3 })
  .send();
```

The signature change is deliberate. Every call site fails to compile
until it migrates, rather than switching pricing silently.
`Call.Cost.httpRequest(args, expected)` now prices version 2. The
version 1 `Call.Cost.httpRequest(args)` is gone, but
`Prim.costHttpRequest` still exposes it.

`[requirements] moc` goes to 2.1.0. Merging publishes `ic` 6.0.0, so
this is a draft.

## Differences from #16

- **No `Prim.subnetSelfNodeCount`.** `moc` imports every system API it
references into each canister that imports `mo:ic/Call`, even a canister
that only calls `createCanister`. Replicas without
`ic0.subnet_self_node_count`, such as the ones in `dfx` 0.30.1 and
0.31.0, refuse to install those canisters. #16 used the node count only
to cap the default reservation when `replication` is unset. Without it,
such a flexible outcall reserves for full-size responses, and the
surplus is refunded. Setting `replication` restores the cap.
- **`#flexible_` instead of `#_351978059_`.** `moc` drops the trailing
underscore when it maps to Candid, the same escape `didc` uses for
`#public_`.
- **`replication` is passed through as given.** When it's unset, the
system applies its own defaults, the same as
`ic-cdk-management-canister`.
- **`Cost.httpRequestV2` is named `Cost.httpRequest`.** It takes a
second argument, so old call sites fail to compile.

## Why CI changed

The `dfx` 0.30.1 replica that CI uses implements an earlier draft of
`ic0.cost_http_request_v2`, with five parameters and no `outcall_type`.
It rejects the specified encoding:

```
Canister violated contract: Failed to decode HttpRequestV2CostParams from Candid
```

PocketIC 16.1.0 implements the spec. However, under `mops test` PocketIC
has no signing keys and makes no outcalls. The suite is now split:

- **`test` job:** runs `test/Call.test.mo` on `dfx` 0.30.1 for
`create_canister`, ECDSA, and Schnorr. The suite is unchanged except
that the HTTP tests moved out.
- **`test-pocket-ic` job:** runs `test/HttpRequest.test.mo` on PocketIC
16.1.0. It pins each builder's cost to `ic0.cost_http_request_v2` on the
parameters the spec defines, and checks that each setter moves the price
the right way.

Both jobs use `mops` 2.24.0. `mops publish` would run every test file on
a single replica, and neither replica passes both. So `mops-publish.yml`
now runs the same two commands, then `mops publish --no-test`. As a side
effect, mops.one won't show test results for 6.0.0.

No CI replica can both price version 2 and make an outcall, so nothing
here sends a request end to end. `send()` is `getCost()` plus the
management canister call.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Leo Eichhorn <leo.eichhorn@dfinity.org>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants