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, |       amount: number, | ||||||
|     ): Transaction; |     ): Transaction; | ||||||
|     static assign(from: PublicKey, programId: PublicKey): 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 === |   // === src/validator-info.js === | ||||||
|   | |||||||
| @@ -43,5 +43,25 @@ export const rustString = (property: string = 'string') => { | |||||||
|     return _encode(data, buffer, offset); |     return _encode(data, buffer, offset); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   rsl.alloc = str => { | ||||||
|  |     return ( | ||||||
|  |       BufferLayout.u32().span + | ||||||
|  |       BufferLayout.u32().span + | ||||||
|  |       Buffer.from(str, 'utf8').length | ||||||
|  |     ); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   return rsl; |   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 { |   get fromPublicKey(): PublicKey | null { | ||||||
|     if ( |     if ( | ||||||
|       this.type == SystemInstructionLayout.Create || |       this.type == SystemInstructionLayout.Create || | ||||||
|  |       this.type == SystemInstructionLayout.CreateWithSeed || | ||||||
|       this.type == SystemInstructionLayout.Transfer |       this.type == SystemInstructionLayout.Transfer | ||||||
|     ) { |     ) { | ||||||
|       return this.keys[0].pubkey; |       return this.keys[0].pubkey; | ||||||
| @@ -80,6 +81,7 @@ export class SystemInstruction extends TransactionInstruction { | |||||||
|   get toPublicKey(): PublicKey | null { |   get toPublicKey(): PublicKey | null { | ||||||
|     if ( |     if ( | ||||||
|       this.type == SystemInstructionLayout.Create || |       this.type == SystemInstructionLayout.Create || | ||||||
|  |       this.type == SystemInstructionLayout.CreateWithSeed || | ||||||
|       this.type == SystemInstructionLayout.Transfer |       this.type == SystemInstructionLayout.Transfer | ||||||
|     ) { |     ) { | ||||||
|       return this.keys[1].pubkey; |       return this.keys[1].pubkey; | ||||||
| @@ -95,7 +97,10 @@ export class SystemInstruction extends TransactionInstruction { | |||||||
|     const data = this.type.layout.decode(this.data); |     const data = this.type.layout.decode(this.data); | ||||||
|     if (this.type == SystemInstructionLayout.Transfer) { |     if (this.type == SystemInstructionLayout.Transfer) { | ||||||
|       return data.amount; |       return data.amount; | ||||||
|     } else if (this.type == SystemInstructionLayout.Create) { |     } else if ( | ||||||
|  |       this.type == SystemInstructionLayout.Create || | ||||||
|  |       this.type == SystemInstructionLayout.CreateWithSeed | ||||||
|  |     ) { | ||||||
|       return data.lamports; |       return data.lamports; | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
| @@ -139,13 +144,25 @@ const SystemInstructionLayout = Object.freeze({ | |||||||
|       BufferLayout.ns64('amount'), |       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 |  * Populate a buffer of instruction data using the SystemInstructionType | ||||||
|  */ |  */ | ||||||
| function encodeData(type: SystemInstructionType, fields: Object): Buffer { | 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); |   const layoutFields = Object.assign({instruction: type.index}, fields); | ||||||
|   type.layout.encode(layoutFields, data); |   type.layout.encode(layoutFields, data); | ||||||
|   return data; |   return data; | ||||||
| @@ -221,4 +238,34 @@ export class SystemProgram { | |||||||
|       data, |       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 |   // 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', () => { | test('SystemInstruction create', () => { | ||||||
|   const from = new Account(); |   const from = new Account(); | ||||||
|   const to = new Account(); |   const to = new Account(); | ||||||
| @@ -104,6 +123,30 @@ test('SystemInstruction assign', () => { | |||||||
|   expect(systemInstruction.programId).toEqual(SystemProgram.programId); |   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', () => { | test('non-SystemInstruction error', () => { | ||||||
|   const from = new Account(); |   const from = new Account(); | ||||||
|   const program = new Account(); |   const program = new Account(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user