-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Support associated token for JS (Also, make the program testable) #1364
Support associated token for JS (Also, make the program testable) #1364
Conversation
Unfortunately the type definitions in |
@@ -41,7 +43,7 @@ pub fn process_instruction( | |||
|
|||
let associated_token_account_signer_seeds: &[&[_]] = &[ | |||
&wallet_account_info.key.to_bytes(), | |||
&spl_token::id().to_bytes(), |
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.
So, spl-associated-program wrongly assumed spl_token
isn't position-independent-code; but it actually IS. spent some time to debug... I suspected myself.. It turned out I needed these program changes... my js was correct. lol
@@ -10,5 +10,6 @@ npm install | |||
npm run lint | |||
npm run flow | |||
npm run defs | |||
npm run test |
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.
argh, so these aren't running properly?: https://github.com/solana-labs/solana-program-library/pull/1364/files#diff-4c840378b893bfac59f5aeba8c8933d67df2080e4254f22a9330b1808d15e00dR35
&spl_token::id().to_bytes(), | ||
&spl_token_mint_address.to_bytes(), | ||
], | ||
get_associated_token_address_and_bump_seed_internal( |
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.
I believe these and following ./program
diffs should absolutely have no-functional-changes. :)
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.
Well, associated token program actually gets more flexible because it will not error for non-Tokenkeg program id's. But I think that's a nice change :) Do you mind adding the program change details to the PR title and description for posterity?
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.
yeah, that's right. I meant in the context of real production environment (mainnet-beta). :)
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.
Also, the subtlety is that deployed program and this helper get_associated_token_address
are still hard-coded to the Tokenkeg program 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.
Ah, I thought you planned to deploy an update to associated token program at some point. Are the program changes just for local testing then?
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.
@jstarry According to this (https://explorer.solana.com/address/ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL), the associated token program isn't upgradable? ;) I wonder it's worth side-loading it... Also, I came up with some niceties: #1371
console.log( | ||
' Note: To reload program remove client/util/store/config.json', |
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.
no hard-coding, please. :)
info = await connection.getAccountInfo(programId); | ||
assert(info != null); | ||
info = await connection.getAccountInfo(associatedProgramId); | ||
assert(info != null); |
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.
some extra check for stale caches (where associated programs aren't loaded.)
/** | ||
* The address of this account | ||
*/ | ||
address: PublicKey, |
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.
I needed this. Othwerwise, getOrCreateAssociatedAccountInfo
is too cumbersome to use.
* @param owner User account that will own the new account | ||
* @return Public key of the new associated account | ||
*/ | ||
async getOrCreateAssociatedAccountInfo( |
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.
Some must-to-have helper.. I realized doing right like this is too hard for most of partners, I think...
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.
Yes, this is super useful!
associatedAccount = await testToken.getOrCreateAssociatedAccountInfo(dest); | ||
assert(associatedAccount.amount.toNumber() === 0); | ||
|
||
await testToken.transferChecked( | ||
testAccount, | ||
associatedAccount.address, | ||
testAccountOwner, | ||
[], | ||
123, | ||
testTokenDecimals, | ||
); | ||
|
||
associatedAccount = await testToken.getOrCreateAssociatedAccountInfo(dest); | ||
assert(associatedAccount.amount.toNumber() === 123); |
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.
thanks to https://github.com/solana-labs/solana-program-library/pull/1364/files#r586222243, this is so simple for api users. :)
token/js/client/token.js
Outdated
try { | ||
return await this.getAccountInfo(associatedAddress); | ||
} catch (err) { | ||
if (err.message == FAILED_TO_FIND_ACCOUNT) { |
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.
I think it's worth handling the Invalid account owner
error as well because otherwise, partners won't be able to create associated token accounts for addresses that receive small lamport transfers
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.
yeah, another reason we need to publish this helper in this library. Too hard to write correct. ;)
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.
@ryoqun very useful work! looks great
* @param owner User account that will own the new account | ||
* @return Public key of the new associated account | ||
*/ | ||
async getOrCreateAssociatedAccountInfo( |
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.
Yes, this is super useful!
&spl_token::id().to_bytes(), | ||
&spl_token_mint_address.to_bytes(), | ||
], | ||
get_associated_token_address_and_bump_seed_internal( |
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.
Well, associated token program actually gets more flexible because it will not error for non-Tokenkeg program id's. But I think that's a nice change :) Do you mind adding the program change details to the PR title and description for posterity?
Can you also please bump the version in |
* @return Public key of the new associated account | ||
*/ | ||
async getOrCreateAssociatedAccountInfo( | ||
owner: PublicKey, |
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.
btw, I thought about checking the owner to avoid nested associated tokens like this: https://explorer.solana.com/address/7RyJpfyH38Vi5PcnNp4Nmiikc1dnXKv3WFXFY5GoC3Nm
But, the check would need extra rpc round-trip...
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.
Yeah, it's gross. Anything can be the owner of a token account, including tokens haha. I think it's better to leave that out, some day tokens owning tokens will probably be a feature that someone wants 😉
// Assuming program derived addressing is safe, this is the only case | ||
// for the INVALID_ACCOUNT_OWNER in this code-path | ||
if ( | ||
err.message === FAILED_TO_FIND_ACCOUNT || |
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.
(small fyi: btw because we take no user-provided callbacks, this is safe; otherwise, we must guard against throw undefined
, iirc; nasty JavaScript... lol)
Problem
Changes
./program
's NFC modifications.