Skip to content

PoC for external id#5

Closed
karim-en wants to merge 21 commits intoNear-Bridge-Lab:mainfrom
Near-One:external-id
Closed

PoC for external id#5
karim-en wants to merge 21 commits intoNear-Bridge-Lab:mainfrom
Near-One:external-id

Conversation

@karim-en
Copy link
Copy Markdown
Collaborator

@karim-en karim-en commented Apr 7, 2026

No description provided.

frolvanya and others added 21 commits August 18, 2025 16:31
Allow specifying Max Gas Fee during transfer initiation
feat: added ci to update contracts
* provide metadata for nBTC

* fix satoshi bridge cargo toml

* address -> script_pubkey

* btc-p2wpkh -> utxo_chain

* remove btc_network

* draft zcash tx impl

* fix consenses decode

* zcash_primitives

* fix cargo toml

* test for script

* fix get output

* change signhash

* Refactor transaction type

* Bump zcash_primitives version

* Convert btc tx into zcash tx

* Set `final_script_sig`

* Fix sighash

* fix

* fix sequence

* zcash build

* add zcash features

* AddressInner -> Address

* new line

* test parse address

* test from pubkey

* improve tests

* fix warnings

* check zcash fee

* safe expiry height

* fix expiry_height

* add rbf

* fix sequence

* extract expire_height for cancel_withdraw

* fix cancel active utxo managment

* fix active utxo managment

* utxo_mamagment extract block height

* gap into config

* Replace last_block_header with last_block_height

* construct output for zcash

* add simple try from address

* parse unified address

* script pubkey

* fmt for unified address

* fix find receiver in list

* fix warnings

* safe psbt wraper

* deserialize

* move het hash to sign

* extract tx byte

* save signature

* incapsulate psbt

* separate transaction

* separate expiry hight

* separate psbt creation

* separate chain specific functions

* separate zcash functions

* separate zcash

* fix btc

* fix warnings

* optimize get min fee

* remove psbt for zcash

* remove unsed function

* add get_input_num and get_output_num

* fix satoshi bridge

* fix new

* make psbt field private

* reduce copy past

* reduce copypast

* fix test contract config for zcash

* don't panic

* fmt

* reduce include

* updaye zcash_protocol

* fix chain id

* remove default

* add version

* fix bitcoin connector

* remove bytes_to_btc_transaction

* don't use fully-qualified name

* fix bitcoin tests

* fix check block heigth for branch id

* save block height

* fix

* fix make

* fix valid confiramtions range

* Bump version

* fix: use `U128` type for amount in `ft_on_transfer_callback`

* provide max gas fee

---------

Co-authored-by: karim-en <[email protected]>
Co-authored-by: Ivan Frolov <[email protected]>
* feat: implement safe deposit

* Fix burn

* Replace `{{UTXO_TX_ID}}`

* Remove safe_burn

* Storage deposit on init

* Fix after merge

* Add `safe_deposit` field

* Fix safe deposit with the new safe_deposit message

* Fix tests and lint

* Bump version
* Fix config migration

* Add upgrade tests

* Fix testnet migration
* chore: added ci

* chore: allow `needless_pass_by_value`

* fix: ci

* fix: fixed clippy issues and loosened clippy rules

* chore: added `make build` call

* chore: install cargo-near

* fix(tests): nbtc contract initialization

* chore: cleanup

* Update contracts/satoshi-bridge/tests/setup/context.rs

Co-authored-by: Olga Kunyavskaya <[email protected]>

* Update contracts/satoshi-bridge/src/nbtc/burn.rs

Co-authored-by: Olga Kunyavskaya <[email protected]>

* Update contracts/satoshi-bridge/src/nbtc/burn.rs

Co-authored-by: Olga Kunyavskaya <[email protected]>

* Update contracts/satoshi-bridge/src/btc_light_client/mod.rs

Co-authored-by: Olga Kunyavskaya <[email protected]>

* refactor: use `u64::from(u32::MAX)` instead

* refactor: use `u64::MAX` instead

* Revert "refactor: use `u64::MAX` instead"

This reverts commit 4c53c2c.

---------

Co-authored-by: Olga Kunyavskaya <[email protected]>
* improve utxo_id injection

* Remove unused code

* check field value

* Revert "check field value"

This reverts commit 3312dcd.

* Bump version

* Remove extra prints

---------

Co-authored-by: Olga Kunyavskaya <[email protected]>
* fix: `utxo_id` injection in nested json

* chore: fmt

* chore: simplify test

* fix: escaped json

* fix: clippy

* fix: use enum

* feat: removed escaped json

* feat: added already set utxo id test

* chore: bumped version
* create proposal script

* fix cmp

* check proposal script

* add tmp folder

* improve view command

* update migration proposal

* Revert "update migration proposal"

This reverts commit fbd5c0f.

* NEAR_NETWORK VAR

* update hash

* check_proposal script

* add proposal id in check proposal

---------

Co-authored-by: karim-en <[email protected]>
* Activate NU6.1 on mainnet

* Bump version
* provide metadata for nBTC

* fix satoshi bridge cargo toml

* address -> script_pubkey

* btc-p2wpkh -> utxo_chain

* remove btc_network

* draft zcash tx impl

* fix consenses decode

* zcash_primitives

* fix cargo toml

* test for script

* fix get output

* change signhash

* Refactor transaction type

* Bump zcash_primitives version

* Convert btc tx into zcash tx

* Set `final_script_sig`

* Fix sighash

* fix

* fix sequence

* zcash build

* add zcash features

* AddressInner -> Address

* new line

* test parse address

* test from pubkey

* improve tests

* fix warnings

* check zcash fee

* safe expiry height

* fix expiry_height

* add rbf

* fix sequence

* extract expire_height for cancel_withdraw

* fix cancel active utxo managment

* fix active utxo managment

* utxo_mamagment extract block height

* gap into config

* Replace last_block_header with last_block_height

* construct output for zcash

* add simple try from address

* parse unified address

* script pubkey

* fmt for unified address

* fix find receiver in list

* fix warnings

* safe psbt wraper

* deserialize

* move het hash to sign

* extract tx byte

* save signature

* incapsulate psbt

* separate transaction

* separate expiry hight

* separate psbt creation

* separate chain specific functions

* separate zcash functions

* separate zcash

* fix btc

* fix warnings

* optimize get min fee

* remove psbt for zcash

* remove unsed function

* add get_input_num and get_output_num

* fix satoshi bridge

* fix new

* make psbt field private

* reduce copy past

* reduce copypast

* fix test contract config for zcash

* don't panic

* fmt

* reduce include

* updaye zcash_protocol

* fix chain id

* remove default

* add version

* fix bitcoin connector

* remove bytes_to_btc_transaction

* don't use fully-qualified name

* fix bitcoin tests

* fix check block heigth for branch id

* save block height

* fix

* fix make

* fix valid confiramtions range

* Bump version

* fix: use `U128` type for amount in `ft_on_transfer_callback`

* provide max gas fee

* Add orchard support

* Add comments

* Add comment

* fix outputs checks

* Calculate sighash with `orchard_bundle`

* Implement Orchard Bundle Validation for Zcash Withdrawals (#20)

* feat: zcash validation

* fix: use ZIP-317 formula with orchard action count for fee calculation

Previously, get_min_fee() hardcoded 0 for orchard_action_count when
calling the ZIP-317 fee_required function. This resulted in incorrect
fee calculations for transactions with Orchard bundles.

Now properly counts actions from the orchard bundle (if present) and
passes this to the ZIP-317 FeeRule. The formula accounts for:
- Transparent inputs (P2PKH standard size)
- Transparent outputs (serialized size)
- Orchard actions (actual count from bundle)

The fee calculation varies with transaction structure per ZIP-317
specification - 10,000 satoshis is just the minimum for simple
transactions with minimal inputs/outputs.

* fix: replace hardcoded gas fee with ZIP-317 calculation

Previously used hardcoded 10000 satoshis as default gas fee. Now:

1. Calculate actual fee using ZIP-317 formula via psbt.get_min_fee()
2. Use max_gas_fee as an upper bound when provided
3. Fall back to computed fee when max_gas_fee not provided

This properly accounts for transaction structure (inputs, outputs,
orchard actions) per ZIP-317 spec. The fee varies with tx complexity
rather than using a fixed minimum value.

Changes in contract_methods.rs:
- Create preliminary PSBT to compute fee before validation
- Recreate PSBT with correct expected amounts for validation

Changes in token_receiver.rs:
- Compute ZIP-317 fee and use max_gas_fee as cap

* fix: update minimum orchard actions check from 1 to 2

Per Orchard protocol specification, bundles must contain at least 2
actions for privacy (MIN_ACTIONS = 2). The Orchard builder
automatically pads bundles with dummy actions to meet this minimum.

Changes:
- Added MIN_ACTIONS constant (2) in orchard_policy.rs
- Updated validation to require >= MIN_ACTIONS instead of == 1
- Applied to both strict validation (with expected values) and
  looser validation (internal operations)
- Improved error messages to show expected vs actual action count

Reference: https://github.com/zcash/orchard/blob/main/src/builder.rs#L36

* fix: use base64 encoding only for ZCash to preserve Bitcoin compatibility

Previously, all chains used base64 encoding for tx_bytes in the
SignedBtcTransaction event, which breaks backward compatibility
for Bitcoin indexers expecting hex encoding.

Now:
- ZCash chains (mainnet/testnet): Use base64 (1.33x overhead)
  - Beneficial for larger Orchard transactions
- Bitcoin/other chains: Use hex encoding (backward compatible)

The event field name remains `tx_bytes_base64` but contains hex
for non-ZCash chains to avoid breaking the event structure.

* fix: validate Unified Address for actual Orchard receiver presence

Previously, the code checked if an address starts with "u1" or "u"
to determine if an Orchard bundle is required. This is incorrect
because Unified Addresses can contain only transparent receivers
(P2PKH/P2SH), which is a valid scenario without an Orchard bundle.

Now:
- Added has_orchard_receiver() helper to check if a Unified Address
  actually contains an Orchard receiver
- Only require Orchard bundle if the UA has an Orchard receiver
- Allow Unified Addresses with only transparent receivers to work
  without an Orchard bundle

Transparent addresses can be extracted from Unified Addresses,
which is a valid use case.

* fix: reject mixed Orchard and transparent output scenarios

Previously, the code would crash when a Unified Address with only
an Orchard receiver was provided along with transparent change outputs.
The transparent validation path would fail trying to extract a
transparent script_pubkey from an Orchard-only UA.

Now:
- Added has_orchard_bundle() helper to PsbtWrapper
- Explicitly reject transactions with both Orchard bundle and
  transparent outputs
- Require either pure Orchard (no transparent outputs) or pure
  transparent (no Orchard bundle)

This prevents the classic case where target has Orchard receiver
but change goes to transparent output, which would cause the
program to crash.

The restriction simplifies validation logic and avoids edge cases.
Users must choose one path or the other for clarity.

* fix: validate actual Orchard output amount from bundle

Previously, for Orchard-only withdrawals, the code calculated what the output amount *should* be but didn't validate the actual amount in the Orchard bundle. This was a critical security issue - users could provide any amount in their Orchard bundle.

Now:
- Added get_orchard_output_amount() to PsbtWrapper that recovers the actual output amount using the bridge OVK
- Validate that the recovered Orchard amount is within the expected range (accounting for fees and potential dust adjustment)
- Use the same validation logic as transparent outputs: allow a range between (expected - min_change_amount) and expected

The solution deserializes and validates the Orchard output amount, ensuring users receive the correct amount after fees.

* fix: support mixed Orchard output with transparent change (baseline case)

Previously, commit 9ce848d rejected mixed scenarios (Orchard + transparent),
but this was incorrect. The baseline scenario for Orchard withdrawals is:
- 1 Orchard output to the user's Unified Address
- 1+ transparent change outputs back to the bridge

Now properly supports mixed scenarios:
- Validate the Orchard output amount (to user)
- Validate all transparent outputs are change to bridge address
- Ensure change amounts are within min/max limits
- Verify change + gas_fee doesn't exceed input

The new validation logic:
- Orchard bundle present: Validate Orchard output + transparent change
- No Orchard bundle: Use existing transparent-only validation

* fix: comprehensive Orchard bundle validation improvements

Fixed 4 critical validation gaps identified during code review:

1. **Value Balance Validation** (NEW)
   - Now validates bundle.value_balance() equals -(output_amount)
   - Ensures value flows correctly from transparent to Orchard pool
   - Prevents malicious bundles with incorrect value balance

2. **All Outputs Validated** (NEW)
   - Iterates through ALL actions/outputs (not just first)
   - Ensures exactly 1 non-zero output exists
   - Prevents hiding extra outputs in unused actions

3. **Range Validation for Dust Adjustment** (FIXED)
   - Changed from exact match to range validation
   - Allows user to adjust amount for dust (like transparent does)
   - Range: [expected - min_change_amount, expected]
   - Provides same flexibility as transparent withdrawals

4. **Exact Transparent Accounting** (FIXED)
   - Changed `<=` to `==` in transparent change validation
   - Validates: input == orchard_amount + change + gas_fee
   - Prevents missing value in accounting
   - Ensures value balance integrity

These fixes provide defense-in-depth. While the ZCash network would
catch most issues, we now validate comprehensively at contract level
to reject bad requests early and ensure robust accounting.

* tmp contract fix

* Min Actions = 1

* safe output

* remove unused function

* separate validation

* merge checks

* fix add extra amount

* remove unused function

* remove changes

* fmt

* remove amount checks

* remove no needed check

* move orchard validation into check function

* remove gas calculation

* provide expire height

* fix get_branch_id

* reduce copypast

* event beckword compatibility

* fix warnings

* clippy

* fix compilation for bitcoin

* fmt

* fix: add expiry_height field to tests

Add missing expiry_height: None to TokenReceiverMessage::Withdraw
in test files after the field was added to the struct.

* save target_address

* orchard bundle in RBF

* chain specific data

* String -> Base64

* remove dublicate code

* return Error on Orchard Bundle

* check the expected actions num

* clean up

* save recipient_address to psbt

* check version

* fix tests compilation

* fix: update fixtures, exports, and tests

* fix: replace unwrap() with descriptive error messages

- bitcoin_utils/psbt_wrapper: add ERR_INVALID_PSBT_HEX, ERR_EXTRACT_TX,
  ERR_MISSING_WITNESS_UTXO, ERR_SIGHASH messages
- zcash_utils/psbt_wrapper: add ERR_INVALID_ORCHARD_BUNDLE,
  ERR_ORCHARD_VALIDATION, and detailed ERR_INVALID_PSBT messages for
  all deserialization steps
- config: add ERR_CONFIG messages for uninitialized contract/config
- account: include account_id in ERR_ACCOUNT_NOT_REGISTERED message

* provide chain_specific data

* remove unnecessary .clone() calls

* add type alias for Orchard raw address bytes and simplify extract_orchard_bundle

* replace expect() with env::panic_str() to reduce contract size

* Update contracts/satoshi-bridge/src/network.rs

Co-authored-by: Ivan Frolov <[email protected]>

* chore: imported `OrchardRawAddress`

---------

Co-authored-by: Olga Kunyavskaya <[email protected]>
Co-authored-by: Ivan Frolov <[email protected]>
Co-authored-by: Ivan Frolov <[email protected]>

* Avoid extra clone of Orchard bundle

* Replace panic with panic_str

* Bump version

* Fix clippy

* Fix build variant

* Fix orchard tests

* Run lint and tests to CI

* Revert default

* Remove added ci

* Remove unused fmt

* Fix tests

* Fix orchard test

* Fix orchard test

* Add missed `safe_deposit`

* add different wasm path

* fix orchard tests

* fix test_base

* fix check predecessor account id

* fix test

* fix check outputs number

* unwrap -> expect

* extract expiry height check

---------

Co-authored-by: Olga Kunyavskaya <[email protected]>
Co-authored-by: Ivan Frolov <[email protected]>
Co-authored-by: r-near <[email protected]>
Co-authored-by: Ivan Frolov <[email protected]>
* add claude

* add AGENTS.md

* update CLAUDE

* reduce Claude.md

* reduce Claude.md

* remove before suggestin section

* remove key contract section

* Safe function section

* remove code example

* remove reference

* remove command for production relese

* remove dublicate

* fix state managment

* remove dublicate

* Design Decission
* chore: updated `near-sdk` and `near-contract-standards` versions

* feat: added trusted relayer

* chore: added comment about account id conversion

* chore: fmt

* fix: tests

* fix(tests): grant `UnrestrictedRelayer` role

* feat: made `verify_active_utxo_management` callable only by trusted relayer

* feat: covered max result length with tests

* feat(tests): covered that random account can't call trusted relayer only methods

* chore: bumped bridge version
@karim-en karim-en closed this Apr 7, 2026
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