Skip to content

feat: Aptos support#626

Open
karim-en wants to merge 17 commits into
mainfrom
aptos
Open

feat: Aptos support#626
karim-en wants to merge 17 commits into
mainfrom
aptos

Conversation

@karim-en

@karim-en karim-en commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

feat: Aptos support

Adds the Aptos side of the Omni Bridge — a complete Move package that
mirrors the EVM and Starknet implementations — plus the NEAR-side
ChainKind::Aptos wiring so transfers can flow end-to-end.

What's in this PR

Aptos Move package (aptos/)

Module Purpose
omni_bridge::omni_bridge Main contract — initialize, init_transfer, fin_transfer, deploy_token, log_metadata, set_token_metadata, pause, role management, views
omni_bridge::bridge_token Fungible Asset wrapper holding MintRef/BurnRef/TransferRef/MutateMetadataRef as a private resource; package fun interface
omni_bridge::bridge_types Payload structs + Borsh encoders byte-compatible with Starknet
omni_bridge::borsh Borsh sequence helpers (encode_string, encode_byte_vec); primitives delegate to bcs::to_bytes
omni_bridge::utils Eth-style ECDSA verification via aptos_std::secp256k1 + keccak256

NEAR-side integration

  • ChainKind::Aptos = 13 and OmniAddress::Aptos(AptosAddress) (H256 alias)
  • OmniAddress::FromStr accepts "aptos:0x..."
  • get_native_token_address(Aptos) returns the canonical APT FA metadata address (0xa)
  • Factory address helper for integration tests

CI

Architecture highlights

  • Named-object pattern: BridgeState lives on a deterministic Object owned by @omni_bridge (seed b"omni_bridge::state"). disable_ungated_transfer pins it permanently — deployer-key compromise can't move locked funds.
  • Role-based access control: Table<u8, vector<address>> — three roles (Admin, Pauser, MetadataAdmin) with multi-holder support, generic grant_role/revoke_role, last-admin guard prevents bricking. Discoverable off-chain via all_roles().
  • Deterministic FA addresses: raw NEAR token id used as the create_named_object seed — no keccak (Aptos's seed is arbitrary-length, unlike Starknet's felt252).
  • Capability confinement: token mint/burn/metadata refs are held in a private resource on each FA object, accessible only via package fun from the omni_bridge package.

Borsh compatibility

The Borsh payload encoding is byte-identical to the Starknet variant so the NEAR MPC signature works without per-chain branching on the NEAR side:

  • 32-byte big-endian addresses
  • Destination chain id interleaved as the OmniAddress tag before token_address and recipient
  • Destination chain id is bound into the hash (not the payload) → prevents cross-chain replay
  • message field deliberately untagged (matches Starknet)

Security invariants

  • No replay: destination_nonce checked + marked before any token transfer; origin_nonce incremented atomically
  • State before external calls: pause/nonce/role checks all happen before mint or transfer
  • No token release without signature: every mint/transfer path goes through eth-signature verification
  • Event completeness: InitTransfer carries every field the NEAR side needs to reconstruct the transfer

Roles & operational model

Role Authority
Admin Rotate any role, change near_bridge_derived_address, set pause flags
Pauser pause_all only
MetadataAdmin set_token_metadata (icon URI / project URI)

Each role accepts an unbounded list of holders. Suggested ops setup: cold-multisig Admin, warm-multisig Pauser, smaller-key MetadataAdmin.

Testing

35 unit tests covering:

  • Borsh encoding edge cases (length-prefix exhaustiveness, empty inputs, multi-byte lengths)
  • Payload Borsh layout (offsets, dual chain-id tag, Option semantics)
  • Nonce bitmap (word-boundary at 127/128, idempotent mark)
  • Pause flags + access control
  • Role grant/revoke (idempotent grant, last-admin guard, admin step-down)
  • Metadata mutation (admin only, bridge-tokens only)
  • Bridge token mint/burn round-trip
  • Signature verification negatives (invalid sig, wrong length)
  • init_transfer (7 tests): bridged-token burn, non-bridge-token lock, nonce increment, paused, zero amount, fee≥amount, amount>u64::MAX

Run with:

aptos move test --named-addresses omni_bridge=0xCAFE

@karim-en

karim-en commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merged origin/main into the aptos branch and resolved the three conflicts in near/omni-types/src/lib.rs, near/omni-types/src/tests/lib_test.rs, and near/omni-tests/src/omni_token.rs. The key resolution was assigning Fogo to chain index 12 (as it was already in main) and Aptos to chain index 13, with both variants included across all affected match arms and test fixtures (commit d54a011).

@karim-en karim-en marked this pull request as ready for review June 5, 2026 17:03
@karim-en karim-en requested a review from a team June 5, 2026 17:03
Comment thread aptos/sources/utils.move Outdated
/// Recovered Ethereum address does not match the expected signer.
const E_INVALID_SIGNATURE: u64 = 3;

const MAX_ALLOWED_DECIMALS: u8 = 18;

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.

With u64 to store balances this is incompatible. I suggest we use the standard 8 decimals

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.

Comment thread aptos/sources/omni_bridge.move
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