Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ solana-transaction = "3.0.1"
solana-system-interface = { version = "2.0.0", features = ["bincode"] }
spl-token-interface = "2.0.0"
magicblock-delegation-program-api = { version = "0.1.1", default-features = false }
bytemuck = { version = "1.23.1", features = ["derive"] }
bytemuck = { version = "1.25.0", features = ["derive"] }
const-crypto = "0.3.0"
2 changes: 1 addition & 1 deletion e-token-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pinocchio-log = { workspace = true }
pinocchio-pubkey = { workspace = true }
solana-address = { workspace = true, features = ["bytemuck"] }
ephemeral-rollups-pinocchio = { workspace = true }
bytemuck = { workspace = true, features = ["derive"] }
bytemuck = { workspace = true }

[lints]
workspace = true
8 changes: 4 additions & 4 deletions e-token-api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use pinocchio::error::{ProgramError, ToStr};
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EphemeralSplError {
// invalid instruction data
InvalidInstruction = 1,
// invalid discriminator
InstructionNotFound = 1,
// account already initialized / in use
AlreadyInUse = 2,
// Ephemeral ATA mismatch
Expand All @@ -30,7 +30,7 @@ impl ToStr for EphemeralSplError {
use EphemeralSplError::*;

match self {
InvalidInstruction => "EphemeralSplError::InvalidInstruction",
InstructionNotFound => "EphemeralSplError::InstructionNotFound",
AlreadyInUse => "EphemeralSplError::AlreadyInUse",
EphemeralAtaMismatch => "EphemeralSplError::EphemeralAtaMismatch",
MintMismatch => "EphemeralSplError::MintMismatch",
Expand All @@ -45,7 +45,7 @@ impl core::convert::TryFrom<u32> for EphemeralSplError {
type Error = ProgramError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
1 => Ok(EphemeralSplError::InvalidInstruction),
1 => Ok(EphemeralSplError::InstructionNotFound),
2 => Ok(EphemeralSplError::AlreadyInUse),
3 => Ok(EphemeralSplError::EphemeralAtaMismatch),
4 => Ok(EphemeralSplError::MintMismatch),
Expand Down
221 changes: 221 additions & 0 deletions e-token-api/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
use alloc::vec::Vec;

///
/// ESplInstruction defines the public instructions
///
/// Reserved values:
/// - single: 196 : for DLP undelegatation callback used to restore delegated state
/// - range: (201.. =255) : for ESPL internal instructions
///
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ESplInstruction {
/// 0 - InitializeEphemeralAta: initialize the ephemeral ATA account derived from [user, mint]
InitializeEphemeralAta = 0,

/// 1 - InitializeGlobalVault: initialize the global vault [mint] PDA plus vault-owned Ephemeral ATA and vault ATA
InitializeGlobalVault = 1,

/// 2 - DepositSplTokens: transfer tokens to global vault and increase EphemeralAta amount
/// Works for both standard EATA and shuttle EATA, as long as the data account is program-owned.
DepositSplTokens = 2,

/// 3 - WithdrawSplTokens: transfer tokens from global vault back to user and decrease EphemeralAta amount
WithdrawSplTokens = 3,

/// 4 - DelegateEphemeralAta: delegate the ephemeral ATA to a DLP program using PDA seeds
DelegateEphemeralAta = 4,

/// 5 - UndelegateEphemeralAta: commit state and undelegate an ephemeral ATA via the delegation program
UndelegateEphemeralAta = 5,

/// 6 - CreateEphemeralAtaPermission: create a permission account for the ephemeral ATA
/// Instruction data:
/// [0] bump
/// [1] MemberFlags bitfield encoded via MemberFlags::to_acl_flag_byte.
CreateEphemeralAtaPermission = 6,

/// 7 - DelegateEphemeralAtaPermission: delegate the permission PDA for an ephemeral ATA
DelegateEphemeralAtaPermission = 7,

/// 8 - UndelegateEphemeralAtaPermission: commit and undelegate the permission PDA
UndelegateEphemeralAtaPermission = 8,

/// 9 - ResetEphemeralAtaPermission: reset permission members to creation-time defaults
/// Instruction data:
/// [0] bump
/// [1] MemberFlags bitfield encoded via MemberFlags::to_acl_flag_byte.
ResetEphemeralAtaPermission = 9,

/// 10 - CloseEphemeralAta: close an empty ephemeral ATA and refund rent to recipient
CloseEphemeralAta = 10,

/// 11 - InitializeShuttleEphemeralAta: initialize shuttle account derived from [owner, mint, shuttle_id]
/// Instruction data:
/// [0..4] shuttle_id (u32 LE)
/// [4] bump
InitializeShuttleEphemeralAta = 11,

/// 12 - InitializeTransferQueue: initialize per-mint transfer queue PDA derived from [QUEUE_SEED, mint]
/// Instruction data:
/// [] default size (9728 bytes)
/// [0..4] optional queue size in bytes (u32 LE), 0 => default
InitializeTransferQueue = 12,

/// 13 - DelegateShuttleEphemeralAta: delegate shuttle account to a DLP program using PDA seeds
DelegateShuttleEphemeralAta = 13,

/// 14 - UndelegateAndCloseShuttleToOwner: revoke delegation on a shuttle ATA
/// and schedule settlement/close using an owner-owned destination token account.
UndelegateAndCloseShuttleToOwner = 14,

/// 15 - MergeShuttleIntoEphemeralAta: transfer all shuttle ATA funds into destination ATA and keep shuttle account open
MergeShuttleIntoEphemeralAta = 15,

/// 16 - DepositAndQueueTransfer: transfer tokens from signer into the vault ATA and enqueue one or more delayed transfers
/// Instruction data:
/// [0..8] amount (u64 LE)
/// [8..16] min_delay_ms (u64 LE), 0 => immediate
/// [16..24] max_delay_ms (u64 LE), must be >= min_delay_ms
/// [24..28] split count (u32 LE), must be >= 1
/// [28] optional legacy flags (u8)
/// [28..36] optional client_ref_id (u64 LE) when flags are omitted
/// [29..37] optional client_ref_id (u64 LE) after legacy flags
DepositAndQueueTransfer = 16,

/// 17 - EnsureTransferQueueCrank: ensure the per-mint recurring queue crank is scheduled
/// Instruction data:
/// []
EnsureTransferQueueCrank = 17,

/// 19 - DelegateTransferQueue: delegate the per-mint transfer queue PDA to the delegation program
/// Instruction data:
/// [] no instruction args
DelegateTransferQueue = 19,

/// 20 - SponsoredLamportsTransfer: create a zero-data PDA derived from
/// [b"lamports", payer, destination, salt], fund it with the requested
/// lamports plus sponsored rent from the global rent PDA, delegate it,
/// then schedule post-delegation transfer + cleanup actions.
/// Instruction data:
/// [0..8] amount (u64 LE)
/// [8..40] salt ([u8; 32])
SponsoredLamportsTransfer = 20,

/// 23 - InitializeRentPda: initialize the global rent-sponsoring PDA derived from ["rent"]
/// Instruction data:
/// [] no instruction args
InitializeRentPda = 23,

/// 24 - SetupAndDelegateShuttleEphemeralAtaWithMerge: initialize shuttle metadata/EATA/wallet ATA,
/// deposit tokens into the shuttle EATA through the global vault, sponsor delegation from
/// the global rent PDA, and schedule post-delegation merge + cleanup.
/// Instruction data:
/// [0..4] shuttle_id (u32 LE)
/// [4] shuttle metadata bump
/// [5..13] deposit amount (u64 LE)
/// [13..45] optional validator pubkey
SetupAndDelegateShuttleEphemeralAtaWithMerge = 24,

/// 25 - DepositAndDelegateShuttleEphemeralAtaWithMergeAndPrivateTransfer:
/// same setup/deposit/delegate flow as instruction 24, but instead of using instruction 24's
/// merge-to-destination behavior, the first post-delegation action restores the owner's
/// source token account by merging the shuttle balance back there, then a third post-delegation
/// action schedules a private transfer of the same amount to the destination owner's
/// canonical ATA.
/// SDK callers must account for that queued private transfer when calculating required source
/// balances and expected destination credits; the destination does not hold the final funds
/// immediately after the merge/cleanup steps.
/// Instruction data:
/// [0..4] shuttle_id (u32 LE)
/// [4..12] deposit amount (u64 LE)
/// [12..] len-prefixed optional validator pubkey bytes
/// [...] len-prefixed encrypted destination owner pubkey bytes
/// [...] len-prefixed encrypted packed suffix
/// (min_delay_ms:u64, max_delay_ms:u64, split:u32, client_ref_id?:u64)
/// Legacy payloads may still append flags before client_ref_id.
DepositAndDelegateShuttleEphemeralAtaWithMergeAndPrivateTransfer = 25,

/// 26 - WithdrawThroughDelegatedShuttleWithMerge: initialize shuttle metadata/EATA/wallet ATA,
/// sponsor delegation from the global rent PDA, then schedule a post-delegation transfer
/// from the owner ATA into the shuttle wallet ATA followed by shuttle undelegate
/// and close/refund.
/// Instruction data:
/// [0..4] shuttle_id (u32 LE)
/// [4] shuttle metadata bump
/// [5..13] transfer amount (u64 LE)
/// [13..45] optional validator pubkey
WithdrawThroughDelegatedShuttleWithMerge = 26,

/// 27 - AllocateTransferQueue: allocates more space for the transfer queue
AllocateTransferQueue = 27,

/// 28 - ProcessPendingTransferQueueRefill: permissionless idempotent helper that
/// checks the queue-refill-state PDA and, when pending, tops up the queue
/// lamports from the global rent PDA.
/// Instruction data:
/// [] no instruction args
ProcessPendingTransferQueueRefill = 28,
}

impl ESplInstruction {
#[inline(always)]
pub const fn discriminator(self) -> u8 {
self as u8
}

#[inline(always)]
pub const fn to_bytes(self) -> [u8; 8] {
[self.discriminator(), 0, 0, 0, 0, 0, 0, 0]
}

#[inline(always)]
pub fn to_vec(self) -> Vec<u8> {
self.to_bytes().to_vec()
}

#[inline(always)]
pub fn with_data(self, instruction_data: &[u8]) -> Vec<u8> {
let mut data = Vec::with_capacity(8 + instruction_data.len());
data.extend_from_slice(&self.to_bytes());
data.extend_from_slice(instruction_data);
data
}
}

impl TryFrom<u8> for ESplInstruction {
type Error = ();

#[inline(always)]
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::InitializeEphemeralAta),
1 => Ok(Self::InitializeGlobalVault),
2 => Ok(Self::DepositSplTokens),
3 => Ok(Self::WithdrawSplTokens),
4 => Ok(Self::DelegateEphemeralAta),
5 => Ok(Self::UndelegateEphemeralAta),
6 => Ok(Self::CreateEphemeralAtaPermission),
7 => Ok(Self::DelegateEphemeralAtaPermission),
8 => Ok(Self::UndelegateEphemeralAtaPermission),
9 => Ok(Self::ResetEphemeralAtaPermission),
10 => Ok(Self::CloseEphemeralAta),
11 => Ok(Self::InitializeShuttleEphemeralAta),
12 => Ok(Self::InitializeTransferQueue),
13 => Ok(Self::DelegateShuttleEphemeralAta),
14 => Ok(Self::UndelegateAndCloseShuttleToOwner),
15 => Ok(Self::MergeShuttleIntoEphemeralAta),
16 => Ok(Self::DepositAndQueueTransfer),
17 => Ok(Self::EnsureTransferQueueCrank),
19 => Ok(Self::DelegateTransferQueue),
20 => Ok(Self::SponsoredLamportsTransfer),
23 => Ok(Self::InitializeRentPda),
24 => Ok(Self::SetupAndDelegateShuttleEphemeralAtaWithMerge),
25 => Ok(Self::DepositAndDelegateShuttleEphemeralAtaWithMergeAndPrivateTransfer),
26 => Ok(Self::WithdrawThroughDelegatedShuttleWithMerge),
27 => Ok(Self::AllocateTransferQueue),
28 => Ok(Self::ProcessPendingTransferQueueRefill),
_ => Err(()),
}
}
}
Loading
Loading