spl_token.sol 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import 'solana';
  2. import './system_instruction.sol';
  3. library SplToken {
  4. address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
  5. address constant associatedTokenProgramId = address"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
  6. address constant rentAddress = address"SysvarRent111111111111111111111111111111111";
  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. /// Initialize a new token account.
  42. ///
  43. /// @param tokenAccount the public key of the token account to initialize
  44. /// @param mint the public key of the mint account for this new token account
  45. /// @param owner the public key of the owner of this new token account
  46. function initialize_account(address tokenAccount, address mint, address owner) internal{
  47. bytes instr = new bytes(1);
  48. instr[0] = uint8(TokenInstruction.InitializeAccount);
  49. AccountMeta[4] metas = [
  50. AccountMeta({pubkey: tokenAccount, is_writable: true, is_signer: false}),
  51. AccountMeta({pubkey: mint, is_writable: false, is_signer: false}),
  52. AccountMeta({pubkey: owner, is_writable: false, is_signer: false}),
  53. AccountMeta({pubkey: rentAddress, is_writable: false, is_signer: false})
  54. ];
  55. tokenProgramId.call{accounts: metas}(instr);
  56. }
  57. /// Initialize a new associated token account.
  58. ///
  59. /// @param payer the public key of the payer to create the associated token account
  60. /// @param tokenAccount the public key of the token account to initialize
  61. /// @param mint the public key of the mint account for this new token account
  62. /// @param owner the public key of the owner of this new token account
  63. function create_associated_token_account(address payer, address tokenAccount, address mint, address owner) internal {
  64. AccountMeta[6] metas = [
  65. AccountMeta({pubkey: payer, is_writable: true, is_signer: true}),
  66. AccountMeta({pubkey: tokenAccount, is_writable: true, is_signer: false}),
  67. AccountMeta({pubkey: owner, is_writable: false, is_signer: false}),
  68. AccountMeta({pubkey: mint, is_writable: false, is_signer: false}),
  69. AccountMeta({pubkey: SystemInstruction.systemAddress, is_writable: false, is_signer: false}),
  70. AccountMeta({pubkey: SplToken.tokenProgramId, is_writable: false, is_signer: false})
  71. ];
  72. bytes instructionData = abi.encode((0));
  73. associatedTokenProgramId.call{accounts: metas}(instructionData);
  74. }
  75. // Initialize mint instruction data
  76. struct InitializeMintInstruction {
  77. uint8 instruction;
  78. uint8 decimals;
  79. address mintAuthority;
  80. uint8 freezeAuthorityOption;
  81. address freezeAuthority;
  82. }
  83. /// Initialize a new mint account.
  84. ///
  85. /// @param mint the public key of the mint account to initialize
  86. /// @param mintAuthority the public key of the mint authority
  87. /// @param freezeAuthority the public key of the freeze authority
  88. /// @param decimals the decimals of the mint
  89. function initialize_mint(address mint, address mintAuthority, address freezeAuthority, uint8 decimals) internal {
  90. InitializeMintInstruction instr = InitializeMintInstruction({
  91. instruction: 20,
  92. decimals: decimals,
  93. mintAuthority: mintAuthority,
  94. freezeAuthorityOption: 1,
  95. freezeAuthority: freezeAuthority
  96. });
  97. AccountMeta[1] metas = [
  98. AccountMeta({pubkey: mint, is_writable: true, is_signer: false})
  99. ];
  100. tokenProgramId.call{accounts: metas}(instr);
  101. }
  102. /// Create and initialize a new mint account in one instruction
  103. ///
  104. /// @param payer the public key of the account paying to create the mint account
  105. /// @param mint the public key of the mint account to initialize
  106. /// @param mintAuthority the public key of the mint authority
  107. /// @param freezeAuthority the public key of the freeze authority
  108. /// @param decimals the decimals of the mint
  109. function create_mint(address payer, address mint, address mintAuthority, address freezeAuthority, uint8 decimals) internal {
  110. // Invoke System Program to create a new account for the mint account
  111. // Program owner is set to the Token program
  112. SystemInstruction.create_account(
  113. payer, // lamports sent from this account (payer)
  114. mint, // lamports sent to this account (account to be created)
  115. 1461600, // lamport amount (minimum lamports for mint account)
  116. 82, // space required for the account (mint account)
  117. SplToken.tokenProgramId // new program owner
  118. );
  119. InitializeMintInstruction instr = InitializeMintInstruction({
  120. instruction: 20,
  121. decimals: decimals,
  122. mintAuthority: mintAuthority,
  123. freezeAuthorityOption: 1,
  124. freezeAuthority: freezeAuthority
  125. });
  126. AccountMeta[1] metas = [
  127. AccountMeta({pubkey: mint, is_writable: true, is_signer: false})
  128. ];
  129. tokenProgramId.call{accounts: metas}(instr);
  130. }
  131. /// Mint new tokens. The transaction should be signed by the mint authority keypair
  132. ///
  133. /// @param mint the account of the mint
  134. /// @param account the token account where the minted tokens should go
  135. /// @param authority the public key of the mint authority
  136. /// @param amount the amount of tokens to mint
  137. function mint_to(address mint, address account, address authority, uint64 amount) internal {
  138. bytes instr = new bytes(9);
  139. instr[0] = uint8(TokenInstruction.MintTo);
  140. instr.writeUint64LE(amount, 1);
  141. AccountMeta[3] metas = [
  142. AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
  143. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  144. AccountMeta({pubkey: authority, is_writable: true, is_signer: true})
  145. ];
  146. tokenProgramId.call{accounts: metas}(instr);
  147. }
  148. /// Transfer @amount token from @from to @to. The transaction should be signed by the owner
  149. /// keypair of the from account.
  150. ///
  151. /// @param from the account to transfer tokens from
  152. /// @param to the account to transfer tokens to
  153. /// @param owner the publickey of the from account owner keypair
  154. /// @param amount the amount to transfer
  155. function transfer(address from, address to, address owner, uint64 amount) internal {
  156. bytes instr = new bytes(9);
  157. instr[0] = uint8(TokenInstruction.Transfer);
  158. instr.writeUint64LE(amount, 1);
  159. AccountMeta[3] metas = [
  160. AccountMeta({pubkey: from, is_writable: true, is_signer: false}),
  161. AccountMeta({pubkey: to, is_writable: true, is_signer: false}),
  162. AccountMeta({pubkey: owner, is_writable: true, is_signer: true})
  163. ];
  164. tokenProgramId.call{accounts: metas}(instr);
  165. }
  166. /// Burn @amount tokens in account. This transaction should be signed by the owner.
  167. ///
  168. /// @param account the acount for which tokens should be burned
  169. /// @param mint the mint for this token
  170. /// @param owner the publickey of the account owner keypair
  171. /// @param amount the amount to transfer
  172. function burn(address account, address mint, address owner, uint64 amount) internal {
  173. bytes instr = new bytes(9);
  174. instr[0] = uint8(TokenInstruction.Burn);
  175. instr.writeUint64LE(amount, 1);
  176. AccountMeta[3] metas = [
  177. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  178. AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
  179. AccountMeta({pubkey: owner, is_writable: true, is_signer: true})
  180. ];
  181. tokenProgramId.call{accounts: metas}(instr);
  182. }
  183. /// Approve an amount to a delegate. This transaction should be signed by the owner
  184. ///
  185. /// @param account the account for which a delegate should be approved
  186. /// @param delegate the delegate publickey
  187. /// @param owner the publickey of the account owner keypair
  188. /// @param amount the amount to approve
  189. function approve(address account, address delegate, address owner, uint64 amount) internal {
  190. bytes instr = new bytes(9);
  191. instr[0] = uint8(TokenInstruction.Approve);
  192. instr.writeUint64LE(amount, 1);
  193. AccountMeta[3] metas = [
  194. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  195. AccountMeta({pubkey: delegate, is_writable: false, is_signer: false}),
  196. AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
  197. ];
  198. tokenProgramId.call{accounts: metas}(instr);
  199. }
  200. /// Revoke a previously approved delegate. This transaction should be signed by the owner. After
  201. /// this transaction, no delgate is approved for any amount.
  202. ///
  203. /// @param account the account for which a delegate should be approved
  204. /// @param owner the publickey of the account owner keypair
  205. function revoke(address account, address owner) internal {
  206. bytes instr = new bytes(1);
  207. instr[0] = uint8(TokenInstruction.Revoke);
  208. AccountMeta[2] metas = [
  209. AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
  210. AccountMeta({pubkey: owner, is_writable: false, is_signer: true})
  211. ];
  212. tokenProgramId.call{accounts: metas}(instr);
  213. }
  214. /// Get the total supply for the mint, i.e. the total amount in circulation
  215. /// @param mint the mint for this token
  216. function total_supply(address mint) internal view returns (uint64) {
  217. AccountInfo account = get_account_info(mint);
  218. return account.data.readUint64LE(36);
  219. }
  220. /// Get the balance for an account.
  221. ///
  222. /// @param account the account for which we want to know a balance
  223. function get_balance(address account) internal view returns (uint64) {
  224. AccountInfo ai = get_account_info(account);
  225. return ai.data.readUint64LE(64);
  226. }
  227. /// Get the account info for an account. This walks the transaction account infos
  228. /// and find the account info, or the transaction fails.
  229. ///
  230. /// @param account the account for which we want to have the acount info.
  231. function get_account_info(address account) internal view returns (AccountInfo) {
  232. for (uint64 i = 0; i < tx.accounts.length; i++) {
  233. AccountInfo ai = tx.accounts[i];
  234. if (ai.key == account) {
  235. return ai;
  236. }
  237. }
  238. revert("account missing");
  239. }
  240. /// This enum represents the state of a token account
  241. enum AccountState {
  242. Uninitialized,
  243. Initialized,
  244. Frozen
  245. }
  246. /// This struct is the return of 'get_token_account_data'
  247. struct TokenAccountData {
  248. address mintAccount;
  249. address owner;
  250. uint64 balance;
  251. bool delegate_present;
  252. address delegate;
  253. AccountState state;
  254. bool is_native_present;
  255. uint64 is_native;
  256. uint64 delegated_amount;
  257. bool close_authority_present;
  258. address close_authority;
  259. }
  260. /// Fetch the owner, mint account and balance for an associated token account.
  261. ///
  262. /// @param tokenAccount The token account
  263. /// @return struct TokenAccountData
  264. function get_token_account_data(address tokenAccount) public view returns (TokenAccountData) {
  265. AccountInfo ai = get_account_info(tokenAccount);
  266. TokenAccountData data = TokenAccountData(
  267. {
  268. mintAccount: ai.data.readAddress(0),
  269. owner: ai.data.readAddress(32),
  270. balance: ai.data.readUint64LE(64),
  271. delegate_present: ai.data.readUint32LE(72) > 0,
  272. delegate: ai.data.readAddress(76),
  273. state: AccountState(ai.data[108]),
  274. is_native_present: ai.data.readUint32LE(109) > 0,
  275. is_native: ai.data.readUint64LE(113),
  276. delegated_amount: ai.data.readUint64LE(121),
  277. close_authority_present: ai.data.readUint32LE(129) > 10,
  278. close_authority: ai.data.readAddress(133)
  279. }
  280. );
  281. return data;
  282. }
  283. // This struct is the return of 'get_mint_account_data'
  284. struct MintAccountData {
  285. bool authority_present;
  286. address mint_authority;
  287. uint64 supply;
  288. uint8 decimals;
  289. bool is_initialized;
  290. bool freeze_authority_present;
  291. address freeze_authority;
  292. }
  293. /// Retrieve the information saved in a mint account
  294. ///
  295. /// @param mintAccount the account whose information we want to retrive
  296. /// @return the MintAccountData struct
  297. function get_mint_account_data(address mintAccount) public view returns (MintAccountData) {
  298. AccountInfo ai = get_account_info(mintAccount);
  299. uint32 authority_present = ai.data.readUint32LE(0);
  300. uint32 freeze_authority_present = ai.data.readUint32LE(46);
  301. MintAccountData data = MintAccountData( {
  302. authority_present: authority_present > 0,
  303. mint_authority: ai.data.readAddress(4),
  304. supply: ai.data.readUint64LE(36),
  305. decimals: uint8(ai.data[44]),
  306. is_initialized: ai.data[45] > 0,
  307. freeze_authority_present: freeze_authority_present > 0,
  308. freeze_authority: ai.data.readAddress(50)
  309. });
  310. return data;
  311. }
  312. // A mint account has an authority, whose type is one of the members of this struct.
  313. enum AuthorityType {
  314. MintTokens,
  315. FreezeAccount,
  316. AccountOwner,
  317. CloseAccount
  318. }
  319. /// Remove the mint authority from a mint account
  320. ///
  321. /// @param mintAccount the public key for the mint account
  322. /// @param mintAuthority the public for the mint authority
  323. function remove_mint_authority(address mintAccount, address mintAuthority) public {
  324. AccountMeta[2] metas = [
  325. AccountMeta({pubkey: mintAccount, is_signer: false, is_writable: true}),
  326. AccountMeta({pubkey: mintAuthority, is_signer: true, is_writable: false})
  327. ];
  328. bytes data = new bytes(9);
  329. data[0] = uint8(TokenInstruction.SetAuthority);
  330. data[1] = uint8(AuthorityType.MintTokens);
  331. data[3] = 0;
  332. tokenProgramId.call{accounts: metas}(data);
  333. }
  334. }