Skip to content

Commit 700520e

Browse files
authored
Merge pull request #150 from m0-foundation/portal-executor-integration
PROTO-317: Portal executor integration
2 parents 44df41b + dd714cf commit 700520e

File tree

20 files changed

+1343
-139
lines changed

20 files changed

+1343
-139
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/portal/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ anchor-lang = { workspace = true, features = ["init-if-needed"] }
2323
anchor-spl.workspace = true
2424
spl-token-2022.workspace = true
2525
wormhole-anchor-sdk = { git = "https://github.com/wormhole-foundation/wormhole-scaffolding", default-features = false, rev = "3be80a9"}
26+
executor-account-resolver-svm = { version = "0.0.1", git = "https://github.com/wormholelabs-xyz/executor-account-resolver-svm", rev = "a39fe39cece715b3e1d1afef1f6e47740595a562" }
2627
wormhole-io = "0.1.3"
2728
bitmaps = "3.2.1"
2829
cfg-if.workspace = true
2930
solana-security-txt.workspace = true
3031
earn = { path = "../earn", features = ["cpi"] }
32+
33+
[dev-dependencies]
34+
base64 = "0.22.1"

programs/portal/idls/ext_swap.json

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,33 @@
881881
},
882882
{
883883
"name": "ext_program"
884+
},
885+
{
886+
"name": "ext_global",
887+
"writable": true,
888+
"pda": {
889+
"seeds": [
890+
{
891+
"kind": "const",
892+
"value": [
893+
103,
894+
108,
895+
111,
896+
98,
897+
97,
898+
108
899+
]
900+
}
901+
],
902+
"program": {
903+
"kind": "account",
904+
"path": "ext_program"
905+
}
906+
}
907+
},
908+
{
909+
"name": "ext_mint",
910+
"writable": true
884911
}
885912
],
886913
"args": []
@@ -1273,11 +1300,35 @@
12731300
{
12741301
"name": "whitelisted_extensions",
12751302
"type": {
1276-
"vec": "pubkey"
1303+
"vec": {
1304+
"defined": {
1305+
"name": "WhitelistedExtension"
1306+
}
1307+
}
12771308
}
12781309
}
12791310
]
12801311
}
1312+
},
1313+
{
1314+
"name": "WhitelistedExtension",
1315+
"type": {
1316+
"kind": "struct",
1317+
"fields": [
1318+
{
1319+
"name": "program_id",
1320+
"type": "pubkey"
1321+
},
1322+
{
1323+
"name": "mint",
1324+
"type": "pubkey"
1325+
},
1326+
{
1327+
"name": "token_program",
1328+
"type": "pubkey"
1329+
}
1330+
]
1331+
}
12811332
}
12821333
],
12831334
"constants": [

programs/portal/src/config.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ pub struct Config {
6060
pub release_inbound_remaining_accounts: [RemainingAccount; 2],
6161
/// destination token addresses
6262
pub evm_token: [u8; 32],
63-
pub evm_wrapped_token: [u8; 32],
63+
/// lut for resolve_execute
64+
pub resolve_lut: Pubkey,
6465
}
6566

6667
#[derive(AnchorSerialize, AnchorDeserialize, Clone, InitSpace)]

programs/portal/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ pub enum NTTError {
6363
ThresholdTooHigh,
6464
#[msg("InvalidTransceiverProgram")]
6565
InvalidTransceiverProgram,
66+
#[msg("InvalidVAA")]
67+
InvalidVAA,
68+
#[msg("RESOLVER_RESULT_ACCOUNT needs to be writable")]
69+
InvalidReturnAccount,
70+
#[msg("Missing payer account")]
71+
MissingPayerAccount,
6672
}
6773

6874
impl From<ScalingError> for NTTError {

programs/portal/src/instructions/initialize.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use anchor_lang::prelude::*;
22
use anchor_spl::{associated_token::AssociatedToken, token_interface};
3+
use executor_account_resolver_svm::{
4+
RESOLVER_RESULT_ACCOUNT_INIT_SIZE, RESOLVER_RESULT_ACCOUNT_SEED,
5+
};
36

47
use crate::{
58
bitmap::Bitmap,
69
config::{Config, RemainingAccount},
710
error::NTTError,
11+
instructions::ExecutorAccountResolverResult,
812
ntt_messages::{BpfLoaderUpgradeable, ChainId, Mode},
913
queue::{outbox::OutboxRateLimit, rate_limit::RateLimitState},
1014
};
@@ -87,6 +91,7 @@ pub struct InitializeArgs {
8791
pub chain_id: u16,
8892
pub limit: u64,
8993
pub mode: Mode,
94+
pub evm_token: [u8; 32],
9095
}
9196

9297
pub fn initialize(ctx: Context<Initialize>, args: InitializeArgs) -> Result<()> {
@@ -111,8 +116,8 @@ pub fn initialize(ctx: Context<Initialize>, args: InitializeArgs) -> Result<()>
111116
true,
112117
),
113118
],
114-
evm_token: [0; 32],
115-
evm_wrapped_token: [0; 32],
119+
evm_token: args.evm_token,
120+
resolve_lut: Pubkey::default(),
116121
});
117122

118123
ctx.accounts.rate_limit.set_inner(OutboxRateLimit {
@@ -123,25 +128,33 @@ pub fn initialize(ctx: Context<Initialize>, args: InitializeArgs) -> Result<()>
123128
}
124129

125130
#[derive(Accounts)]
126-
pub struct SetDestinationAddresses<'info> {
131+
pub struct InitializeResolverAccounts<'info> {
132+
#[account(mut)]
127133
pub owner: Signer<'info>,
128134

129135
#[account(
130136
mut,
131137
has_one = owner,
132-
seeds = [Config::SEED_PREFIX],
138+
)]
139+
pub config: Account<'info, Config>,
140+
141+
#[account(
142+
init_if_needed,
143+
payer = owner,
144+
space = 8 + RESOLVER_RESULT_ACCOUNT_INIT_SIZE,
145+
seeds = [RESOLVER_RESULT_ACCOUNT_SEED],
133146
bump
134147
)]
135-
pub config: Box<Account<'info, Config>>,
148+
pub result_account: Account<'info, ExecutorAccountResolverResult>,
149+
150+
system_program: Program<'info, System>,
136151
}
137152

138-
pub fn set_destination_addresses(
139-
ctx: Context<SetDestinationAddresses>,
140-
evm_token: [u8; 32],
141-
evm_wrapped_token: [u8; 32],
153+
pub fn initialize_resolver_accounts(
154+
ctx: Context<InitializeResolverAccounts>,
155+
additional_lut: Option<Pubkey>,
142156
) -> Result<()> {
143-
ctx.accounts.config.evm_token = evm_token;
144-
ctx.accounts.config.evm_wrapped_token = evm_wrapped_token;
157+
ctx.accounts.config.resolve_lut = additional_lut.unwrap_or_default();
145158

146159
Ok(())
147160
}

programs/portal/src/instructions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod luts;
66
pub mod redeem;
77
pub mod release_inbound;
88
pub mod release_inbound_extension;
9+
pub mod resolve_execute;
910
pub mod transfer;
1011
pub mod transfer_extension;
1112

@@ -16,6 +17,7 @@ pub use luts::*;
1617
pub use redeem::*;
1718
pub use release_inbound::*;
1819
pub use release_inbound_extension::*;
20+
pub use resolve_execute::*;
1921
pub use transfer::*;
2022
pub use transfer_extension::*;
2123

programs/portal/src/instructions/release_inbound.rs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
error::NTTError,
1414
instructions::BridgeEvent,
1515
ntt_messages::Mode,
16-
queue::inbox::{InboxItem, ReleaseStatus, TokenTransfer},
16+
queue::inbox::{InboxItem, ReleaseStatus},
1717
};
1818

1919
#[derive(Accounts)]
@@ -58,6 +58,7 @@ pub struct ReleaseInboundMint<'info> {
5858
pub earn_program: Program<'info, Earn>,
5959

6060
#[account(
61+
mut,
6162
seeds = [GLOBAL_SEED],
6263
seeds::program = earn::ID,
6364
bump = m_global.bump,
@@ -73,25 +74,16 @@ pub struct ReleaseInboundArgs {
7374
pub fn release_inbound_mint<'info>(
7475
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>,
7576
args: ReleaseInboundArgs,
76-
) -> Result<()> {
77-
release_inbound(ctx, args, false)
78-
}
79-
80-
pub fn release_inbound<'info>(
81-
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>,
82-
args: ReleaseInboundArgs,
83-
release_extension: bool,
8477
) -> Result<()> {
8578
let inbox_item = &mut ctx.accounts.inbox_item;
8679

8780
// Validate token account depending on call context
8881
validate_recipient_token_account(
8982
&ctx.accounts.recipient.key(),
90-
&inbox_item.transfer,
83+
&inbox_item,
9184
&ctx.accounts.token_authority.key(),
9285
&ctx.accounts.mint.key(),
9386
&ctx.accounts.token_program.key(),
94-
release_extension,
9587
)?;
9688

9789
if !inbox_item.try_release()? {
@@ -186,40 +178,53 @@ pub fn release_inbound<'info>(
186178

187179
fn validate_recipient_token_account(
188180
recipient: &Pubkey,
189-
transfer: &TokenTransfer,
181+
inbox_item: &InboxItem,
190182
token_authority: &Pubkey,
191-
mint: &Pubkey,
183+
m_mint: &Pubkey,
192184
token_program: &Pubkey,
193-
release_extension: bool,
194185
) -> Result<()> {
186+
let expected = get_inbox_recipient_token_account(
187+
&inbox_item.transfer.recipient,
188+
&inbox_item.destination_mint,
189+
inbox_item.transfer.amount,
190+
token_authority,
191+
m_mint,
192+
token_program,
193+
);
194+
195+
if expected.is_some() && !expected.unwrap().eq(recipient) {
196+
return err!(NTTError::InvalidRecipientAddress);
197+
}
198+
199+
Ok(())
200+
}
201+
202+
pub fn get_inbox_recipient_token_account(
203+
recipient: &Pubkey,
204+
destination_mint: &Pubkey,
205+
amount: u64,
206+
token_authority: &Pubkey,
207+
m_mint: &Pubkey,
208+
token_program: &Pubkey,
209+
) -> Option<Pubkey> {
195210
// Only bridging data
196-
if transfer.amount == 0 {
197-
return Ok(());
211+
if amount == 0 {
212+
return None;
198213
}
199214

200215
// Bridging to extension, require intermediate portal token account
201-
if release_extension {
202-
if !recipient.eq(&get_associated_token_address_with_program_id(
216+
if !destination_mint.eq(m_mint) {
217+
return Some(get_associated_token_address_with_program_id(
203218
token_authority,
204-
mint,
219+
m_mint,
205220
token_program,
206-
)) {
207-
msg!("expected recipient to be token authority");
208-
return err!(NTTError::InvalidRecipientAddress);
209-
}
210-
211-
return Ok(());
221+
));
212222
}
213223

214224
// Bridging $M, require user token account
215-
if !recipient.eq(&get_associated_token_address_with_program_id(
216-
&transfer.recipient,
217-
mint,
225+
Some(get_associated_token_address_with_program_id(
226+
&recipient,
227+
m_mint,
218228
token_program,
219-
)) {
220-
msg!("expected recipient to match inbox item");
221-
return err!(NTTError::InvalidRecipientAddress);
222-
}
223-
224-
Ok(())
229+
))
225230
}

programs/portal/src/instructions/release_inbound_extension.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use earn::state::GLOBAL_SEED;
44
use ext_swap::accounts::SwapGlobal;
55
use ext_swap::program::ExtSwap;
66

7-
use crate::instructions::ext_swap;
8-
use crate::instructions::{release_inbound, ReleaseInboundArgs, ReleaseInboundMint};
7+
use crate::instructions::{ext_swap, release_inbound_mint};
8+
use crate::instructions::{ReleaseInboundArgs, ReleaseInboundMint};
99
use crate::ReleaseInboundMintBumps;
1010
use crate::__client_accounts_release_inbound_mint;
1111
use crate::__cpi_client_accounts_release_inbound_mint;
@@ -94,7 +94,7 @@ pub fn release_inbound_mint_extension<'info>(
9494
let token_auth_bump = ctx.bumps.common.token_authority;
9595

9696
// Release bridged $M
97-
release_inbound(
97+
release_inbound_mint(
9898
Context::new(
9999
ctx.program_id,
100100
&mut ctx.accounts.common,
@@ -108,7 +108,6 @@ pub fn release_inbound_mint_extension<'info>(
108108
// always revert on delay or wrap will fail
109109
revert_when_not_ready: true,
110110
},
111-
true,
112111
)?;
113112

114113
ctx.accounts.common.recipient.reload()?;

0 commit comments

Comments
 (0)