Skip to content

Conversation

@Reinis-FRP
Copy link
Contributor

@Reinis-FRP Reinis-FRP commented Nov 13, 2024

This introduces support for Across+ targeting Solana. Since Solana instructions require additional context on account keys and their permissions we require that messages targeting Solana adhere to common format to encapsulate this additional information.

This also adds multicall handler implementation that allows to execute multiple instructions resembling compiled instruction format.

Considering the current structure of FilledV3Relay event we get limited by inner instruction size bounds when handler message exceeds 472 bytes, which based on tests allows up to 5 distributions to different recipients within the handler. This could be increased approx 1.6x if we were to emit the hashed version of the message before hitting outer transaction size limits. This could then be further increased if we were loading instruction parameters via buffer account for fills, like we do for executing relayer refunds.

At current implementation the Spoke would invoke the handler without signatures, assuming the handler does not need to retain any privileges on behalf of end users (e.g. swapping out to user controlled ATA). If however the handler needs to retain user privileges (e.g. withdrawing from money market) we would need to introduce signed handler invocations where PDA seeds are derived from some combination of depositor address and origin chain.

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
// Emit the FilledV3Relay event
let message_clone = relay_data.message.clone(); // Clone the message before it is moved

if message_clone.len() > 0 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is slightly different than in EVM where we also check if the recipient is program. On Solana that would not be possible as the recipient is handler PDA, not the handler program itself. But I'm not sure if there would be a real use case for arbitrary messages that are not to be invoked by the handler.

}

#[derive(AnchorDeserialize)]
pub struct CompiledIx {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This resembles compiled instructions within a transaction message, except we don't use the compact-u16 encoding for the sake of code simplicity.

// Transfer value amount from the relayer to the first account in the message accounts.
// Note that the depositor is responsible to make sure that after invoking the handler the recipient account will
// not hold any balance that is below its rent-exempt threshold, otherwise the fill would fail.
if message.value_amount > 0 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is mostly useful when invoked handler transaction requires account initialization payer (e.g. when creating ATAs)

data,
};

// TODO: consider if the message handler requires signed invocation.
Copy link
Contributor Author

@Reinis-FRP Reinis-FRP Nov 15, 2024

Choose a reason for hiding this comment

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

This mostly depends on whether the handler needs to retain any privileges on behalf of the user. E.g. if the handler performs a swap and forwards swapped-in tokens to user's ATA then its not important. On the other hand, if the handler deposits funds in a money market that does not allow depositing on behalf of end user then the handler would need to retain rights to withdraw the funds where such call must be authenticated via spoke pool PDA that is derived from depositing user address and origin chain.

Copy link
Member

Choose a reason for hiding this comment

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

right. main issue being how much size we have left in the tx. I think we can circle back to this once we've totally validated the upper bound of the tx sizing here and that it covers enough basic usecases.

Copy link
Member

@chrismaree chrismaree Nov 19, 2024

Choose a reason for hiding this comment

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

for example this tx that we did from the dev wallet: https://solscan.io/tx/5swoRhHcqpxRXRhyyaCRWqTGDcNuQ8avZXptLomyUFPEPKjQceiMfSPdypA9pcdZQu3inGHmhoZmBWdEbZwGRZYC

this is a deposit into a money market protocol from the perspective of the user. this would not work in the current pattern given this restriction. it could be possible to re-structure this tx such that the relayer deposits and then transfers ownership of the position to the user but this seems a fair bit more complex and requires this kind of transfer logic within the target protocol.

in general though this might be a requirement. we need to think about the exact user flow that we want to accommodate for across+ but I think many of them will require this kind of authentication with a bridge +deposit style flow.

my main worry then is just how big the transactions will become and if we can still encode them within the message structure.

Copy link
Member

Choose a reason for hiding this comment

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

ok after discussion this more we've decided that this is infact not a requirement as the filler on evm is not permissioned (no preservation of msg.sender on evm) so this is fine to to have on svm.

@Reinis-FRP Reinis-FRP marked this pull request as ready for review November 15, 2024 15:35
let message_clone = relay_data.message.clone(); // Clone the message before it is moved

if message_clone.len() > 0 {
invoke_handler(ctx.accounts.signer.as_ref(), ctx.remaining_accounts, &message_clone)?;
Copy link
Member

Choose a reason for hiding this comment

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

execute_v3_slow_relay_leaf will use more transaction size than fill_v3_relay, due to the proof. This might further limit the complexity of the across+ style hooks that can be used depending on whether they were executed fast or slow.

}
}

// Extended version of legacy Message to handle compilation of unsigned transactions. Base implementation is here:
Copy link
Member

Choose a reason for hiding this comment

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

^

// Encode handler message for the token distribution.
const multicallHandlerCoder = new MulticallHandlerCoder(transferInstructions);
const handlerMessage = multicallHandlerCoder.encode();
const message = new AcrossPlusMessageCoder({
Copy link
Member

Choose a reason for hiding this comment

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

^

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
])
: [];

// Consolidate all above addresses into a single array for the Address Lookup Table (ALT).
Copy link
Member

Choose a reason for hiding this comment

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

cool how much this let us remove by using sendTransactionWithLookupTable. why did we not need the maxExtendedAccounts prop anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

now we have maxExtendedAccounts inside the sendTransactionWithLookupTable helper, as that is not instruction specific

pub depositor: Pubkey,
pub recipient: Pubkey,
pub message: Vec<u8>,
// TODO: update EVM implementation to use message_hash in all fill related events.
Copy link
Member

Choose a reason for hiding this comment

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

yup, we'll need to update this separately.

@@ -0,0 +1,216 @@
// This script implements a fill where relayed tokens are distributed to random recipients via the message handler.
Copy link
Member

Choose a reason for hiding this comment

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

this is very useful to see the intended encoding structure end to end.

handlerMessage,
});

const encodedMessage = message.encode();
Copy link
Member

Choose a reason for hiding this comment

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

having the AcrossPlusMessageCoder really helps with this. great job.

inputAmount: new BN(relayAmount),
outputAmount: new BN(relayAmount),
originChainId: new BN(1),
depositId: new BN(Math.floor(Math.random() * 1000000)), // force that we always have a new deposit id.
Copy link
Member

Choose a reason for hiding this comment

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

note we are going to have merge conflicts & breaks between your PR and this https://github.com/across-protocol/contracts/pull/759/files due to depositId and how it's defined changing some.

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
@Reinis-FRP Reinis-FRP merged commit 02d8dd0 into master Nov 22, 2024
9 checks passed
@Reinis-FRP Reinis-FRP deleted the reinis-frp/svm-across-plus branch November 22, 2024 10:06
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