feat(system-program): add createAccountWithSeed
This commit is contained in:
		
				
					committed by
					
						 Michael Vines
						Michael Vines
					
				
			
			
				
	
			
			
			
						parent
						
							cc550dfb08
						
					
				
				
					commit
					0760853871
				
			| @@ -220,6 +220,14 @@ declare module '@solana/web3.js' { | ||||
|       amount: number, | ||||
|     ): Transaction; | ||||
|     static assign(from: PublicKey, programId: PublicKey): Transaction; | ||||
|     static createAccountWithSeed( | ||||
|       from: PublicKey, | ||||
|       newAccount: PublicKey, | ||||
|       seed: string, | ||||
|       lamports: number, | ||||
|       space: number, | ||||
|       programId: PublicKey, | ||||
|     ): Transaction; | ||||
|   } | ||||
|  | ||||
|   // === src/validator-info.js === | ||||
|   | ||||
| @@ -43,5 +43,25 @@ export const rustString = (property: string = 'string') => { | ||||
|     return _encode(data, buffer, offset); | ||||
|   }; | ||||
|  | ||||
|   rsl.alloc = str => { | ||||
|     return ( | ||||
|       BufferLayout.u32().span + | ||||
|       BufferLayout.u32().span + | ||||
|       Buffer.from(str, 'utf8').length | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   return rsl; | ||||
| }; | ||||
|  | ||||
| export function getAlloc(type: Object, fields: Object): number { | ||||
|   let alloc = 0; | ||||
|   type.layout.fields.forEach(item => { | ||||
|     if (item.span >= 0) { | ||||
|       alloc += item.span; | ||||
|     } else if (typeof item.alloc === 'function') { | ||||
|       alloc += item.alloc(fields[item.property]); | ||||
|     } | ||||
|   }); | ||||
|   return alloc; | ||||
| } | ||||
|   | ||||
| @@ -66,6 +66,7 @@ export class SystemInstruction extends TransactionInstruction { | ||||
|   get fromPublicKey(): PublicKey | null { | ||||
|     if ( | ||||
|       this.type == SystemInstructionLayout.Create || | ||||
|       this.type == SystemInstructionLayout.CreateWithSeed || | ||||
|       this.type == SystemInstructionLayout.Transfer | ||||
|     ) { | ||||
|       return this.keys[0].pubkey; | ||||
| @@ -80,6 +81,7 @@ export class SystemInstruction extends TransactionInstruction { | ||||
|   get toPublicKey(): PublicKey | null { | ||||
|     if ( | ||||
|       this.type == SystemInstructionLayout.Create || | ||||
|       this.type == SystemInstructionLayout.CreateWithSeed || | ||||
|       this.type == SystemInstructionLayout.Transfer | ||||
|     ) { | ||||
|       return this.keys[1].pubkey; | ||||
| @@ -95,7 +97,10 @@ export class SystemInstruction extends TransactionInstruction { | ||||
|     const data = this.type.layout.decode(this.data); | ||||
|     if (this.type == SystemInstructionLayout.Transfer) { | ||||
|       return data.amount; | ||||
|     } else if (this.type == SystemInstructionLayout.Create) { | ||||
|     } else if ( | ||||
|       this.type == SystemInstructionLayout.Create || | ||||
|       this.type == SystemInstructionLayout.CreateWithSeed | ||||
|     ) { | ||||
|       return data.lamports; | ||||
|     } | ||||
|     return null; | ||||
| @@ -139,13 +144,25 @@ const SystemInstructionLayout = Object.freeze({ | ||||
|       BufferLayout.ns64('amount'), | ||||
|     ]), | ||||
|   }, | ||||
|   CreateWithSeed: { | ||||
|     index: 3, | ||||
|     layout: BufferLayout.struct([ | ||||
|       BufferLayout.u32('instruction'), | ||||
|       Layout.rustString('seed'), | ||||
|       BufferLayout.ns64('lamports'), | ||||
|       BufferLayout.ns64('space'), | ||||
|       Layout.publicKey('programId'), | ||||
|     ]), | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * Populate a buffer of instruction data using the SystemInstructionType | ||||
|  */ | ||||
| function encodeData(type: SystemInstructionType, fields: Object): Buffer { | ||||
|   const data = Buffer.alloc(type.layout.span); | ||||
|   const allocLength = | ||||
|     type.layout.span >= 0 ? type.layout.span : Layout.getAlloc(type, fields); | ||||
|   const data = Buffer.alloc(allocLength); | ||||
|   const layoutFields = Object.assign({instruction: type.index}, fields); | ||||
|   type.layout.encode(layoutFields, data); | ||||
|   return data; | ||||
| @@ -221,4 +238,34 @@ export class SystemProgram { | ||||
|       data, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Generate a Transaction that creates a new account at | ||||
|    *   an address generated with `from`, a seed, and programId | ||||
|    */ | ||||
|   static createAccountWithSeed( | ||||
|     from: PublicKey, | ||||
|     newAccount: PublicKey, | ||||
|     seed: string, | ||||
|     lamports: number, | ||||
|     space: number, | ||||
|     programId: PublicKey, | ||||
|   ): Transaction { | ||||
|     const type = SystemInstructionLayout.CreateWithSeed; | ||||
|     const data = encodeData(type, { | ||||
|       seed, | ||||
|       lamports, | ||||
|       space, | ||||
|       programId: programId.toBuffer(), | ||||
|     }); | ||||
|  | ||||
|     return new Transaction().add({ | ||||
|       keys: [ | ||||
|         {pubkey: from, isSigner: true, isWritable: true}, | ||||
|         {pubkey: newAccount, isSigner: false, isWritable: true}, | ||||
|       ], | ||||
|       programId: SystemProgram.programId, | ||||
|       data, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -50,6 +50,25 @@ test('assign', () => { | ||||
|   // TODO: Validate transaction contents more | ||||
| }); | ||||
|  | ||||
| test('createAccountWithSeed', () => { | ||||
|   const from = new Account(); | ||||
|   const newAccount = new Account(); | ||||
|   let transaction; | ||||
|  | ||||
|   transaction = SystemProgram.createAccountWithSeed( | ||||
|     from.publicKey, | ||||
|     newAccount.publicKey, | ||||
|     'hi there', | ||||
|     123, | ||||
|     BudgetProgram.space, | ||||
|     BudgetProgram.programId, | ||||
|   ); | ||||
|  | ||||
|   expect(transaction.keys).toHaveLength(2); | ||||
|   expect(transaction.programId).toEqual(SystemProgram.programId); | ||||
|   // TODO: Validate transaction contents more | ||||
| }); | ||||
|  | ||||
| test('SystemInstruction create', () => { | ||||
|   const from = new Account(); | ||||
|   const to = new Account(); | ||||
| @@ -104,6 +123,30 @@ test('SystemInstruction assign', () => { | ||||
|   expect(systemInstruction.programId).toEqual(SystemProgram.programId); | ||||
| }); | ||||
|  | ||||
| test('SystemInstruction createWithSeed', () => { | ||||
|   const from = new Account(); | ||||
|   const to = new Account(); | ||||
|   const program = new Account(); | ||||
|   const amount = 42; | ||||
|   const space = 100; | ||||
|   const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash | ||||
|   const create = SystemProgram.createAccountWithSeed( | ||||
|     from.publicKey, | ||||
|     to.publicKey, | ||||
|     'hi there', | ||||
|     amount, | ||||
|     space, | ||||
|     program.publicKey, | ||||
|   ); | ||||
|   const transaction = new Transaction({recentBlockhash}).add(create); | ||||
|  | ||||
|   const systemInstruction = SystemInstruction.from(transaction.instructions[0]); | ||||
|   expect(systemInstruction.fromPublicKey).toEqual(from.publicKey); | ||||
|   expect(systemInstruction.toPublicKey).toEqual(to.publicKey); | ||||
|   expect(systemInstruction.amount).toEqual(amount); | ||||
|   expect(systemInstruction.programId).toEqual(SystemProgram.programId); | ||||
| }); | ||||
|  | ||||
| test('non-SystemInstruction error', () => { | ||||
|   const from = new Account(); | ||||
|   const program = new Account(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user