|
@@ -0,0 +1,387 @@
|
|
|
+import 'solana';
|
|
|
+import 'system_instruction.sol';
|
|
|
+
|
|
|
+library SplToken {
|
|
|
+ address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
|
+ address constant associatedTokenProgramId = address"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
|
+ address constant rentAddress = address"SysvarRent111111111111111111111111111111111";
|
|
|
+ enum TokenInstruction {
|
|
|
+ InitializeMint, // 0
|
|
|
+ InitializeAccount, // 1
|
|
|
+ InitializeMultisig, // 2
|
|
|
+ Transfer, // 3
|
|
|
+ Approve, // 4
|
|
|
+ Revoke, // 5
|
|
|
+ SetAuthority, // 6
|
|
|
+ MintTo, // 7
|
|
|
+ Burn, // 8
|
|
|
+ CloseAccount, // 9
|
|
|
+ FreezeAccount, // 10
|
|
|
+ ThawAccount, // 11
|
|
|
+ TransferChecked, // 12
|
|
|
+ ApproveChecked, // 13
|
|
|
+ MintToChecked, // 14
|
|
|
+ BurnChecked, // 15
|
|
|
+ InitializeAccount2, // 16
|
|
|
+ SyncNative, // 17
|
|
|
+ InitializeAccount3, // 18
|
|
|
+ InitializeMultisig2, // 19
|
|
|
+ InitializeMint2, // 20
|
|
|
+ GetAccountDataSize, // 21
|
|
|
+ InitializeImmutableOwner, // 22
|
|
|
+ AmountToUiAmount, // 23
|
|
|
+ UiAmountToAmount, // 24
|
|
|
+ InitializeMintCloseAuthority, // 25
|
|
|
+ TransferFeeExtension, // 26
|
|
|
+ ConfidentialTransferExtension, // 27
|
|
|
+ DefaultAccountStateExtension, // 28
|
|
|
+ Reallocate, // 29
|
|
|
+ MemoTransferExtension, // 30
|
|
|
+ CreateNativeMint // 31
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Initialize a new token account.
|
|
|
+ ///
|
|
|
+ /// @param tokenAccount the public key of the token account to initialize
|
|
|
+ /// @param mint the public key of the mint account for this new token account
|
|
|
+ /// @param owner the public key of the owner of this new token account
|
|
|
+ function initialize_account(address tokenAccount, address mint, address owner) internal view{
|
|
|
+ bytes instr = new bytes(1);
|
|
|
+
|
|
|
+ instr[0] = uint8(TokenInstruction.InitializeAccount);
|
|
|
+ AccountMeta[4] metas = [
|
|
|
+ AccountMeta({pubkey: tokenAccount, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: mint, is_writable: false, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: owner, is_writable: false, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: rentAddress, is_writable: false, is_signer: false})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Initialize a new associated token account.
|
|
|
+ ///
|
|
|
+ /// @param payer the public key of the payer to create the associated token account
|
|
|
+ /// @param tokenAccount the public key of the token account to initialize
|
|
|
+ /// @param mint the public key of the mint account for this new token account
|
|
|
+ /// @param owner the public key of the owner of this new token account
|
|
|
+ function create_associated_token_account(address payer, address tokenAccount, address mint, address owner) internal view {
|
|
|
+ AccountMeta[6] metas = [
|
|
|
+ AccountMeta({pubkey: payer, is_writable: true, is_signer: true}),
|
|
|
+ AccountMeta({pubkey: tokenAccount, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: owner, is_writable: false, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: mint, is_writable: false, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: SystemInstruction.systemAddress, is_writable: false, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: SplToken.tokenProgramId, is_writable: false, is_signer: false})
|
|
|
+ ];
|
|
|
+
|
|
|
+ bytes instructionData = abi.encode((0));
|
|
|
+ associatedTokenProgramId.call{accounts: metas}(instructionData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Initialize mint instruction data
|
|
|
+ struct InitializeMintInstruction {
|
|
|
+ uint8 instruction;
|
|
|
+ uint8 decimals;
|
|
|
+ address mintAuthority;
|
|
|
+ uint8 freezeAuthorityOption;
|
|
|
+ address freezeAuthority;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Initialize a new mint account.
|
|
|
+ ///
|
|
|
+ /// @param mint the public key of the mint account to initialize
|
|
|
+ /// @param mintAuthority the public key of the mint authority
|
|
|
+ /// @param freezeAuthority the public key of the freeze authority
|
|
|
+ /// @param decimals the decimals of the mint
|
|
|
+ function initialize_mint(address mint, address mintAuthority, address freezeAuthority, uint8 decimals) internal view {
|
|
|
+ InitializeMintInstruction instr = InitializeMintInstruction({
|
|
|
+ instruction: 20,
|
|
|
+ decimals: decimals,
|
|
|
+ mintAuthority: mintAuthority,
|
|
|
+ freezeAuthorityOption: 1,
|
|
|
+ freezeAuthority: freezeAuthority
|
|
|
+ });
|
|
|
+
|
|
|
+ AccountMeta[1] metas = [
|
|
|
+ AccountMeta({pubkey: mint, is_writable: true, is_signer: false})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Create and initialize a new mint account in one instruction
|
|
|
+ ///
|
|
|
+ /// @param payer the public key of the account paying to create the mint account
|
|
|
+ /// @param mint the public key of the mint account to initialize
|
|
|
+ /// @param mintAuthority the public key of the mint authority
|
|
|
+ /// @param freezeAuthority the public key of the freeze authority
|
|
|
+ /// @param decimals the decimals of the mint
|
|
|
+ function create_mint(address payer, address mint, address mintAuthority, address freezeAuthority, uint8 decimals) internal view {
|
|
|
+ // Invoke System Program to create a new account for the mint account
|
|
|
+ // Program owner is set to the Token program
|
|
|
+ SystemInstruction.create_account(
|
|
|
+ payer, // lamports sent from this account (payer)
|
|
|
+ mint, // lamports sent to this account (account to be created)
|
|
|
+ 1461600, // lamport amount (minimum lamports for mint account)
|
|
|
+ 82, // space required for the account (mint account)
|
|
|
+ SplToken.tokenProgramId // new program owner
|
|
|
+ );
|
|
|
+
|
|
|
+ InitializeMintInstruction instr = InitializeMintInstruction({
|
|
|
+ instruction: 20,
|
|
|
+ decimals: decimals,
|
|
|
+ mintAuthority: mintAuthority,
|
|
|
+ freezeAuthorityOption: 1,
|
|
|
+ freezeAuthority: freezeAuthority
|
|
|
+ });
|
|
|
+
|
|
|
+ AccountMeta[1] metas = [
|
|
|
+ AccountMeta({pubkey: mint, is_writable: true, is_signer: false})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Mint new tokens. The transaction should be signed by the mint authority keypair
|
|
|
+ ///
|
|
|
+ /// @param mint the account of the mint
|
|
|
+ /// @param account the token account where the minted tokens should go
|
|
|
+ /// @param authority the public key of the mint authority
|
|
|
+ /// @param amount the amount of tokens to mint
|
|
|
+ function mint_to(address mint, address account, address authority, uint64 amount) internal view {
|
|
|
+ bytes instr = new bytes(9);
|
|
|
+
|
|
|
+ instr[0] = uint8(TokenInstruction.MintTo);
|
|
|
+ instr.writeUint64LE(amount, 1);
|
|
|
+
|
|
|
+ AccountMeta[3] metas = [
|
|
|
+ AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: authority, is_writable: true, is_signer: true})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Transfer @amount token from @from to @to. The transaction should be signed by the owner
|
|
|
+ /// keypair of the from account.
|
|
|
+ ///
|
|
|
+ /// @param from the account to transfer tokens from
|
|
|
+ /// @param to the account to transfer tokens to
|
|
|
+ /// @param owner the publickey of the from account owner keypair
|
|
|
+ /// @param amount the amount to transfer
|
|
|
+ function transfer(address from, address to, address owner, uint64 amount) internal view {
|
|
|
+ bytes instr = new bytes(9);
|
|
|
+
|
|
|
+ instr[0] = uint8(TokenInstruction.Transfer);
|
|
|
+ instr.writeUint64LE(amount, 1);
|
|
|
+
|
|
|
+ AccountMeta[3] metas = [
|
|
|
+ AccountMeta({pubkey: from, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: to, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: owner, is_writable: true, is_signer: true})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Burn @amount tokens in account. This transaction should be signed by the owner.
|
|
|
+ ///
|
|
|
+ /// @param account the acount for which tokens should be burned
|
|
|
+ /// @param mint the mint for this token
|
|
|
+ /// @param owner the publickey of the account owner keypair
|
|
|
+ /// @param amount the amount to transfer
|
|
|
+ function burn(address account, address mint, address owner, uint64 amount) internal view {
|
|
|
+ bytes instr = new bytes(9);
|
|
|
+
|
|
|
+ instr[0] = uint8(TokenInstruction.Burn);
|
|
|
+ instr.writeUint64LE(amount, 1);
|
|
|
+
|
|
|
+ AccountMeta[3] metas = [
|
|
|
+ AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: owner, is_writable: true, is_signer: true})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Approve an amount to a delegate. This transaction should be signed by the owner
|
|
|
+ ///
|
|
|
+ /// @param account the account for which a delegate should be approved
|
|
|
+ /// @param delegate the delegate publickey
|
|
|
+ /// @param owner the publickey of the account owner keypair
|
|
|
+ /// @param amount the amount to approve
|
|
|
+ function approve(address account, address delegate, address owner, uint64 amount) internal view {
|
|
|
+ bytes instr = new bytes(9);
|
|
|
+
|
|
|
+ instr[0] = uint8(TokenInstruction.Approve);
|
|
|
+ instr.writeUint64LE(amount, 1);
|
|
|
+
|
|
|
+ AccountMeta[3] metas = [
|
|
|
+ AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: delegate, is_writable: false, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Revoke a previously approved delegate. This transaction should be signed by the owner. After
|
|
|
+ /// this transaction, no delgate is approved for any amount.
|
|
|
+ ///
|
|
|
+ /// @param account the account for which a delegate should be approved
|
|
|
+ /// @param owner the publickey of the account owner keypair
|
|
|
+ function revoke(address account, address owner) internal view {
|
|
|
+ bytes instr = new bytes(1);
|
|
|
+
|
|
|
+ instr[0] = uint8(TokenInstruction.Revoke);
|
|
|
+
|
|
|
+ AccountMeta[2] metas = [
|
|
|
+ AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
|
|
|
+ AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
|
|
|
+ ];
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(instr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Get the total supply for the mint, i.e. the total amount in circulation
|
|
|
+ /// @param mint the mint for this token
|
|
|
+ function total_supply(address mint) internal view returns (uint64) {
|
|
|
+ AccountInfo account = get_account_info(mint);
|
|
|
+
|
|
|
+ return account.data.readUint64LE(36);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Get the balance for an account.
|
|
|
+ ///
|
|
|
+ /// @param account the account for which we want to know a balance
|
|
|
+ function get_balance(address account) internal view returns (uint64) {
|
|
|
+ AccountInfo ai = get_account_info(account);
|
|
|
+
|
|
|
+ return ai.data.readUint64LE(64);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Get the account info for an account. This walks the transaction account infos
|
|
|
+ /// and find the account info, or the transaction fails.
|
|
|
+ ///
|
|
|
+ /// @param account the account for which we want to have the acount info.
|
|
|
+ function get_account_info(address account) internal view returns (AccountInfo) {
|
|
|
+ for (uint64 i = 0; i < tx.accounts.length; i++) {
|
|
|
+ AccountInfo ai = tx.accounts[i];
|
|
|
+ if (ai.key == account) {
|
|
|
+ return ai;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ revert("account missing");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// This enum represents the state of a token account
|
|
|
+ enum AccountState {
|
|
|
+ Uninitialized,
|
|
|
+ Initialized,
|
|
|
+ Frozen
|
|
|
+ }
|
|
|
+
|
|
|
+ /// This struct is the return of 'get_token_account_data'
|
|
|
+ struct TokenAccountData {
|
|
|
+ address mintAccount;
|
|
|
+ address owner;
|
|
|
+ uint64 balance;
|
|
|
+ bool delegate_present;
|
|
|
+ address delegate;
|
|
|
+ AccountState state;
|
|
|
+ bool is_native_present;
|
|
|
+ uint64 is_native;
|
|
|
+ uint64 delegated_amount;
|
|
|
+ bool close_authority_present;
|
|
|
+ address close_authority;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Fetch the owner, mint account and balance for an associated token account.
|
|
|
+ ///
|
|
|
+ /// @param tokenAccount The token account
|
|
|
+ /// @return struct TokenAccountData
|
|
|
+ function get_token_account_data(address tokenAccount) public view returns (TokenAccountData) {
|
|
|
+ AccountInfo ai = get_account_info(tokenAccount);
|
|
|
+
|
|
|
+ TokenAccountData data = TokenAccountData(
|
|
|
+ {
|
|
|
+ mintAccount: ai.data.readAddress(0),
|
|
|
+ owner: ai.data.readAddress(32),
|
|
|
+ balance: ai.data.readUint64LE(64),
|
|
|
+ delegate_present: ai.data.readUint32LE(72) > 0,
|
|
|
+ delegate: ai.data.readAddress(76),
|
|
|
+ state: AccountState(ai.data[108]),
|
|
|
+ is_native_present: ai.data.readUint32LE(109) > 0,
|
|
|
+ is_native: ai.data.readUint64LE(113),
|
|
|
+ delegated_amount: ai.data.readUint64LE(121),
|
|
|
+ close_authority_present: ai.data.readUint32LE(129) > 10,
|
|
|
+ close_authority: ai.data.readAddress(133)
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This struct is the return of 'get_mint_account_data'
|
|
|
+ struct MintAccountData {
|
|
|
+ bool authority_present;
|
|
|
+ address mint_authority;
|
|
|
+ uint64 supply;
|
|
|
+ uint8 decimals;
|
|
|
+ bool is_initialized;
|
|
|
+ bool freeze_authority_present;
|
|
|
+ address freeze_authority;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Retrieve the information saved in a mint account
|
|
|
+ ///
|
|
|
+ /// @param mintAccount the account whose information we want to retrive
|
|
|
+ /// @return the MintAccountData struct
|
|
|
+ function get_mint_account_data(address mintAccount) public view returns (MintAccountData) {
|
|
|
+ AccountInfo ai = get_account_info(mintAccount);
|
|
|
+
|
|
|
+ uint32 authority_present = ai.data.readUint32LE(0);
|
|
|
+ uint32 freeze_authority_present = ai.data.readUint32LE(46);
|
|
|
+ MintAccountData data = MintAccountData( {
|
|
|
+ authority_present: authority_present > 0,
|
|
|
+ mint_authority: ai.data.readAddress(4),
|
|
|
+ supply: ai.data.readUint64LE(36),
|
|
|
+ decimals: uint8(ai.data[44]),
|
|
|
+ is_initialized: ai.data[45] > 0,
|
|
|
+ freeze_authority_present: freeze_authority_present > 0,
|
|
|
+ freeze_authority: ai.data.readAddress(50)
|
|
|
+ });
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ // A mint account has an authority, whose type is one of the members of this struct.
|
|
|
+ enum AuthorityType {
|
|
|
+ MintTokens,
|
|
|
+ FreezeAccount,
|
|
|
+ AccountOwner,
|
|
|
+ CloseAccount
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Remove the mint authority from a mint account
|
|
|
+ ///
|
|
|
+ /// @param mintAccount the public key for the mint account
|
|
|
+ /// @param mintAuthority the public for the mint authority
|
|
|
+ function remove_mint_authority(address mintAccount, address mintAuthority) public view {
|
|
|
+ AccountMeta[2] metas = [
|
|
|
+ AccountMeta({pubkey: mintAccount, is_signer: false, is_writable: true}),
|
|
|
+ AccountMeta({pubkey: mintAuthority, is_signer: true, is_writable: false})
|
|
|
+ ];
|
|
|
+
|
|
|
+ bytes data = new bytes(9);
|
|
|
+ data[0] = uint8(TokenInstruction.SetAuthority);
|
|
|
+ data[1] = uint8(AuthorityType.MintTokens);
|
|
|
+ data[3] = 0;
|
|
|
+
|
|
|
+ tokenProgramId.call{accounts: metas}(data);
|
|
|
+ }
|
|
|
+}
|