-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generating metas #2
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,15 +3,56 @@ package com.solanamobile.buoy.playground | |
import com.google.gson.Gson | ||
import com.solanamobile.buoy.playground.idlspec.IdlRootV1 | ||
import com.solanamobile.web3.core.PublicKey | ||
import com.squareup.kotlinpoet.FileSpec | ||
import com.squareup.kotlinpoet.FunSpec | ||
import com.squareup.kotlinpoet.PropertySpec | ||
import com.squareup.kotlinpoet.TypeSpec | ||
import com.solanamobile.web3.core.TransactionInstruction | ||
import com.squareup.kotlinpoet.* | ||
import java.io.File | ||
import java.security.MessageDigest | ||
import java.util.* | ||
import kotlin.reflect.KClass | ||
|
||
/** | ||
* ./gradlew :playground:run --args=<Path_to_json> | ||
*/ | ||
|
||
fun classTypeForArgType( argType: String ): KClass<*> { | ||
return when(argType) { | ||
"u8" -> UByte::class | ||
"u16" -> UShort::class | ||
"u32" -> UInt::class | ||
"u64" -> ULong::class | ||
"i8" -> Byte::class | ||
"i16" -> Short::class | ||
"i32" -> Int::class | ||
"i64" -> Long::class | ||
"publicKey" -> PublicKey::class | ||
else -> { | ||
// throw Exception("This should not happen.") | ||
return ClassName("solanapackagename", argType)::class | ||
} | ||
} | ||
} | ||
|
||
private fun convertCamelToSnakeCase(camelCase : String) : String { | ||
val snakeCase = StringBuilder() | ||
for(character in camelCase) { | ||
if(character.isUpperCase()) { | ||
snakeCase.append("_${character.toLowerCase()}") | ||
} else { | ||
snakeCase.append(character) | ||
} | ||
} | ||
return snakeCase.removePrefix("_").toString() | ||
} | ||
|
||
fun sighash( nameSpace: String = "global", name: String ): Array<Byte> { | ||
val preImage = String.format("%s:%s", nameSpace, name) | ||
val preImageByteArray = preImage.toByteArray() | ||
val md = MessageDigest.getInstance("SHA-256") | ||
val digest = md.digest(preImageByteArray) | ||
val sighash = digest.copyOfRange(0, 8) | ||
return sighash.toTypedArray() | ||
} | ||
|
||
fun main(arguments: Array<String>) { | ||
println("----------------------------------------") | ||
println("") | ||
|
@@ -24,6 +65,7 @@ fun main(arguments: Array<String>) { | |
|
||
//Use the objects to generate the contract file | ||
val contractFile = FileSpec.builder("com.solanamobile.buoy", idlSource.name) | ||
|
||
val mainClass = contractFile.addType( | ||
TypeSpec.classBuilder(idlSource.name) | ||
.build() | ||
|
@@ -32,40 +74,62 @@ fun main(arguments: Array<String>) { | |
idlSource.instructions.forEach { instruction -> | ||
val functionBuilder = FunSpec.builder(instruction.name) | ||
|
||
functionBuilder | ||
.returns(TransactionInstruction::class) | ||
|
||
// start the list of function | ||
functionBuilder.addStatement("val = listOf(") | ||
|
||
// for each meta descriptor, add a parameter accepting | ||
// the pubkey and generate the AccountMeta item in the list | ||
instruction.args.forEach { arg -> | ||
val type = classTypeForArgType(arg.type) | ||
functionBuilder | ||
.addParameter(arg.name, type) | ||
} | ||
|
||
instruction.accounts.forEach { metaDescriptor -> | ||
|
||
val metaItem = String.format("\tAccountMeta(publicKey = %s, isSigner = %s, isWritable = %s)", metaDescriptor.name, metaDescriptor.isSigner.toString(), metaDescriptor.isMut.toString()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just want to make sure that AccountMeta is imported properly into the resulting class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure how to do that lol but I will likely learn soon |
||
|
||
functionBuilder | ||
.addParameter(metaDescriptor.name, PublicKey::class) | ||
.build() | ||
.addStatement(metaItem) | ||
|
||
} | ||
|
||
// close the listOf function | ||
functionBuilder.addStatement(")") | ||
|
||
val sighHashStatement = String.format("val sigHash: Array<Byte> = sighash(\"%s\", \"%s\")", "global", convertCamelToSnakeCase(instruction.name)) | ||
functionBuilder.addStatement( sighHashStatement ) | ||
|
||
val instructionDataStatementBuilder = StringBuilder() | ||
|
||
instructionDataStatementBuilder | ||
.append("val instructionData: Array<Byte> = sigHash") | ||
|
||
instruction.args.forEach { arg -> | ||
functionBuilder | ||
.addStatement(String.format("val %sBytes = %s.toBytes()", arg.name, arg.name)) | ||
instructionDataStatementBuilder.append( String.format(" + %sBytes", arg.name, arg.name) ) | ||
} | ||
|
||
val instructionDataStatement = instructionDataStatementBuilder.toString() | ||
|
||
functionBuilder.addStatement(instructionDataStatement) | ||
|
||
functionBuilder.addStatement("return TransactionInstruction(") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like some of these variable names could be turned into reusable variables, but again, I don't think we worry about that now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed! honestly I think I just wanted core functionality to start shaping and I'll hit it w/ a refactor soon #famouslastwords |
||
functionBuilder.addStatement("\tkeys = keys,") | ||
functionBuilder.addStatement("\tprogramId = programId,") | ||
functionBuilder.addStatement("\tdata = instructionData") | ||
functionBuilder.addStatement(")") | ||
|
||
mainClass.addFunction( | ||
functionBuilder.build() | ||
).build() | ||
} | ||
|
||
contractFile.build().writeTo(System.out) | ||
|
||
val file = FileSpec.builder("com.solanamobile", "HelloWorld") | ||
.addType( | ||
TypeSpec.classBuilder("Greeter") | ||
.primaryConstructor( | ||
FunSpec.constructorBuilder() | ||
.addParameter("name", String::class) | ||
.build() | ||
) | ||
.addProperty( | ||
PropertySpec.builder("name", String::class) | ||
.initializer("name") | ||
.build() | ||
) | ||
.addFunction( | ||
FunSpec.builder("greet") | ||
.addStatement("println(%P)", "Hello, \$name") | ||
.build() | ||
) | ||
.build() | ||
) | ||
.build() | ||
|
||
file.writeTo(System.out) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking that we'll probably actually want to inherit the project's package name for our gen'd classes, but we can put that in later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes agree'd