-
Notifications
You must be signed in to change notification settings - Fork 75
fix(svm): across plus to solana #747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| 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)?; |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. | |||
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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>
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
FilledV3Relayevent 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.