spl_token.sol 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // SPDX-License-Identifier: Apache-2.0
  2. // Disclaimer: This library provides a way for Solidity to interact with Solana's SPL-Token. Although it is production ready,
  3. // it has not been audited for security, so use it at your own risk.
  4. import 'solana';
  5. library SplToken {
  6. address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
  7. enum TokenInstruction {
  8. InitializeMint, // 0
  9. InitializeAccount, // 1
  10. InitializeMultisig, // 2
  11. Transfer, // 3
  12. Approve, // 4
  13. Revoke, // 5
  14. SetAuthority, // 6
  15. MintTo, // 7
  16. Burn, // 8
  17. CloseAccount, // 9
  18. FreezeAccount, // 10
  19. ThawAccount, // 11
  20. TransferChecked, // 12
  21. ApproveChecked, // 13
  22. MintToChecked, // 14
  23. BurnChecked, // 15
  24. InitializeAccount2, // 16
  25. SyncNative, // 17
  26. InitializeAccount3, // 18
  27. InitializeMultisig2, // 19
  28. InitializeMint2, // 20
  29. GetAccountDataSize, // 21
  30. InitializeImmutableOwner, // 22
  31. AmountToUiAmount, // 23
  32. UiAmountToAmount, // 24
  33. InitializeMintCloseAuthority, // 25
  34. TransferFeeExtension, // 26
  35. ConfidentialTransferExtension, // 27
  36. DefaultAccountStateExtension, // 28
  37. Reallocate, // 29
  38. MemoTransferExtension, // 30
  39. CreateNativeMint // 31
  40. }
  41. /// Mint new tokens. The transaction should be signed by the mint authority keypair
  42. ///
  43. /// @param mint the account of the mint
  44. /// @param account the token account where the minted tokens should go
  45. /// @param authority the public key of the mint authority
  46. /// @param amount the amount of tokens to mint
  47. function mint_to(address mint, address account, address authority, uint64 amount) internal {
  48. bytes instr = new bytes(9);
  49. instr[0] = uint8(TokenInstruction.MintTo);
  50. instr.writeUint64LE(amount, 1);
  51. AccountMeta[3] metas = [
  52. AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
  53. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  54. AccountMeta({pubkey: authority, is_writable: false, is_signer: true})
  55. ];
  56. tokenProgramId.call{accounts: metas}(instr);
  57. }
  58. /// Transfer @amount token from @from to @to. The transaction should be signed by the owner
  59. /// keypair of the from account.
  60. ///
  61. /// @param from the account to transfer tokens from
  62. /// @param to the account to transfer tokens to
  63. /// @param owner the publickey of the from account owner keypair
  64. /// @param amount the amount to transfer
  65. function transfer(address from, address to, address owner, uint64 amount) internal {
  66. bytes instr = new bytes(9);
  67. instr[0] = uint8(TokenInstruction.Transfer);
  68. instr.writeUint64LE(amount, 1);
  69. AccountMeta[3] metas = [
  70. AccountMeta({pubkey: from, is_writable: true, is_signer: false}),
  71. AccountMeta({pubkey: to, is_writable: true, is_signer: false}),
  72. AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
  73. ];
  74. tokenProgramId.call{accounts: metas}(instr);
  75. }
  76. /// Burn @amount tokens in account. This transaction should be signed by the owner.
  77. ///
  78. /// @param account the acount for which tokens should be burned
  79. /// @param mint the mint for this token
  80. /// @param owner the publickey of the account owner keypair
  81. /// @param amount the amount to burn
  82. function burn(address account, address mint, address owner, uint64 amount) internal {
  83. bytes instr = new bytes(9);
  84. instr[0] = uint8(TokenInstruction.Burn);
  85. instr.writeUint64LE(amount, 1);
  86. AccountMeta[3] metas = [
  87. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  88. AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
  89. AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
  90. ];
  91. tokenProgramId.call{accounts: metas}(instr);
  92. }
  93. /// Approve an amount to a delegate. This transaction should be signed by the owner
  94. ///
  95. /// @param account the account for which a delegate should be approved
  96. /// @param delegate the delegate publickey
  97. /// @param owner the publickey of the account owner keypair
  98. /// @param amount the amount to approve
  99. function approve(address account, address delegate, address owner, uint64 amount) internal {
  100. bytes instr = new bytes(9);
  101. instr[0] = uint8(TokenInstruction.Approve);
  102. instr.writeUint64LE(amount, 1);
  103. AccountMeta[3] metas = [
  104. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  105. AccountMeta({pubkey: delegate, is_writable: false, is_signer: false}),
  106. AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
  107. ];
  108. tokenProgramId.call{accounts: metas}(instr);
  109. }
  110. /// Revoke a previously approved delegate. This transaction should be signed by the owner. After
  111. /// this transaction, no delgate is approved for any amount.
  112. ///
  113. /// @param account the account for which a delegate should be approved
  114. /// @param owner the publickey of the account owner keypair
  115. function revoke(address account, address owner) internal {
  116. bytes instr = new bytes(1);
  117. instr[0] = uint8(TokenInstruction.Revoke);
  118. AccountMeta[2] metas = [
  119. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  120. AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
  121. ];
  122. tokenProgramId.call{accounts: metas}(instr);
  123. }
  124. /// Get the total supply for the mint, i.e. the total amount in circulation
  125. /// @param mint the mint for this token
  126. function total_supply(address mint) internal view returns (uint64) {
  127. AccountInfo account = get_account_info(mint);
  128. return account.data.readUint64LE(36);
  129. }
  130. /// Get the balance for an account.
  131. ///
  132. /// @param account the account for which we want to know a balance
  133. function get_balance(address account) internal view returns (uint64) {
  134. AccountInfo ai = get_account_info(account);
  135. return ai.data.readUint64LE(64);
  136. }
  137. /// Get the account info for an account. This walks the transaction account infos
  138. /// and find the account info, or the transaction fails.
  139. ///
  140. /// @param account the account for which we want to have the acount info.
  141. function get_account_info(address account) internal view returns (AccountInfo) {
  142. for (uint64 i = 0; i < tx.accounts.length; i++) {
  143. AccountInfo ai = tx.accounts[i];
  144. if (ai.key == account) {
  145. return ai;
  146. }
  147. }
  148. revert("account missing");
  149. }
  150. /// This enum represents the state of a token account
  151. enum AccountState {
  152. Uninitialized,
  153. Initialized,
  154. Frozen
  155. }
  156. /// This struct is the return of 'get_token_account_data'
  157. struct TokenAccountData {
  158. address mintAccount;
  159. address owner;
  160. uint64 balance;
  161. bool delegate_present;
  162. address delegate;
  163. AccountState state;
  164. bool is_native_present;
  165. uint64 is_native;
  166. uint64 delegated_amount;
  167. bool close_authority_present;
  168. address close_authority;
  169. }
  170. /// Fetch the owner, mint account and balance for an associated token account.
  171. ///
  172. /// @param tokenAccount The token account
  173. /// @return struct TokenAccountData
  174. function get_token_account_data(address tokenAccount) public view returns (TokenAccountData) {
  175. AccountInfo ai = get_account_info(tokenAccount);
  176. TokenAccountData data = TokenAccountData(
  177. {
  178. mintAccount: ai.data.readAddress(0),
  179. owner: ai.data.readAddress(32),
  180. balance: ai.data.readUint64LE(64),
  181. delegate_present: ai.data.readUint32LE(72) > 0,
  182. delegate: ai.data.readAddress(76),
  183. state: AccountState(ai.data[108]),
  184. is_native_present: ai.data.readUint32LE(109) > 0,
  185. is_native: ai.data.readUint64LE(113),
  186. delegated_amount: ai.data.readUint64LE(121),
  187. close_authority_present: ai.data.readUint32LE(129) > 0,
  188. close_authority: ai.data.readAddress(133)
  189. }
  190. );
  191. return data;
  192. }
  193. // This struct is the return of 'get_mint_account_data'
  194. struct MintAccountData {
  195. bool authority_present;
  196. address mint_authority;
  197. uint64 supply;
  198. uint8 decimals;
  199. bool is_initialized;
  200. bool freeze_authority_present;
  201. address freeze_authority;
  202. }
  203. /// Retrieve the information saved in a mint account
  204. ///
  205. /// @param mintAccount the account whose information we want to retrive
  206. /// @return the MintAccountData struct
  207. function get_mint_account_data(address mintAccount) public view returns (MintAccountData) {
  208. AccountInfo ai = get_account_info(mintAccount);
  209. uint32 authority_present = ai.data.readUint32LE(0);
  210. uint32 freeze_authority_present = ai.data.readUint32LE(46);
  211. MintAccountData data = MintAccountData( {
  212. authority_present: authority_present > 0,
  213. mint_authority: ai.data.readAddress(4),
  214. supply: ai.data.readUint64LE(36),
  215. decimals: uint8(ai.data[44]),
  216. is_initialized: ai.data[45] > 0,
  217. freeze_authority_present: freeze_authority_present > 0,
  218. freeze_authority: ai.data.readAddress(50)
  219. });
  220. return data;
  221. }
  222. // A mint account has an authority, whose type is one of the members of this struct.
  223. enum AuthorityType {
  224. MintTokens,
  225. FreezeAccount,
  226. AccountOwner,
  227. CloseAccount
  228. }
  229. /// Remove the mint authority from a mint account
  230. ///
  231. /// @param mintAccount the public key for the mint account
  232. /// @param mintAuthority the public for the mint authority
  233. function remove_mint_authority(address mintAccount, address mintAuthority) public {
  234. AccountMeta[2] metas = [
  235. AccountMeta({pubkey: mintAccount, is_signer: false, is_writable: true}),
  236. AccountMeta({pubkey: mintAuthority, is_signer: true, is_writable: false})
  237. ];
  238. bytes data = new bytes(3);
  239. data[0] = uint8(TokenInstruction.SetAuthority);
  240. data[1] = uint8(AuthorityType.MintTokens);
  241. data[2] = 0;
  242. tokenProgramId.call{accounts: metas}(data);
  243. }
  244. }