Skip to content
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

token-js: added extra account resolution for transfer hook extension #5112

Merged
merged 6 commits into from
Aug 30, 2023

Conversation

wjthieme
Copy link
Contributor

@wjthieme wjthieme commented Aug 25, 2023

resolves #5108

Couple notes:

  • Not sure exactly how multisigners is implemented for extra account resolution in the program since it changes the ordering of the account keys. Does it mean that if a user wants multisigners to work they explicitly need to code this into their transfer hook program?
  • What is done by tlv-account-resolution in rust is now baked into @solana/spl-token for js. Might be a nice improvement to separate that into its own package?
  • I have written a few tests to the best of my understanding of what exactly the data structure of the extra accounts data account is. Might be worth for @joncinque or @buffalojoec to check if my implementation actually matches that of the rust client.
  • The way transferCheckedWithHook and addExtraAccountsToInstruction are structured is a little bit made up by me. Let me know if it matches your expectations @joncinque

@mergify mergify bot added the community Community contribution label Aug 25, 2023
Copy link
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

Nice work mate!

The Rust lib separates serialization/deserialization of the seed configs into the seeds module, since it has to be used to pack and unpack both on-chain and off-chain, but JavaScript doesn't need that! I appreciate your condensed version of the seed resolution, doing both deserialization and resolution in one step 🤌🏼

I left a handful of comments, but this is shaping up nicely!

token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/seeds.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/seeds.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
token/js/src/errors.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
token/js/test/unit/transferHook.test.ts Show resolved Hide resolved
@buffalojoec
Copy link
Contributor

  • Not sure exactly how multisigners is implemented for extra account resolution in the program since it changes the ordering of the account keys. Does it mean that if a user wants multisigners to work they explicitly need to code this into their transfer hook program?

Multisigs are evaluated at the Token-2022 level. The TransferChecked instruction expects - if you're working with multisig - the multisig account first and then the signers immediately preceding.

/// * Single owner/delegate
/// 0. `[writable]` The source account.
/// 1. `[]` The token mint.
/// 2. `[writable]` The destination account.
/// 3. `[signer]` The source account's owner/delegate.
///
/// * Multisignature owner/delegate
/// 0. `[writable]` The source account.
/// 1. `[]` The token mint.
/// 2. `[writable]` The destination account.
/// 3. `[]` The source account's multisignature owner/delegate.
/// 4. ..4+M `[signer]` M signer accounts.

You can see here we're reading the data of the authority account and then using validate_owner to evaluate the mutisigs from the remaining accounts.

Self::validate_owner(
program_id,
&source_account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;

Then your extra metas would come after!

  • What is done by tlv-account-resolution in rust is now baked into @solana/spl-token for js. Might be a nice improvement to separate that into its own package?

We could separate it, but I mentioned in my comment above that it's actually not necessary at the moment for the JavaScript library to be able to handle (de)serialization of the seed configs and TLV state. However, the more I think about it, the more I think we will eventually want a JavaScript TLV library so we don't have to keep writing the same count and values structures with buffer layout. Perhaps just a split-out TLV JS library?

  • I have written a few tests to the best of my understanding of what exactly the data structure of the extra accounts data account is. Might be worth for @joncinque or @buffalojoec to check if my implementation actually matches that of the rust client.

Actually you're pretty damn close, I just had the comment about little-endian. It's all facilitated by the PodSlice type in the spl-type-length-value library, which uses bytemuck zero-copy serde to do exactly what you're doing, iterate over deterministic bytes in a slice with a u32 length at the front to know where to stop.

  • The way transferCheckedWithHook and addExtraAccountsToInstruction are structured is a little bit made up by me. Let me know if it matches your expectations @joncinque

I think these look pretty good! I don't think we'll really be able to test these handlers properly until we have the instructions from the transfer hook interface baked into the JS client, so we can tell a program to pack some extra metas and then try to transfer with them.

Once there, we can probably pluck these commands out of the package.json and into a shell script so we can also deploy a hook program and maybe even another CPI program like the Rust tests do.

"test:e2e-built": "start-server-and-test 'solana-test-validator --bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA ../../target/deploy/spl_token.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'",
"test:e2e-2022": "TEST_PROGRAM_ID=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb start-server-and-test 'solana-test-validator --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL ../../target/deploy/spl_associated_token_account.so --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ../../target/deploy/spl_token_2022.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e*'",
"test:e2e-native": "start-server-and-test 'solana-test-validator --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'",
"test:build-programs": "cargo build-sbf --manifest-path ../program/Cargo.toml && cargo build-sbf --manifest-path ../program-2022/Cargo.toml && cargo build-sbf --manifest-path ../../associated-token-account/program/Cargo.toml",

@wjthieme
Copy link
Contributor Author

wjthieme commented Aug 26, 2023

Multisigs are evaluated at the Token-2022 level.

Awesome! So the js implementation for this is correct and a transfer hook program would need to take these extra multisig accounts into consideration if they want to support it.

Perhaps just a split-out TLV JS library?

Yeah I think that would make more sense than a tlv-account-resolution library. Perhaps we can create a issue for it but guess it is not that important for now.

so we can tell a program to pack some extra metas and then try to transfer with them

Yeah was thinking about that as well but figured that was beyond the scope of this PR/issue

@wjthieme
Copy link
Contributor Author

wjthieme commented Aug 26, 2023

Extra clarification question

Is it correct that tlv-account-resolution has a limitation that accounts using other accounts as seeds need to be ordered after the account that needs them? At least that is what I am seeing in the rust implementation so just copied that logic into js as well.

i.e. if you are resolving the account at index 3 it can only use accounts 0..2 as seed inputs.

@buffalojoec
Copy link
Contributor

Extra clarification question

Is it correct that tlv-account-resolution has a limitation that accounts using other accounts as seeds need to be ordered after the account that needs them? At least that is what I am seeing in the rust implementation so just copied that logic into js as well.

i.e. if you are resolving the account at index 3 it can only use accounts 0..2 as seed inputs.

Yep that's correct. I have an implementation where this constraint is no longer required but it was a bit complicated. If we ever need it, I'll pull that bad boy out

@buffalojoec
Copy link
Contributor

Awesome! So the js implementation for this is correct and a transfer hook program would need to take these extra multisig accounts into consideration if they want to support it.

Yep

Perhaps just a split-out TLV JS library?

Yeah I think that would make more sense than a tlv-account-resolution library. Perhaps we can create a issue for it but guess it is not that important for now.

Agreed I swore I made one already but I'll throw one up now #5127

so we can tell a program to pack some extra metas and then try to transfer with them

Yeah was thinking about that as well but figured that was beyond the scope of this PR/issue

Agreed not in this PR. Also made an issue for this! #5128

Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

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

Really great work! This is pretty close to ready, thanks so much for contributing. We'll just need to fixup the decoding bit and clean up a few nits, then it should be good to go!

token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
token/js/test/unit/transferHook.test.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

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

This is extremely close, just a few last little things! Thanks for your patience and quick responses

token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/actions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/instructions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/instructions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/instructions.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
token/js/src/extensions/transferHook/state.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

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

This looks good to me, thanks so much for taking this on! You:

rock

I'll give @buffalojoec a chance to add any other comments

Copy link
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

Beast! Ship it 🚢

@joncinque joncinque merged commit c79c727 into solana-labs:master Aug 30, 2023
29 checks passed
@wjthieme wjthieme deleted the wjthieme-patch-3 branch August 31, 2023 06:18
thlorenz added a commit to ironforge-cloud/solana-program-library that referenced this pull request Sep 4, 2023
* master: (719 commits)
  release: Bump token-2022 and all dependencies (solana-labs#5189)
  SPL errors from hashes (solana-labs#5169)
  stake-pool: Add comments about unnecessary ownership checks (HAL-01) (solana-labs#5084)
  stake-pool: Enforce that pool mint uses 9 decimal places (HAL-03) (solana-labs#5085)
  build(deps-dev): bump tsx from 3.12.7 to 3.12.8 in /single-pool/js (solana-labs#5188)
  account-compression: Fixup sdk doc deployment (solana-labs#5187)
  token-js: renamed `getExtraAccountMetaAccount` to `getExtraAccountMetaAddress` (solana-labs#5186)
  token-js: added an e2e test for transferring using a mint with a transfer hook extension (solana-labs#5138)
  Serde optional dependencies clean-up (solana-labs#5181)
  build(deps): bump chrono from 0.4.27 to 0.4.28 (solana-labs#5180)
  stake-pool: Use unaligned types for safe pointer cast (solana-labs#5179)
  spl-pod: make code docs more explicit (solana-labs#5178)
  token-js: added extra account resolution for transfer hook extension (solana-labs#5112)
  Fix incorrect code doc (solana-labs#5177)
  Move Pod types to separate library (solana-labs#5119)
  build(deps-dev): bump @typescript-eslint/eslint-plugin from 6.4.1 to 6.5.0 in /memo/js (solana-labs#5176)
  build(deps): bump chrono from 0.4.26 to 0.4.27 (solana-labs#5171)
  build(deps-dev): bump prettier from 3.0.2 to 3.0.3 in /token-swap/js (solana-labs#5174)
  build(deps-dev): bump prettier from 3.0.2 to 3.0.3 in /token/js (solana-labs#5172)
  build(deps-dev): bump prettier from 3.0.2 to 3.0.3 in /token-lending/js (solana-labs#5173)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community Community contribution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

token-js: Fetch extra account metas for transfer-hooks on transfer
3 participants