Skip to content

Conversation

@vaporif
Copy link
Collaborator

@vaporif vaporif commented Jan 5, 2026

Description

closes: #XXXX

  • New ics27-ift Solana program (transfer, mint, ack/timeout refunds)
  • Relayer IFT callback support
  • GMP callback forwarding to upstream callers
  • E2E tests for transfer, ack failure, and timeout flows
  • docs/adr/solana-ift-architecture.md
  • TODO: evm relayer module + e2e

Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Linked to GitHub issue with discussion and accepted design, OR link to spec that describes this work.
  • Wrote unit and integration tests.
  • Added relevant natspec and godoc comments.
  • Provide a conventional commit message to follow the repository standards.
  • Re-reviewed Files changed in the GitHub PR explorer.
  • Review SonarCloud Report in the comment section below once CI passes.

@codecov
Copy link

codecov bot commented Jan 5, 2026

Codecov Report

❌ Patch coverage is 45.84500% with 580 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.51%. Comparing base (956ac2a) to head (f31b496).

Files with missing lines Patch % Lines
...rograms/ics27-ift/src/instructions/ift_transfer.rs 23.88% 137 Missing ⚠️
...na/programs/ics27-ift/src/instructions/ift_mint.rs 0.00% 50 Missing ⚠️
programs/solana/programs/ics27-ift/src/lib.rs 2.32% 42 Missing ⚠️
.../programs/ics27-ift/src/instructions/initialize.rs 0.00% 41 Missing ⚠️
.../ics27-ift/src/instructions/register_ift_bridge.rs 0.00% 41 Missing ⚠️
...rams/ics27-gmp/src/instructions/callback_helper.rs 0.00% 38 Missing ⚠️
...ograms/ics27-ift/src/instructions/on_ack_packet.rs 7.50% 37 Missing ⚠️
programs/solana/programs/ics27-ift/src/gmp_cpi.rs 0.00% 32 Missing ⚠️
...ms/ics27-ift/src/instructions/on_timeout_packet.rs 0.00% 26 Missing ⚠️
programs/solana/programs/ics27-ift/src/helpers.rs 0.00% 24 Missing ⚠️
... and 12 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #873      +/-   ##
==========================================
+ Coverage   70.82%   71.51%   +0.69%     
==========================================
  Files          83      125      +42     
  Lines       14176    16219    +2043     
==========================================
+ Hits        10040    11599    +1559     
- Misses       4136     4620     +484     
Flag Coverage Δ
solana 69.48% <45.84%> (-1.35%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vaporif vaporif marked this pull request as ready for review January 12, 2026 21:11
@vaporif vaporif requested a review from srdtrk as a code owner January 12, 2026 21:11
@vaporif vaporif changed the title feat(solana): ift WIP feat(solana): IFT Jan 12, 2026
);

let discriminator = solana_sha256_hasher::hash(discriminator_name);
let mut ix_data = discriminator.to_bytes()[..8].to_vec();
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: you can use const for discriminator len

&[bump],
],
gmp_program,
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about using GMPAccount::pda() from solana-ibc-types/src/ics27.rs?

/// Whether bridge is active
pub active: bool,

pub _reserved: [u8; 64],
Copy link
Collaborator

Choose a reason for hiding this comment

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

In all apps we reserve 256 bytes. Is there any particular reason to reduce it for this case?

/// Transfer initiation timestamp
pub timestamp: i64,

pub _reserved: [u8; 32],
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

/// GMP program address for sending cross-chain calls
pub gmp_program: Pubkey,

pub _reserved: [u8; 128],
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

Copy link
Member

@srdtrk srdtrk left a comment

Choose a reason for hiding this comment

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

Initial review. I mostly looked at the docs and have some major questions

Comment on lines +430 to +432
GMP forwards `on_timeout_packet` and `on_acknowledgement_packet` to the original sender program when `remaining_accounts` are provided by the relayer.

The sender program ID is extracted from `GMPPacketData.sender`. GMP constructs the callback using Anchor's discriminator convention (`sha256("global:<instruction>")[..8]`) and invokes the sender program.
Copy link
Member

Choose a reason for hiding this comment

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

I thought these callbacks would be done in a separate instruction by the relayer and not in an extension an already existing instruction.

For example, IFT would have an instruction called on_ack_packet callback, and the relayer would be able to call it directly (access controlled so that only a relayer can call it). IFT would verify the validity of the callback via the state stored in GMP?

Comment on lines +436 to +441
Programs sending GMP packets that need callbacks must implement:

```rust
pub fn on_timeout_packet(ctx: Context<...>, msg: OnTimeoutPacketMsg) -> Result<()>
pub fn on_acknowledgement_packet(ctx: Context<...>, msg: OnAcknowledgementPacketMsg) -> Result<()>
```
Copy link
Member

Choose a reason for hiding this comment

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

How does the relayer know these instructions are implemented in general? (a non-IFT sender implementing this?)

| **Set Authority** | `initialize.rs` | Transfer mint authority to IFT PDA |
| **Create ATA** | `ift_mint.rs` | Create receiver's token account if needed |

### Operations Intentionally Not Used
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why this section is needed?

Comment on lines +38 to +49
## SPL Token Operations

### Operations Used

| Operation | Instruction | Usage |
|-----------|-------------|-------|
| **Burn** | `ift_transfer.rs` | Burn tokens when initiating cross-chain transfer |
| **Mint** | `ift_mint.rs` | Mint tokens to receiver on incoming transfer |
| **Mint** | `on_ack_packet.rs` | Refund on failed transfer (mint back to sender) |
| **Mint** | `on_timeout_packet.rs` | Refund on timeout (mint back to sender) |
| **Set Authority** | `initialize.rs` | Transfer mint authority to IFT PDA |
| **Create ATA** | `ift_mint.rs` | Create receiver's token account if needed |
Copy link
Member

Choose a reason for hiding this comment

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

So a couple questions:

  1. This contract is not an SPL token, but is meant to work with an existing one, right?
  2. This contract only supports a single SPL token, right?

pub mint: Pubkey,
pub client_id: String, // IBC client (max 64)
pub counterparty_ift_address: String,// IFT contract on destination (max 128)
pub counterparty_chain_type: CounterpartyChainType,
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be better for this to follow the solidity pattern, where this is a program ID. But I don't feel strongly about this.


### Key Security Properties

1. **Burn Authorization**: Only token owner can burn (standard SPL token semantics)
Copy link
Member

Choose a reason for hiding this comment

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

Anyone calling iftTransfer can burn right?

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.

4 participants