Skip to content
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

Merged
merged 4 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 92 additions & 28 deletions playground/src/main/java/com/solanamobile/buoy/playground/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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("")
Expand All @@ -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)
Copy link
Owner

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes agree'd


val mainClass = contractFile.addType(
TypeSpec.classBuilder(idlSource.name)
.build()
Expand All @@ -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())
Copy link
Owner

Choose a reason for hiding this comment

The 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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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(")
Copy link
Owner

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ data class Arg(
val type: String
)

//data class VecType(
// val vec: Any
//)
//
//data class OptionalType(
// val option: Any
//)
//
//data class DefinedParam(
// val defined: String
//)

data class AccountData(
val name: String,
val type: AccountDataType
Expand Down