Bug
When calling management_canister.create_canister({...}).with_cycles(500_000_000_000) from a canister on mainnet, cycles are deducted from the caller canister but not deposited into the newly created canister. The new canister ends up with 0 cycles.
Workaround
Use an explicit deposit_cycles call after create_canister:
# Step 1: create (cycles here seem to be consumed but not transferred)
create_result = yield management_canister.create_canister(
{"settings": None}
).with_cycles(deploy_cycles)
# Step 2: explicitly deposit cycles into the new canister
deposit_result = yield management_canister.deposit_cycles(
{"canister_id": new_canister_id}
).with_cycles(deploy_cycles)
Root cause hypothesis
The with_cycles() method sets self.args[3] = cycles and self.payment = cycles, and the Rust shim in perform_service_call extracts payment via service_call.get_attr("payment").and_then(|p| p.extract_u64()). If extract_u64() fails silently for large Python ints (500B), it falls back to unwrap_or(0u64), meaning 0 cycles are attached.
Alternatively, call_raw in async_handler.rs also extracts cpython_get_arg(args, 3)?.extract_u64()? — need to verify which code path is actually used for @update generators.
Environment
- Deployer canister:
3dwln-xiaaa-aaaag-ayrva-cai (mainnet)
- ic-basilisk: 0.11.25
- Confirmed:
deposit_cycles.with_cycles() DOES work correctly for the same amount (500B)
- The issue appears to be specific to
create_canister or possibly other management canister methods
Impact
Any canister using management_canister.create_canister(...).with_cycles(N) will create canisters with insufficient funds, leading to failures on subsequent operations.
Bug
When calling
management_canister.create_canister({...}).with_cycles(500_000_000_000)from a canister on mainnet, cycles are deducted from the caller canister but not deposited into the newly created canister. The new canister ends up with 0 cycles.Workaround
Use an explicit
deposit_cyclescall aftercreate_canister:Root cause hypothesis
The
with_cycles()method setsself.args[3] = cyclesandself.payment = cycles, and the Rust shim inperform_service_callextracts payment viaservice_call.get_attr("payment").and_then(|p| p.extract_u64()). Ifextract_u64()fails silently for large Python ints (500B), it falls back tounwrap_or(0u64), meaning 0 cycles are attached.Alternatively,
call_rawinasync_handler.rsalso extractscpython_get_arg(args, 3)?.extract_u64()?— need to verify which code path is actually used for@updategenerators.Environment
3dwln-xiaaa-aaaag-ayrva-cai(mainnet)deposit_cycles.with_cycles()DOES work correctly for the same amount (500B)create_canisteror possibly other management canister methodsImpact
Any canister using
management_canister.create_canister(...).with_cycles(N)will create canisters with insufficient funds, leading to failures on subsequent operations.