Skip to content

Always require funder auth - #21

Merged
leighmcculloch merged 7 commits into
mainfrom
always-require-funder-auth
Jul 29, 2026
Merged

leighmcculloch merged 7 commits into
mainfrom
always-require-funder-auth

Conversation

@leighmcculloch

@leighmcculloch leighmcculloch commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Warning

Intended to merge to main after #22.

What

Drop the amount > 0 branches so that opening a channel and topping one up always require the funder's authorization.

Why

Keep the channel contracts consistent on when auth is required, so they require auth on the same functions every time even in the zero case. Originally this was left off on the idea that someone else might create the channel first, but this is not a realistic expectation because the commitment key must be known at channel open time. So either way the funder has to be involved and there must be coordination.

The SAC's own transfer authorizes and transfers unconditionally at zero amounts, so matching it removes branches and the assumptions those branches break when the channel is composed with other contracts. In the factory the funder has to sign regardless, since they supply their own commitment key.

Copilot AI review requested due to automatic review settings July 28, 2026 23:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens authorization semantics across the channel and channel-factory Soroban contracts by requiring explicit funder authorization even when the deposit/top-up amount is zero, and adds tests/snapshots to enforce this behavior.

Changes:

  • Require funder authorization for zero-amount top_up and initial deposit during __constructor.
  • Require funder authorization for zero-amount channel deployments via the factory open.
  • Add/refresh tests and snapshot fixtures to assert the expected auth invocation trees for zero-amount operations.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
README.md Updates user-facing docs to reflect that channel opening is deployer-initiated but funder-authorized (no zero-amount auth bypass).
contracts/channel/src/lib.rs Makes top_up require funder auth regardless of amount; constructor docs updated accordingly.
contracts/channel/src/test.rs Adds/updates tests asserting auth is required for zero-amount top_up and zero-amount channel construction.
contracts/channel/test_snapshots/test/test_top_up_zero.1.json Updates snapshot to include funder auth + transfer(0) sub-invocation.
contracts/channel/test_snapshots/test/test_open_zero_amount.1.json New snapshot covering channel construction with zero amount requiring auth.
contracts/channel-factory/src/lib.rs Makes factory open always require funder auth (including for amount 0) and updates docs.
contracts/channel-factory/src/test.rs Adds test asserting auth tree for factory open with zero initial deposit.
contracts/channel-factory/test_snapshots/test/test_open.1.json Updates snapshot due to auth/wasm/code changes.
contracts/channel-factory/test_snapshots/test/test_open_zero_amount.1.json New snapshot covering factory open with zero amount requiring auth.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread contracts/channel/src/lib.rs Outdated
Comment on lines +317 to +323
pub fn top_up(env: &Env, amount: i128) {
assert_with_error!(env, amount >= 0, Error::NegativeAmount);
if amount > 0 {
// Transfer tokens from the funder to the channel.
let from = Self::from(env);
from.require_auth();
Self::token_client(env).transfer(&from, &env.current_contract_address(), &amount);
}

// Transfer tokens from the funder to the channel.
let from = Self::from(env);
from.require_auth();
Self::token_client(env).transfer(&from, &env.current_contract_address(), &amount);
@leighmcculloch leighmcculloch changed the title Require funder authorization for zero-amount channel operations Always require funder auth and transfer Jul 29, 2026
@leighmcculloch
leighmcculloch force-pushed the always-require-funder-auth branch from f985205 to fa474e4 Compare July 29, 2026 00:19
@leighmcculloch
leighmcculloch changed the base branch from main to bump-ethnum-fix-build July 29, 2026 00:19
Comment on lines +318 to -321
let from = Self::from(env);
from.require_auth();
if amount > 0 {
// Transfer tokens from the funder to the channel.
let from = Self::from(env);
from.require_auth();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the change here could go a couple different ways:

  • It could do as is currently done here, always require auth, and only transfer on > 0.
  • It could always transfer too.
  • It could do nothing new, keep the behaviour the same for top_up, and the __constructor could get a from.require_auth() always instead, and in this way top_up would not require auth when 0, although that seems like an odd case to cater for.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • It could always transfer too.

Nah, an extra no-op cross-contract call is expensive and undesirable here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the top_up function accepting amount == 0 here just so the constructor can call it with zero initial balance?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so, I considered what if we changed it and the constructor did the initial conditioned topup itself and topup always did a transfer, but given that changes behaviour in a larger way wasn't clear we should do that at this stage.

@leighmcculloch leighmcculloch changed the title Always require funder auth and transfer Always require funder auth Jul 29, 2026
Base automatically changed from bump-ethnum-fix-build to main July 29, 2026 21:26

@marcelosalloum marcelosalloum left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! A squatter can no longer create a from-bound channel at the deterministic address without from's signature, even at amount == 0.

Comment on lines +318 to -321
let from = Self::from(env);
from.require_auth();
if amount > 0 {
// Transfer tokens from the funder to the channel.
let from = Self::from(env);
from.require_auth();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • It could always transfer too.

Nah, an extra no-op cross-contract call is expensive and undesirable here.

Comment on lines +318 to -321
let from = Self::from(env);
from.require_auth();
if amount > 0 {
// Transfer tokens from the funder to the channel.
let from = Self::from(env);
from.require_auth();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the top_up function accepting amount == 0 here just so the constructor can call it with zero initial balance?

@leighmcculloch
leighmcculloch force-pushed the always-require-funder-auth branch from 92a6c04 to bd88100 Compare July 29, 2026 23:04
@leighmcculloch
leighmcculloch force-pushed the always-require-funder-auth branch from bd88100 to 4e9e9c3 Compare July 29, 2026 23:10
@leighmcculloch
leighmcculloch merged commit 25dea1b into main Jul 29, 2026
9 checks passed
@leighmcculloch
leighmcculloch deleted the always-require-funder-auth branch July 29, 2026 23:14
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.

3 participants