123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // SPDX-License-Identifier: Apache-2.0
- // Disclaimer: This library provides a bridge for Solidity to interact with Solana's system instructions. Although it is production ready,
- // it has not been audited for security, so use it at your own risk.
- import 'solana';
- library SystemInstruction {
- address constant systemAddress = address"11111111111111111111111111111111";
- address constant recentBlockHashes = address"SysvarRecentB1ockHashes11111111111111111111";
- address constant rentAddress = address"SysvarRent111111111111111111111111111111111";
- uint64 constant state_size = 80;
- enum Instruction {
- CreateAccount,
- Assign,
- Transfer,
- CreateAccountWithSeed,
- AdvanceNounceAccount,
- WithdrawNonceAccount,
- InitializeNonceAccount,
- AuthorizeNonceAccount,
- Allocate,
- AllocateWithSeed,
- AssignWithSeed,
- TransferWithSeed,
- UpgradeNonceAccount // This is not available on Solana v1.9.15
- }
- /// Create a new account on Solana
- ///
- /// @param from public key for the account from which to transfer lamports to the new account
- /// @param to public key for the account to be created
- /// @param lamports amount of lamports to be transfered to the new account
- /// @param space the size in bytes that is going to be made available for the account
- /// @param owner public key for the program that will own the account being created
- function create_account(address from, address to, uint64 lamports, uint64 space, address owner) internal {
- AccountMeta[2] metas = [
- AccountMeta({pubkey: from, is_signer: true, is_writable: true}),
- AccountMeta({pubkey: to, is_signer: true, is_writable: true})
- ];
- bytes bincode = abi.encode(uint32(Instruction.CreateAccount), lamports, space, owner);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Create a new account on Solana using a public key derived from a seed
- ///
- /// @param from public key for the account from which to transfer lamports to the new account
- /// @param to the public key for the account to be created. The public key must match create_with_seed(base, seed, owner)
- /// @param base the base address that derived the 'to' address using the seed
- /// @param seed the string utilized to created the 'to' public key
- /// @param lamports amount of lamports to be transfered to the new account
- /// @param space the size in bytes that is going to be made available for the account
- /// @param owner public key for the program that will own the account being created
- function create_account_with_seed(address from, address to, address base, string seed, uint64 lamports, uint64 space, address owner) internal {
- AccountMeta[3] metas = [
- AccountMeta({pubkey: from, is_signer: true, is_writable: true}),
- AccountMeta({pubkey: to, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: base, is_signer: true, is_writable: false})
- ];
- uint32 buffer_size = 92 + seed.length;
- bytes bincode = new bytes(buffer_size);
- bincode.writeUint32LE(uint32(Instruction.CreateAccountWithSeed), 0);
- bincode.writeAddress(base, 4);
- bincode.writeUint64LE(uint64(seed.length), 36);
- bincode.writeString(seed, 44);
- uint32 offset = seed.length + 44;
- bincode.writeUint64LE(lamports, offset);
- offset += 8;
- bincode.writeUint64LE(space, offset);
- offset += 8;
- bincode.writeAddress(owner, offset);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Assign account to a program (owner)
- ///
- /// @param pubkey the public key for the account whose owner is going to be reassigned
- /// @param owner the public key for the new account owner
- function assign(address pubkey, address owner) internal {
- AccountMeta[1] meta = [
- AccountMeta({pubkey: pubkey, is_signer: true, is_writable: true})
- ];
- bytes bincode = abi.encode(uint32(Instruction.Assign), owner);
- systemAddress.call{accounts: meta}(bincode);
- }
- /// Assign account to a program (owner) based on a seed
- ///
- /// @param addr the public key for the account whose owner is going to be reassigned. The public key must match create_with_seed(base, seed, owner)
- /// @param base the base address that derived the 'addr' key using the seed
- /// @param seed the string utilized to created the 'addr' public key
- /// @param owner the public key for the new program owner
- function assign_with_seed(address addr, address base, string seed, address owner) internal {
- AccountMeta[2] metas = [
- AccountMeta({pubkey: addr, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: base, is_signer: true, is_writable: false})
- ];
- uint32 buffer_size = 76 + seed.length;
- bytes bincode = new bytes(buffer_size);
- bincode.writeUint32LE(uint32(Instruction.AssignWithSeed), 0);
- bincode.writeAddress(base, 4);
- bincode.writeUint64LE(uint64(seed.length), 36);
- bincode.writeString(seed, 44);
- bincode.writeAddress(owner, 44 + seed.length);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Transfer lamports between accounts
- ///
- /// @param from public key for the funding account
- /// @param to public key for the recipient account
- /// @param lamports amount of lamports to transfer
- function transfer(address from, address to, uint64 lamports) internal {
- AccountMeta[2] metas = [
- AccountMeta({pubkey: from, is_signer: true, is_writable: true}),
- AccountMeta({pubkey: to, is_signer: false, is_writable: true})
- ];
- bytes bincode = abi.encode(uint32(Instruction.Transfer), lamports);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Transfer lamports from a derived address
- ///
- /// @param from_pubkey The funding account public key. It should match create_with_seed(from_base, seed, from_owner)
- /// @param from_base the base address that derived the 'from_pubkey' key using the seed
- /// @param seed the string utilized to create the 'from_pubkey' public key
- /// @param from_owner owner to use to derive the funding account address
- /// @param to_pubkey the public key for the recipient account
- /// @param lamports amount of lamports to transfer
- function transfer_with_seed(address from_pubkey, address from_base, string seed, address from_owner, address to_pubkey, uint64 lamports) internal {
- AccountMeta[3] metas = [
- AccountMeta({pubkey: from_pubkey, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: from_base, is_signer: true, is_writable: false}),
- AccountMeta({pubkey: to_pubkey, is_signer: false, is_writable: true})
- ];
- uint32 buffer_size = seed.length + 52;
- bytes bincode = new bytes(buffer_size);
- bincode.writeUint32LE(uint32(Instruction.TransferWithSeed), 0);
- bincode.writeUint64LE(lamports, 4);
- bincode.writeUint64LE(seed.length, 12);
- bincode.writeString(seed, 20);
- bincode.writeAddress(from_owner, 20 + seed.length);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Allocate space in a (possibly new) account without funding
- ///
- /// @param pub_key account for which to allocate space
- /// @param space number of bytes of memory to allocate
- function allocate(address pub_key, uint64 space) internal {
- AccountMeta[1] meta = [
- AccountMeta({pubkey: pub_key, is_signer: true, is_writable: true})
- ];
- bytes bincode = abi.encode(uint32(Instruction.Allocate), space);
- systemAddress.call{accounts: meta}(bincode);
- }
- /// Allocate space for an assign an account at an address derived from a base public key and a seed
- ///
- /// @param addr account for which to allocate space. It should match create_with_seed(base, seed, owner)
- /// @param base the base address that derived the 'addr' key using the seed
- /// @param seed the string utilized to create the 'addr' public key
- /// @param space number of bytes of memory to allocate
- /// @param owner owner to use to derive the 'addr' account address
- function allocate_with_seed(address addr, address base, string seed, uint64 space, address owner) internal {
- AccountMeta[2] metas = [
- AccountMeta({pubkey: addr, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: base, is_signer: true, is_writable: false})
- ];
- bytes bincode = new bytes(seed.length + 84);
- bincode.writeUint32LE(uint32(Instruction.AllocateWithSeed), 0);
- bincode.writeAddress(base, 4);
- bincode.writeUint64LE(seed.length, 36);
- bincode.writeString(seed, 44);
- uint32 offset = 44 + seed.length;
- bincode.writeUint64LE(space, offset);
- offset += 8;
- bincode.writeAddress(owner, offset);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Create a new nonce account on Solana using a public key derived from a seed
- ///
- /// @param from public key for the account from which to transfer lamports to the new account
- /// @param nonce the public key for the account to be created. The public key must match create_with_seed(base, seed, systemAddress)
- /// @param base the base address that derived the 'nonce' key using the seed
- /// @param seed the string utilized to create the 'addr' public key
- /// @param authority The entity authorized to execute nonce instructions on the account
- /// @param lamports amount of lamports to be transfered to the new account
- function create_nonce_account_with_seed(address from, address nonce, address base, string seed, address authority, uint64 lamports) internal {
- create_account_with_seed(from, nonce, base, seed, lamports, state_size, systemAddress);
- AccountMeta[3] metas = [
- AccountMeta({pubkey: nonce, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: recentBlockHashes, is_signer: false, is_writable: false}),
- AccountMeta({pubkey: rentAddress, is_signer: false, is_writable: false})
- ];
- bytes bincode = abi.encode(uint32(Instruction.InitializeNonceAccount), authority);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Create a new account on Solana
- ///
- /// @param from public key for the account from which to transfer lamports to the new account
- /// @param nonce the public key for the nonce account to be created
- /// @param authority The entity authorized to execute nonce instructions on the account
- /// @param lamports amount of lamports to be transfered to the new account
- function create_nonce_account(address from, address nonce, address authority, uint64 lamports) internal {
- create_account(from, nonce, lamports, state_size, systemAddress);
- AccountMeta[3] metas = [
- AccountMeta({pubkey: nonce, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: recentBlockHashes, is_signer: false, is_writable: false}),
- AccountMeta({pubkey: rentAddress, is_signer: false, is_writable: false})
- ];
- bytes bincode = abi.encode(uint32(Instruction.InitializeNonceAccount), authority);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Consumes a stored nonce, replacing it with a successor
- ///
- /// @param nonce_pubkey the public key for the nonce account
- /// @param authorized_pubkey the publick key for the entity authorized to execute instructins on the account
- function advance_nonce_account(address nonce_pubkey, address authorized_pubkey) internal {
- AccountMeta[3] metas = [
- AccountMeta({pubkey: nonce_pubkey, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: recentBlockHashes, is_signer: false, is_writable: false}),
- AccountMeta({pubkey: authorized_pubkey, is_signer: true, is_writable: false})
- ];
- bytes bincode = abi.encode(uint32(Instruction.AdvanceNounceAccount));
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Withdraw funds from a nonce account
- ///
- /// @param nonce_pubkey the public key for the nonce account
- /// @param authorized_pubkey the public key for the entity authorized to execute instructins on the account
- /// @param to_pubkey the recipient account
- /// @param lamports the number of lamports to withdraw
- function withdraw_nonce_account(address nonce_pubkey, address authorized_pubkey, address to_pubkey, uint64 lamports) internal {
- AccountMeta[5] metas = [
- AccountMeta({pubkey: nonce_pubkey, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: to_pubkey, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: recentBlockHashes, is_signer: false, is_writable: false}),
- AccountMeta({pubkey: rentAddress, is_signer: false, is_writable: false}),
- AccountMeta({pubkey: authorized_pubkey, is_signer: true, is_writable: false})
- ];
- bytes bincode = abi.encode(uint32(Instruction.WithdrawNonceAccount), lamports);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// Change the entity authorized to execute nonce instructions on the account
- ///
- /// @param nonce_pubkey the public key for the nonce account
- /// @param authorized_pubkey the public key for the entity authorized to execute instructins on the account
- /// @param new_authority
- function authorize_nonce_account(address nonce_pubkey, address authorized_pubkey, address new_authority) internal {
- AccountMeta[2] metas = [
- AccountMeta({pubkey: nonce_pubkey, is_signer: false, is_writable: true}),
- AccountMeta({pubkey: authorized_pubkey, is_signer: true, is_writable: false})
- ];
- bytes bincode = abi.encode(uint32(Instruction.AuthorizeNonceAccount), new_authority);
- systemAddress.call{accounts: metas}(bincode);
- }
- /// One-time idempotent upgrade of legacy nonce version in order to bump them out of chain domain.
- ///
- /// @param nonce the public key for the nonce account
- // This is not available on Solana v1.9.15
- function upgrade_nonce_account(address nonce) internal {
- AccountMeta[1] meta = [
- AccountMeta({pubkey: nonce, is_signer: false, is_writable: true})
- ];
- bytes bincode = abi.encode(uint32(Instruction.UpgradeNonceAccount));
- systemAddress.call{accounts: meta}(bincode);
- }
- }
|