Created
April 2, 2022 17:35
-
-
Save arcticmatt/0f90aaa2a416aa0a02ab520372b55750 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Connection, | |
ParsedInstruction, | |
ParsedTransactionWithMeta, | |
PartiallyDecodedInstruction, | |
PublicKey, | |
} from "@solana/web3.js"; | |
import { Maybe } from "formfn-shared/dist/types/UtilityTypes"; | |
import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; | |
type TokenAccountInfo = { | |
delegate: string; | |
delegatedAmount: { | |
amount: string; | |
decimals: number; | |
uiAmount: number; | |
uiAmountString: string; | |
}; | |
isNative: boolean; | |
mint: string; | |
owner: string; | |
state: string; | |
tokenAmount: { | |
amount: string; | |
decimals: number; | |
uiAmount: number; | |
uiAmountString: string; | |
}; | |
}; | |
async function getTokenAccountInfo( | |
connection: Connection, | |
tokenAccount: PublicKey | |
): Promise<Maybe<TokenAccountInfo>> { | |
const parsedAccountInfo = await connection.getParsedAccountInfo(tokenAccount); | |
const data = parsedAccountInfo?.value?.data; | |
if (data != null && "parsed" in data) { | |
return data.parsed.info; | |
} | |
return null; | |
} | |
function getAllInnerIxs(parsedTx: ParsedTransactionWithMeta) { | |
return parsedTx.meta?.innerInstructions?.reduce( | |
(acc: Array<ParsedInstruction | PartiallyDecodedInstruction>, currVal) => [ | |
...acc, | |
...currVal.instructions, | |
], | |
[] | |
); | |
} | |
function isInitializeAccountIx( | |
ix: ParsedInstruction | PartiallyDecodedInstruction, | |
account?: string | |
) { | |
return ( | |
ix.programId.toString() === TOKEN_PROGRAM_ID.toString() && | |
(ix as ParsedInstruction).parsed?.type === "initializeAccount" && | |
(account == null || | |
(ix as ParsedInstruction).parsed?.info?.account?.toString() === account) | |
); | |
} | |
export default async function getTokenAccountOwner( | |
connection: Connection, | |
tokenAccount: PublicKey | |
): Promise<Maybe<string>> { | |
// First, try to get the token account info. May return null if account has been closed. | |
const tokenAccountInfo = await getTokenAccountInfo(connection, tokenAccount); | |
if (tokenAccountInfo != null) { | |
return tokenAccountInfo.owner; | |
} | |
const signatures = await connection.getConfirmedSignaturesForAddress2( | |
tokenAccount | |
); | |
if (signatures.length === 0) { | |
return null; | |
} | |
// Parse the first tx, which should have created the token account | |
const firstSignature = signatures[signatures.length - 1]; | |
const parsedTx = await connection.getParsedTransaction( | |
firstSignature.signature, | |
"confirmed" | |
); | |
if (parsedTx == null) { | |
return null; | |
} | |
const ixs = parsedTx.transaction.message.instructions; | |
const allInnerIxs = getAllInnerIxs(parsedTx) ?? []; | |
const initializeAccountIx = [...ixs, ...allInnerIxs].find((ix) => | |
isInitializeAccountIx(ix, tokenAccount.toString()) | |
); | |
if (initializeAccountIx == null) { | |
return null; | |
} | |
return ( | |
initializeAccountIx as ParsedInstruction | |
).parsed?.info?.owner?.toString(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment