From 648f7e5eb2ab1771623fb16cccef7a739145fff0 Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Fri, 3 Oct 2025 11:50:09 +0100 Subject: [PATCH 01/11] Use Solana Dev net with deployNodes and Gradle tasks - includes 3 manual steps still and duplicated code --- .../bridging/flows/BridgeFungibleTokenFlow.kt | 2 +- .../bridging/flows/BridgeTokensUtilities.kt | 7 +- .../tokens/bridging/flows/rpc/BridgeTokens.kt | 10 + .../bridging-setup/build.gradle | 27 ++ .../kotlin/net/corda/bank/BridgingSetup.kt | 160 +++++++++++ .../net/corda/bank/solana/TokenManagement.kt | 254 ++++++++++++++++++ Tokens/stockpaydividend/build.gradle | 137 +++++++++- Tokens/stockpaydividend/settings.gradle | 2 +- .../stockpaydividend/workflows/build.gradle | 2 + 9 files changed, 593 insertions(+), 8 deletions(-) create mode 100644 Tokens/stockpaydividend/bridging-setup/build.gradle create mode 100644 Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt create mode 100644 Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt diff --git a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt index 9145456c..b7b3e2a4 100644 --- a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt +++ b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt @@ -60,7 +60,7 @@ class BridgeFungibleTokenFlow( destination, bridgeAuthority ) - + logger.info("Generating Solana bridging transaction with the following parameters: mint=$mint, mintAuthority=$mintAuthority, destination=$destination.") return subFlow( InternalBridgeFungibleTokenFlow( participantSessions = participantSessions, diff --git a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt index ecb5f602..3b6dae5c 100644 --- a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt +++ b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt @@ -54,10 +54,9 @@ fun bridgeTokens( addCommand(additionalCommand, keys) } } - if (additionalCommand is BridgingContract.BridgingCommand.BridgeToSolana) { - val instruction = Token2022.mintTo(mint, destination, mintAuthority, quantity) - transactionBuilder.addNotaryInstruction(instruction) - } + require(additionalCommand is BridgingContract.BridgingCommand.BridgeToSolana) { "Only bridging to Solana is supported" } + val instruction = Token2022.mintTo(mint, destination, mintAuthority, quantity) + transactionBuilder.addNotaryInstruction(instruction) return transactionBuilder } diff --git a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt index b956034c..80cd19a0 100644 --- a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt +++ b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt @@ -8,6 +8,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import com.r3.corda.lib.tokens.contracts.states.FungibleToken import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.StateRef import net.corda.core.utilities.ProgressTracker @InitiatingFlow @@ -37,3 +38,12 @@ class BridgeToken( } } +@StartableByRPC +class BridgeTokenRpc(private val tokenRef: StateRef, private val bridgeAuthority: Party) : FlowLogic() { + @Suspendable + override fun call(): String { + val stateAndRef: StateAndRef = serviceHub.toStateAndRef(tokenRef) + val result = subFlow(BridgeToken(stateAndRef, bridgeAuthority)) + return result + } +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/bridging-setup/build.gradle b/Tokens/stockpaydividend/bridging-setup/build.gradle new file mode 100644 index 00000000..9bb49041 --- /dev/null +++ b/Tokens/stockpaydividend/bridging-setup/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'net.corda.plugins.cordapp' +apply plugin: 'org.jetbrains.kotlin.jvm' +apply plugin: 'idea' +apply plugin: 'net.corda.plugins.quasar-utils' + +cordapp { + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion corda_platform_version.toInteger() + workflow { + name "Token Bridging Setup" + vendor "Corda Open Source" + licence "Apache License, Version 2.0" + versionId 1 + } +} + +dependencies { + cordaProvided "$corda_core_release_group:corda-core:$corda_core_release_version" + cordaProvided "$corda_release_group:corda:$corda_release_version" + + implementation 'net.sf.jopt-simple:jopt-simple:5.0.4' + + implementation "org.slf4j:slf4j-api:$slf4j_version" + + cordaProvided "$corda_release_group:corda-solana-sdk:$corda_release_version" + cordaProvided "$corda_release_group:corda-solana-common:$corda_release_version" +} diff --git a/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt b/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt new file mode 100644 index 00000000..22d80cd1 --- /dev/null +++ b/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt @@ -0,0 +1,160 @@ +package net.corda.bank + +import com.lmax.solana4j.api.PublicKey +import joptsimple.OptionParser +import net.corda.bank.solana.TokenManagement +import net.corda.bank.solana.TokenManagement.Companion.LAMPORTS_PER_SOL +import net.corda.core.identity.CordaX500Name +import net.corda.solana.aggregator.common.Signer +import net.corda.solana.aggregator.common.toPublicKey +import net.corda.solana.sdk.instruction.Pubkey +import java.io.File +import kotlin.io.path.Path +import kotlin.io.readText +import kotlin.io.writeText +import kotlin.jvm.java +import kotlin.system.exitProcess +import kotlin.text.trimIndent + +//TODO use from Corda Enterprise +object BridgingSetup { + + private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH") + + private const val AUTHORITY_RPC_PORT = 10021 + + @Suppress("ComplexMethod", "SpreadOperator") + @JvmStatic + fun main(args: Array) { + val parser = OptionParser() + val rpcUrlArg = + parser.accepts("rpcUrl").withRequiredArg().ofType(String::class.java).describedAs("Solana RPC URL") + val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java) + .describedAs("[FUND_WALLET|DEFUND_WALLET|CREATE_TOKEN|CREATE_TOKEN_ACCOUNT]") + val funderArg = parser.accepts("funder").withRequiredArg().ofType(String::class.java) + .describedAs("File path to the funder wallet for funding or defunding") + val targetArg = parser.accepts("target").withRequiredArg().ofType(String::class.java) + .describedAs("File path to the target wallet for funding or defunding") + val tokenMintArg = parser.accepts("mint").withRequiredArg().ofType(String::class.java) + .describedAs("File path to the token mint pubkey file") + val tokenAccArg = parser.accepts("account").withRequiredArg().ofType(String::class.java) + .describedAs("File path to the token account pubkey file") + val quantityArg = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java) + val currencyArg = + parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]") + + val options = try { + parser.parse(*args) + } catch (e: Exception) { + println(e.message) + printHelp(parser) + exitProcess(1) + } + + try { + val role = options.valueOf(roleArg) + val rpcUrl = options.valueOf(rpcUrlArg) + val funderWallet = Signer.fromFile(Path(options.valueOf(funderArg)!!)) + when (role) { + Role.FUND_WALLET -> { + require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } + val targetWallet = Signer.fromFile(Path(options.valueOf(targetArg)!!)) + val quantity = options.valueOf(quantityArg) ?: 1 + println("rpcUrl $rpcUrl") + println("funderWallet $funderWallet") + println("targetWallet.account ${targetWallet.account}") + println("quantity * LAMPORTS_PER_SOL ${quantity * LAMPORTS_PER_SOL}") + TokenManagement(rpcUrl).fundAccount(funderWallet, targetWallet.account, quantity * LAMPORTS_PER_SOL) + } + + Role.DEFUND_WALLET -> { + require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } + val targetWallet = Signer.fromFile(Path(options.valueOf(targetArg)!!)) + + TokenManagement(rpcUrl).defundAccount(targetWallet, funderWallet.account) + } + + Role.CREATE_TOKEN -> { + require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } + val mintKeyFile = options.valueOf(tokenMintArg)!! + // only 2 decimals as this example will bridge dollars + + println("funderWallet $funderWallet") + val tokenMint = TokenManagement(rpcUrl).createToken(funderWallet, decimals = 2) + tokenMint.writeToFile(mintKeyFile) + } + + Role.CREATE_TOKEN_ACCOUNT -> { + require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } + val tokenMint = readKeyFromFile(options.valueOf(tokenMintArg)!!) + val tokenAccountFile = options.valueOf(tokenAccArg)!! + val tokenAccount = TokenManagement(rpcUrl).createTokenAccount(funderWallet, tokenMint) + tokenAccount.writeToFile(tokenAccountFile) + } + + Role.CREATE_BRIDGING_CONFIG -> { +// println("Requesting creation of bridging config state") +// val tokenMint = readKeyFromFile(options.valueOf(tokenMintArg)!!) +// val params = BankOfCordaWebApi.BridgeConfigRequestParams( +// Currency.getInstance(options.valueOf(currencyArg)), +// "1", +// BOC_NAME, +// NOTARY_NAME, +// tokenMint.base58(), +// funderWallet.account.base58() +// ) +// val result = BankOfCordaClientApi.requestBridgingConfigCreation(NetworkHostAndPort("localhost", AUTHORITY_RPC_PORT), params) +// println("Success! Created state $result") + } + + else -> printHelp(parser) + } + } catch (e: Throwable) { + println("Caught exception: $e") + e.printStackTrace() + printHelp(parser) + throw e + } + } + + fun readKeyFromFile(filename: String): PublicKey = + Pubkey.fromBase58(File(filename).readText(Charsets.US_ASCII)).toPublicKey() + + fun PublicKey.writeToFile(filename: String) { + File(filename).writeText(this.base58(), Charsets.US_ASCII) + } + + private fun printHelp(parser: OptionParser) { + println( + """Modes of operation: + Fund accounts: + --role FUND_WALLET --funder --target [--amount ] + This will transfer an amount of SOL from the funder to the target on the ledger + + Defund accounts: + --role DEFUND_WALLET --funder --target + This will transfer all SOL from the target wallet back to the funder (marking target for deletion) + + Create token: + --role CREATE_TOKEN --funder --mint + This will create a Token2022 definition with funder as payer/mint authority, and store the key in the mint pubkey file + + Create token account: + --role CREATE_TOKEN_ACCOUNT --funder --mint --account + This will create an account owned and payed for by funder that can hold tokens as defined by mint. The key will be written to account. + + Create bridging config + --role CREATE_BRIDGING_CONFIG --funder --mint --currency + """.trimIndent() + ) + parser.printHelpOn(System.out) + } + + enum class Role { + FUND_WALLET, + DEFUND_WALLET, + CREATE_TOKEN, + CREATE_TOKEN_ACCOUNT, + CREATE_BRIDGING_CONFIG + } +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt b/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt new file mode 100644 index 00000000..76806173 --- /dev/null +++ b/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt @@ -0,0 +1,254 @@ +package net.corda.bank.solana + +import com.lmax.solana4j.Solana +import com.lmax.solana4j.api.PublicKey +import com.lmax.solana4j.client.jsonrpc.SolanaJsonRpcClient +import com.lmax.solana4j.programs.SystemProgram +import com.lmax.solana4j.programs.SystemProgram.MINT_ACCOUNT_LENGTH +import com.lmax.solana4j.programs.Token2022Program +import com.lmax.solana4j.programs.TokenProgram.ACCOUNT_LAYOUT_SPAN +import net.corda.solana.aggregator.common.RpcParams +import net.corda.solana.aggregator.common.Signer +import net.corda.solana.aggregator.common.checkResponse +import net.corda.solana.aggregator.common.sendAndConfirm +import net.corda.solana.aggregator.common.toPublicKey +import net.corda.solana.sdk.internal.Token2022 +import java.net.http.HttpClient +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.util.Optional + +//TODO use from Corda Enterprise +/** + * Class to prepare and clean up the tokens setup on devnet + */ +class TokenManagement(rpcUrl: String, val rpcParams: RpcParams = RpcParams()) { + + companion object { + /** + * Fee for a transaction with one signature. Each further signature adds the same fee again. + */ + const val BASE_TRANSACTION_FEE: Long = 5000 + const val LAMPORTS_PER_SOL: Long = 1000000000 + + const val TOKEN2022_BURN_DATA_SIZE = 9 + const val TOKEN2022_BURN_ID: Byte = 8 + + } + + private val client = SolanaJsonRpcClient(HttpClient.newHttpClient(), rpcUrl) + + /** + * Create a token mint (a token definition). The public key returned needs to be stored safely as this is required to + * access the token definition in all other operations. + * + * @param payer The account signing and paying for the token creation + * @param mintAuthority The account who will control minting and burning of the token type. Will + * default to the payer. + * @param tokenMint Optional parameter to specify the key of the token. Defaults to a new random key + * @param decimals Decimal positions of the token. Defaults to 9 + * @return returns the public key of the token definition. + */ + fun createToken( + payer: Signer, + mintAuthority: PublicKey = payer.account, + tokenMint: Signer = Signer.random(), + decimals: Byte = 9, + ): PublicKey { + val rentExemption = client.getMinimumBalanceForRentExemption(MINT_ACCOUNT_LENGTH, rpcParams) + .checkResponse("getMinimumBalanceForRentExemption")!! + client.sendAndConfirm( + { txBuilder -> + SystemProgram.factory(txBuilder).createAccount( + payer.account, + tokenMint.account, + rentExemption, + MINT_ACCOUNT_LENGTH.toLong(), + Token2022Program.PROGRAM_ACCOUNT + ) + Token2022Program.factory(txBuilder).initializeMint( + tokenMint.account, + decimals, + mintAuthority, + Optional.empty() + ) + }, + payer, + listOf(tokenMint), + rpcParams + ) + return tokenMint.account + } + + /** + * Creates an account that can hold the Token2022 token created for the given token mint. + * + * @param payer Account paying for and signing the transaction + * @param tokenMint The token definition this account will be able to hold. + * @param accountOwner The owner of the new token account (that will have to sign for transactions moving tokens out of the account) + * @param tokenAccount Optional parameters specifying the keys of the account. Will default to a random key + * @return Returns the public key (i.e. address) of the new token account + */ + fun createTokenAccount( + payer: Signer, + tokenMint: PublicKey, + accountOwner: PublicKey = payer.account, + tokenAccount: Signer = Signer.random(), + ): PublicKey { + val rentExemption = client.getMinimumBalanceForRentExemption(ACCOUNT_LAYOUT_SPAN, rpcParams) + .checkResponse("getMinimumBalanceForRentExemption")!! + client.sendAndConfirm( + { txBuilder -> + SystemProgram.factory(txBuilder).createAccount( + payer.account, + tokenAccount.account, + rentExemption, + ACCOUNT_LAYOUT_SPAN.toLong(), + Token2022Program.PROGRAM_ACCOUNT + ) + Token2022Program.factory(txBuilder).initializeAccount(tokenAccount.account, tokenMint, accountOwner) + }, + payer, + listOf(tokenAccount), + rpcParams + ) + return tokenAccount.account + } + + /** + * Mints tokens of the given type to the given token account. Will fail with an exception if the token mint does not match + * the account, or the account cannot hold tokens. + * + * @param mint tokenMint (token definition) to be minted + * @param destination token account that will hold the newly minted tokens. Must be able to hold tokens defined by mint + * @param mintAuthority the mint authority that can sign for minting tokens of the given type + * @param amount The amount of tokens to be minted + */ + fun mintTo( + mint: PublicKey, + destination: PublicKey, + mintAuthority: Signer, + amount: Long, + ) { + client.sendAndConfirm( + { txBuilder -> + Token2022Program.factory(txBuilder).mintTo( + mint, + mintAuthority.account, + listOf(Solana.destination(destination, amount)) + ) + }, + mintAuthority, + listOf(mintAuthority) + ) + } + + /** + * Burn tokens of a given mint type + * + * @param tokenAccount the account holding the tokens to be burned + * @param owner The owner of the token account who can sign for getting rid of tokens + * @param tokenMint The token definition address + * @param amount The amount of tokens to be burned + */ + fun burnTokens( + tokenAccount: PublicKey, + owner: Signer, + tokenMint: PublicKey, + amount: Long, + ) { + client.sendAndConfirm( + { txBuilder -> + val data = ByteBuffer.allocate(TOKEN2022_BURN_DATA_SIZE) + .order(ByteOrder.LITTLE_ENDIAN) + .put(TOKEN2022_BURN_ID) + .putLong(amount).array() + txBuilder.append { ixBuilder -> + ixBuilder.program(Token2022.PROGRAM_ID.toPublicKey()) + ixBuilder.account(tokenAccount, false, true) + ixBuilder.account(tokenMint, false, true) + ixBuilder.account(owner.account, true, false) + ixBuilder.data(TOKEN2022_BURN_DATA_SIZE) { buffer -> buffer.put(data) } + } + }, + owner, + emptyList() + ) + } + + /** + * Transfers tokens between two token accounts + * + * @param source Account that will provide the tokens to be transferred + * @param destination Account that will receive the tokens + * @param owner Owner of the source account that needs to sign the transaction + * @param amount The amount of tokens to be transferred + */ + fun transferTokens( + source: PublicKey, + destination: PublicKey, + owner: Signer, + amount: Long, + ) { + client.sendAndConfirm( + { txBuilder -> + Token2022Program.factory(txBuilder).transfer( + source, + destination, + owner.account, + amount, + listOf(owner.account) + ) + }, + owner, + emptyList() + ) + } + + /** + * Helper function to transfer SOL from a funded account to a new account for testing + * @param funder Funded account that can fund the test account + * @param account Address of a test account to be funded + * @param lamports Number of lamports to be transferred. + */ + fun fundAccount( + funder: Signer, + account: PublicKey, + lamports: Long, + ) { + client.sendAndConfirm( + { txBuilder -> + SystemProgram.factory(txBuilder).transfer( + funder.account, + account, + lamports + ) + }, + funder + ) + } + + /** + * Helper function to defund an account when the tests are done. This will send all remaining lamports in an account back to + * a specified address (and thus mark the account for deletion) + * + * @param toBeDefunded Account to be defunded and deleted. This needs to sign the transaction + * @param destination Account where the funds should go + */ + fun defundAccount( + toBeDefunded: Signer, + destination: PublicKey, + ) { + val lamports = client.getBalance(toBeDefunded.account.base58(), RpcParams()).checkResponse("defundAccount")!! + client.sendAndConfirm( + { txBuilder -> + SystemProgram.factory(txBuilder).transfer( + toBeDefunded.account, + destination, + lamports - BASE_TRANSACTION_FEE + ) + }, + toBeDefunded + ) + } +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 0edb0add..334ee21b 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -86,6 +86,22 @@ allprojects { releasesOnly() } } + + // For SNAPSHOT version + maven { + url "https://software.r3.com/artifactory/r3-corda-dev" + authentication { + basic(BasicAuthentication) + } + credentials { + username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') + password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') + } + content { + includeGroup 'com.r3.libs' + includeModule 'com.r3.corda', 'corda-shell' + } + } } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { @@ -136,11 +152,15 @@ dependencies { // CorDapp dependencies. cordapp project(":workflows") cordapp project(":contracts") + cordapp project(":bridging-setup") + + cordapp project(":bridging-contracts") // Used in the test + cordapp project(":bridging-flows") // Used in the test cordaProvided "org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}" cordaProvided "org.apache.logging.log4j:log4j-web:${log4j_version}" cordaProvided "org.slf4j:jul-to-slf4j:$slf4j_version" - cordaDriver "net.corda:corda-shell:$corda_release_version" + cordaDriver "com.r3.corda:corda-shell:$corda_release_version" // Token SDK dependencies. cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version" cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version" @@ -151,6 +171,20 @@ cordapp { minimumPlatformVersion corda_platform_version.toInteger() } +//TODO for now set manually path to solana-aggregator +def solanaAgregatorPath = "" +def solanaNotaryKeyPath = "${solanaAgregatorPath}/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json" +def custodiedKeysDirectory = "${project.buildDir}/nodes/custodied-keys" +def keysDirectory = "${project.buildDir}/nodes/solana-keys" +def funderKeyFile = "${solanaAgregatorPath}/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json" +def bigBankWallet = "${keysDirectory}/big-corp-wallet.json" +def bridgeAuthorityWallet = "${custodiedKeysDirectory}/bridge-authority-wallet.json" +def tokenMintFile = "${keysDirectory}/tokenMint.pub" +def bigBankAccount = "${keysDirectory}/big-corp-token.pub" +def solanaUrl = "https://api.devnet.solana.com" +//TODO ser a task parmeter? +def linearId = '6116560b-c78e-4e13-871d-d666a5d032a3' + task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { nodeDefaults { projectCordapp { @@ -164,7 +198,14 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } node { name "O=Notary,L=London,C=GB" - notary = [validating : false] + notary = [validating : false, + serviceLegalName: "O=Notary Service,L=Zurich,C=CH", + solana : [ + notaryKeypairFile: file(solanaNotaryKeyPath).absolutePath, + custodiedKeysDir : file(custodiedKeysDirectory).absolutePath, + rpcUrl : "https://api.devnet.solana.com" + ] + ] p2pPort 10002 rpcSettings { address("localhost:10003") @@ -207,6 +248,24 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] } + node { + name "O=Bridging Authority,L=New York,C=US" + p2pPort 10018 + rpcSettings { + address("localhost:10019") + adminAddress("localhost:10047") + } + cordapp(project(':bridging-contracts')) + cordapp(project(':bridging-flows')) { + // TODO manual step needed for now - repace placeholders <> with value after deployNodes, before runnnodes + config """\ + participants = { "O=WayneCo,L=SF,C=US" = "" } + mints = { "${linearId}" = "" } + mintAuthorities = { "${linearId}" = "" } + """ + } + rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]] + } } task installQuasar(type: Copy) { @@ -216,3 +275,77 @@ task installQuasar(type: Copy) { } } +tasks.register('createWallets') { + "solana-keygen new -o ${bridgeAuthorityWallet} --no-bip39-passphrase -f".execute() + "solana-keygen new -o ${bigBankWallet} --no-bip39-passphrase -f".execute() +} + +tasks.register("fundAuthorityWallet", JavaExec) { + dependsOn('createWallets') + classpath = sourceSets.main.runtimeClasspath + mainClass = 'net.corda.bank.BridgingSetup' + + args '--rpcUrl' + args "${solanaUrl}" + args '--role' + args 'FUND_WALLET' + args '--quantity' + args 1 + args '--funder' + args "${funderKeyFile}" + args '--target' + args "${bridgeAuthorityWallet}" +} + +tasks.register("createTokenMint", JavaExec) { + dependsOn('fundAuthorityWallet') + classpath = sourceSets.main.runtimeClasspath + mainClass = 'net.corda.bank.BridgingSetup' + + args '--rpcUrl' + args "${solanaUrl}" + args '--role' + args 'CREATE_TOKEN' + args '--funder' + args "${bridgeAuthorityWallet}" + args '--mint' + args "${tokenMintFile}" +} + +tasks.register("fundBigBankWallet", JavaExec) { + dependsOn('createWallets') + classpath = sourceSets.main.runtimeClasspath + mainClass = 'net.corda.bank.BridgingSetup' + + args '--rpcUrl' + args "${solanaUrl}" + args '--role' + args 'FUND_WALLET' + args '--quantity' + args 1 + args '--funder' + args "${funderKeyFile}" + args '--target' + args "${bigBankWallet}" +} + +tasks.register("createTokenAccount", JavaExec) { + dependsOn('createTokenMint', 'fundAuthorityWallet', 'fundBigBankWallet') + classpath = sourceSets.main.runtimeClasspath + mainClass = 'net.corda.bank.BridgingSetup' + + args '--rpcUrl' + args "${solanaUrl}" + args '--role' + args 'CREATE_TOKEN_ACCOUNT' + args '--funder' + args "${bigBankWallet}" + args '--mint' + args "${tokenMintFile}" + args '--account' + args "${bigBankAccount}" +} + +tasks.register("setupBridging") { + dependsOn('createTokenAccount', 'createTokenMint', 'fundAuthorityWallet', 'fundBigBankWallet', 'createWallets') +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/settings.gradle b/Tokens/stockpaydividend/settings.gradle index 853ff054..86a058f1 100644 --- a/Tokens/stockpaydividend/settings.gradle +++ b/Tokens/stockpaydividend/settings.gradle @@ -1,4 +1,4 @@ include 'workflows' include 'contracts' include 'clients' -include 'bridging-contracts', 'bridging-flows' \ No newline at end of file +include 'bridging-contracts', 'bridging-flows', 'bridging-setup' \ No newline at end of file diff --git a/Tokens/stockpaydividend/workflows/build.gradle b/Tokens/stockpaydividend/workflows/build.gradle index 924fb41a..32a13935 100644 --- a/Tokens/stockpaydividend/workflows/build.gradle +++ b/Tokens/stockpaydividend/workflows/build.gradle @@ -59,6 +59,8 @@ dependencies { // Token SDK dependencies. cordaProvided "$tokens_release_group:tokens-contracts:$tokens_release_version" cordaProvided "$tokens_release_group:tokens-workflows:$tokens_release_version" + + cordaProvided "$corda_core_release_group:corda-rpc:$corda_release_version" } task integrationTest(type: Test, dependsOn: []) { From 5e7f12c90e3418234a02554d5131c4fee554f709 Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Thu, 9 Oct 2025 16:29:53 +0100 Subject: [PATCH 02/11] Setup Solana wia bahs scripts --- Tokens/stockpaydividend/README.md | 46 ++++ .../bridging-setup/build.gradle | 27 -- .../kotlin/net/corda/bank/BridgingSetup.kt | 160 ----------- .../net/corda/bank/solana/TokenManagement.kt | 254 ------------------ Tokens/stockpaydividend/build.gradle | 126 +++------ Tokens/stockpaydividend/runDemo.sh | 78 ++++++ Tokens/stockpaydividend/runDevNetSetup.sh | 7 + Tokens/stockpaydividend/runSolana.sh | 18 ++ Tokens/stockpaydividend/settings.gradle | 2 +- .../stockpaydividend/flows/QueryFlows.kt | 8 +- .../resources/bridging-flows-template.conf | 3 + 11 files changed, 188 insertions(+), 541 deletions(-) delete mode 100644 Tokens/stockpaydividend/bridging-setup/build.gradle delete mode 100644 Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt delete mode 100644 Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt create mode 100644 Tokens/stockpaydividend/runDemo.sh create mode 100644 Tokens/stockpaydividend/runDevNetSetup.sh create mode 100644 Tokens/stockpaydividend/runSolana.sh create mode 100644 Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf diff --git a/Tokens/stockpaydividend/README.md b/Tokens/stockpaydividend/README.md index 8809de97..5d900446 100755 --- a/Tokens/stockpaydividend/README.md +++ b/Tokens/stockpaydividend/README.md @@ -123,3 +123,49 @@ Provides basic understanding from the ground up. [Sample - TokenSDK with Account](https://github.com/corda/accounts/tree/master/examples/tokens-integration-test) An basic sample of how account feature can be integrated with TokenSDK +## Bridging To Solana + +##### 1. Create accounts on solana + +##### 2. Build network - deploy Nodes, setup Bridging Authority + +##### 3. Run nodes + +##### 4. IssueStock - Stock Issuer + +WayneCo creates a StockState and issues some stock tokens associated to the created StockState. +> On company WayneCo's node, execute
+`start IssueStock symbol: TEST, name: "Stock, SP500", currency: USD, price: 7.4, issueVol: 500, notary: Notary` + +start CreateAndIssueStock \ +symbol: TEST, \ +name: "Test Stock", \ +currency: USD, \ +price: 7.4, \ +issueVol: 2000, \ +notary: "O=Notary Service,L=Zurich,C=CH", \ +linearId: 6116560b-c78e-4e13-871d-d666a5d032a3 + +##### 5. MoveStock - Stock Issuer + +WayneCo transfers some stock tokens to the Shareholder. +> On company WayneCo's node, execute
+`start MoveStock symbol: TEST, quantity: 100, recipient: "O=Bridging Authority,L=New York,C=US"` + +Now at the Bridging Authority's terminal, we can see that it received 100 stock tokens: +> On shareholder node, execute
`start GetStockBalance symbol: TEST` + +##### 6. Bridging to Solana - Select Token + +> run vaultQuery contractStateType: com.r3.corda.lib.tokens.contracts.states.FungibleToken + +> start GetTokenToBridge symbol: TEST + +##### 7. Bridging to Solana - Bridge + +> start BridgeTokenRpc tokenRef: { txhash: 11EE3F587AF711F2BADB11DEDAA48A4607F6EF8A4ADEC8A3A50410EAC44AD827, index: +> 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" + +##### 8. Check on solana + +##### 9. Redemption \ No newline at end of file diff --git a/Tokens/stockpaydividend/bridging-setup/build.gradle b/Tokens/stockpaydividend/bridging-setup/build.gradle deleted file mode 100644 index 9bb49041..00000000 --- a/Tokens/stockpaydividend/bridging-setup/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -apply plugin: 'net.corda.plugins.cordapp' -apply plugin: 'org.jetbrains.kotlin.jvm' -apply plugin: 'idea' -apply plugin: 'net.corda.plugins.quasar-utils' - -cordapp { - targetPlatformVersion corda_platform_version.toInteger() - minimumPlatformVersion corda_platform_version.toInteger() - workflow { - name "Token Bridging Setup" - vendor "Corda Open Source" - licence "Apache License, Version 2.0" - versionId 1 - } -} - -dependencies { - cordaProvided "$corda_core_release_group:corda-core:$corda_core_release_version" - cordaProvided "$corda_release_group:corda:$corda_release_version" - - implementation 'net.sf.jopt-simple:jopt-simple:5.0.4' - - implementation "org.slf4j:slf4j-api:$slf4j_version" - - cordaProvided "$corda_release_group:corda-solana-sdk:$corda_release_version" - cordaProvided "$corda_release_group:corda-solana-common:$corda_release_version" -} diff --git a/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt b/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt deleted file mode 100644 index 22d80cd1..00000000 --- a/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/BridgingSetup.kt +++ /dev/null @@ -1,160 +0,0 @@ -package net.corda.bank - -import com.lmax.solana4j.api.PublicKey -import joptsimple.OptionParser -import net.corda.bank.solana.TokenManagement -import net.corda.bank.solana.TokenManagement.Companion.LAMPORTS_PER_SOL -import net.corda.core.identity.CordaX500Name -import net.corda.solana.aggregator.common.Signer -import net.corda.solana.aggregator.common.toPublicKey -import net.corda.solana.sdk.instruction.Pubkey -import java.io.File -import kotlin.io.path.Path -import kotlin.io.readText -import kotlin.io.writeText -import kotlin.jvm.java -import kotlin.system.exitProcess -import kotlin.text.trimIndent - -//TODO use from Corda Enterprise -object BridgingSetup { - - private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH") - - private const val AUTHORITY_RPC_PORT = 10021 - - @Suppress("ComplexMethod", "SpreadOperator") - @JvmStatic - fun main(args: Array) { - val parser = OptionParser() - val rpcUrlArg = - parser.accepts("rpcUrl").withRequiredArg().ofType(String::class.java).describedAs("Solana RPC URL") - val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java) - .describedAs("[FUND_WALLET|DEFUND_WALLET|CREATE_TOKEN|CREATE_TOKEN_ACCOUNT]") - val funderArg = parser.accepts("funder").withRequiredArg().ofType(String::class.java) - .describedAs("File path to the funder wallet for funding or defunding") - val targetArg = parser.accepts("target").withRequiredArg().ofType(String::class.java) - .describedAs("File path to the target wallet for funding or defunding") - val tokenMintArg = parser.accepts("mint").withRequiredArg().ofType(String::class.java) - .describedAs("File path to the token mint pubkey file") - val tokenAccArg = parser.accepts("account").withRequiredArg().ofType(String::class.java) - .describedAs("File path to the token account pubkey file") - val quantityArg = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java) - val currencyArg = - parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]") - - val options = try { - parser.parse(*args) - } catch (e: Exception) { - println(e.message) - printHelp(parser) - exitProcess(1) - } - - try { - val role = options.valueOf(roleArg) - val rpcUrl = options.valueOf(rpcUrlArg) - val funderWallet = Signer.fromFile(Path(options.valueOf(funderArg)!!)) - when (role) { - Role.FUND_WALLET -> { - require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } - val targetWallet = Signer.fromFile(Path(options.valueOf(targetArg)!!)) - val quantity = options.valueOf(quantityArg) ?: 1 - println("rpcUrl $rpcUrl") - println("funderWallet $funderWallet") - println("targetWallet.account ${targetWallet.account}") - println("quantity * LAMPORTS_PER_SOL ${quantity * LAMPORTS_PER_SOL}") - TokenManagement(rpcUrl).fundAccount(funderWallet, targetWallet.account, quantity * LAMPORTS_PER_SOL) - } - - Role.DEFUND_WALLET -> { - require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } - val targetWallet = Signer.fromFile(Path(options.valueOf(targetArg)!!)) - - TokenManagement(rpcUrl).defundAccount(targetWallet, funderWallet.account) - } - - Role.CREATE_TOKEN -> { - require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } - val mintKeyFile = options.valueOf(tokenMintArg)!! - // only 2 decimals as this example will bridge dollars - - println("funderWallet $funderWallet") - val tokenMint = TokenManagement(rpcUrl).createToken(funderWallet, decimals = 2) - tokenMint.writeToFile(mintKeyFile) - } - - Role.CREATE_TOKEN_ACCOUNT -> { - require(rpcUrl != null) { "${role.name} requires a Solana rpc URL." } - val tokenMint = readKeyFromFile(options.valueOf(tokenMintArg)!!) - val tokenAccountFile = options.valueOf(tokenAccArg)!! - val tokenAccount = TokenManagement(rpcUrl).createTokenAccount(funderWallet, tokenMint) - tokenAccount.writeToFile(tokenAccountFile) - } - - Role.CREATE_BRIDGING_CONFIG -> { -// println("Requesting creation of bridging config state") -// val tokenMint = readKeyFromFile(options.valueOf(tokenMintArg)!!) -// val params = BankOfCordaWebApi.BridgeConfigRequestParams( -// Currency.getInstance(options.valueOf(currencyArg)), -// "1", -// BOC_NAME, -// NOTARY_NAME, -// tokenMint.base58(), -// funderWallet.account.base58() -// ) -// val result = BankOfCordaClientApi.requestBridgingConfigCreation(NetworkHostAndPort("localhost", AUTHORITY_RPC_PORT), params) -// println("Success! Created state $result") - } - - else -> printHelp(parser) - } - } catch (e: Throwable) { - println("Caught exception: $e") - e.printStackTrace() - printHelp(parser) - throw e - } - } - - fun readKeyFromFile(filename: String): PublicKey = - Pubkey.fromBase58(File(filename).readText(Charsets.US_ASCII)).toPublicKey() - - fun PublicKey.writeToFile(filename: String) { - File(filename).writeText(this.base58(), Charsets.US_ASCII) - } - - private fun printHelp(parser: OptionParser) { - println( - """Modes of operation: - Fund accounts: - --role FUND_WALLET --funder --target [--amount ] - This will transfer an amount of SOL from the funder to the target on the ledger - - Defund accounts: - --role DEFUND_WALLET --funder --target - This will transfer all SOL from the target wallet back to the funder (marking target for deletion) - - Create token: - --role CREATE_TOKEN --funder --mint - This will create a Token2022 definition with funder as payer/mint authority, and store the key in the mint pubkey file - - Create token account: - --role CREATE_TOKEN_ACCOUNT --funder --mint --account - This will create an account owned and payed for by funder that can hold tokens as defined by mint. The key will be written to account. - - Create bridging config - --role CREATE_BRIDGING_CONFIG --funder --mint --currency - """.trimIndent() - ) - parser.printHelpOn(System.out) - } - - enum class Role { - FUND_WALLET, - DEFUND_WALLET, - CREATE_TOKEN, - CREATE_TOKEN_ACCOUNT, - CREATE_BRIDGING_CONFIG - } -} \ No newline at end of file diff --git a/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt b/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt deleted file mode 100644 index 76806173..00000000 --- a/Tokens/stockpaydividend/bridging-setup/src/main/kotlin/net/corda/bank/solana/TokenManagement.kt +++ /dev/null @@ -1,254 +0,0 @@ -package net.corda.bank.solana - -import com.lmax.solana4j.Solana -import com.lmax.solana4j.api.PublicKey -import com.lmax.solana4j.client.jsonrpc.SolanaJsonRpcClient -import com.lmax.solana4j.programs.SystemProgram -import com.lmax.solana4j.programs.SystemProgram.MINT_ACCOUNT_LENGTH -import com.lmax.solana4j.programs.Token2022Program -import com.lmax.solana4j.programs.TokenProgram.ACCOUNT_LAYOUT_SPAN -import net.corda.solana.aggregator.common.RpcParams -import net.corda.solana.aggregator.common.Signer -import net.corda.solana.aggregator.common.checkResponse -import net.corda.solana.aggregator.common.sendAndConfirm -import net.corda.solana.aggregator.common.toPublicKey -import net.corda.solana.sdk.internal.Token2022 -import java.net.http.HttpClient -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.util.Optional - -//TODO use from Corda Enterprise -/** - * Class to prepare and clean up the tokens setup on devnet - */ -class TokenManagement(rpcUrl: String, val rpcParams: RpcParams = RpcParams()) { - - companion object { - /** - * Fee for a transaction with one signature. Each further signature adds the same fee again. - */ - const val BASE_TRANSACTION_FEE: Long = 5000 - const val LAMPORTS_PER_SOL: Long = 1000000000 - - const val TOKEN2022_BURN_DATA_SIZE = 9 - const val TOKEN2022_BURN_ID: Byte = 8 - - } - - private val client = SolanaJsonRpcClient(HttpClient.newHttpClient(), rpcUrl) - - /** - * Create a token mint (a token definition). The public key returned needs to be stored safely as this is required to - * access the token definition in all other operations. - * - * @param payer The account signing and paying for the token creation - * @param mintAuthority The account who will control minting and burning of the token type. Will - * default to the payer. - * @param tokenMint Optional parameter to specify the key of the token. Defaults to a new random key - * @param decimals Decimal positions of the token. Defaults to 9 - * @return returns the public key of the token definition. - */ - fun createToken( - payer: Signer, - mintAuthority: PublicKey = payer.account, - tokenMint: Signer = Signer.random(), - decimals: Byte = 9, - ): PublicKey { - val rentExemption = client.getMinimumBalanceForRentExemption(MINT_ACCOUNT_LENGTH, rpcParams) - .checkResponse("getMinimumBalanceForRentExemption")!! - client.sendAndConfirm( - { txBuilder -> - SystemProgram.factory(txBuilder).createAccount( - payer.account, - tokenMint.account, - rentExemption, - MINT_ACCOUNT_LENGTH.toLong(), - Token2022Program.PROGRAM_ACCOUNT - ) - Token2022Program.factory(txBuilder).initializeMint( - tokenMint.account, - decimals, - mintAuthority, - Optional.empty() - ) - }, - payer, - listOf(tokenMint), - rpcParams - ) - return tokenMint.account - } - - /** - * Creates an account that can hold the Token2022 token created for the given token mint. - * - * @param payer Account paying for and signing the transaction - * @param tokenMint The token definition this account will be able to hold. - * @param accountOwner The owner of the new token account (that will have to sign for transactions moving tokens out of the account) - * @param tokenAccount Optional parameters specifying the keys of the account. Will default to a random key - * @return Returns the public key (i.e. address) of the new token account - */ - fun createTokenAccount( - payer: Signer, - tokenMint: PublicKey, - accountOwner: PublicKey = payer.account, - tokenAccount: Signer = Signer.random(), - ): PublicKey { - val rentExemption = client.getMinimumBalanceForRentExemption(ACCOUNT_LAYOUT_SPAN, rpcParams) - .checkResponse("getMinimumBalanceForRentExemption")!! - client.sendAndConfirm( - { txBuilder -> - SystemProgram.factory(txBuilder).createAccount( - payer.account, - tokenAccount.account, - rentExemption, - ACCOUNT_LAYOUT_SPAN.toLong(), - Token2022Program.PROGRAM_ACCOUNT - ) - Token2022Program.factory(txBuilder).initializeAccount(tokenAccount.account, tokenMint, accountOwner) - }, - payer, - listOf(tokenAccount), - rpcParams - ) - return tokenAccount.account - } - - /** - * Mints tokens of the given type to the given token account. Will fail with an exception if the token mint does not match - * the account, or the account cannot hold tokens. - * - * @param mint tokenMint (token definition) to be minted - * @param destination token account that will hold the newly minted tokens. Must be able to hold tokens defined by mint - * @param mintAuthority the mint authority that can sign for minting tokens of the given type - * @param amount The amount of tokens to be minted - */ - fun mintTo( - mint: PublicKey, - destination: PublicKey, - mintAuthority: Signer, - amount: Long, - ) { - client.sendAndConfirm( - { txBuilder -> - Token2022Program.factory(txBuilder).mintTo( - mint, - mintAuthority.account, - listOf(Solana.destination(destination, amount)) - ) - }, - mintAuthority, - listOf(mintAuthority) - ) - } - - /** - * Burn tokens of a given mint type - * - * @param tokenAccount the account holding the tokens to be burned - * @param owner The owner of the token account who can sign for getting rid of tokens - * @param tokenMint The token definition address - * @param amount The amount of tokens to be burned - */ - fun burnTokens( - tokenAccount: PublicKey, - owner: Signer, - tokenMint: PublicKey, - amount: Long, - ) { - client.sendAndConfirm( - { txBuilder -> - val data = ByteBuffer.allocate(TOKEN2022_BURN_DATA_SIZE) - .order(ByteOrder.LITTLE_ENDIAN) - .put(TOKEN2022_BURN_ID) - .putLong(amount).array() - txBuilder.append { ixBuilder -> - ixBuilder.program(Token2022.PROGRAM_ID.toPublicKey()) - ixBuilder.account(tokenAccount, false, true) - ixBuilder.account(tokenMint, false, true) - ixBuilder.account(owner.account, true, false) - ixBuilder.data(TOKEN2022_BURN_DATA_SIZE) { buffer -> buffer.put(data) } - } - }, - owner, - emptyList() - ) - } - - /** - * Transfers tokens between two token accounts - * - * @param source Account that will provide the tokens to be transferred - * @param destination Account that will receive the tokens - * @param owner Owner of the source account that needs to sign the transaction - * @param amount The amount of tokens to be transferred - */ - fun transferTokens( - source: PublicKey, - destination: PublicKey, - owner: Signer, - amount: Long, - ) { - client.sendAndConfirm( - { txBuilder -> - Token2022Program.factory(txBuilder).transfer( - source, - destination, - owner.account, - amount, - listOf(owner.account) - ) - }, - owner, - emptyList() - ) - } - - /** - * Helper function to transfer SOL from a funded account to a new account for testing - * @param funder Funded account that can fund the test account - * @param account Address of a test account to be funded - * @param lamports Number of lamports to be transferred. - */ - fun fundAccount( - funder: Signer, - account: PublicKey, - lamports: Long, - ) { - client.sendAndConfirm( - { txBuilder -> - SystemProgram.factory(txBuilder).transfer( - funder.account, - account, - lamports - ) - }, - funder - ) - } - - /** - * Helper function to defund an account when the tests are done. This will send all remaining lamports in an account back to - * a specified address (and thus mark the account for deletion) - * - * @param toBeDefunded Account to be defunded and deleted. This needs to sign the transaction - * @param destination Account where the funds should go - */ - fun defundAccount( - toBeDefunded: Signer, - destination: PublicKey, - ) { - val lamports = client.getBalance(toBeDefunded.account.base58(), RpcParams()).checkResponse("defundAccount")!! - client.sendAndConfirm( - { txBuilder -> - SystemProgram.factory(txBuilder).transfer( - toBeDefunded.account, - destination, - lamports - BASE_TRANSACTION_FEE - ) - }, - toBeDefunded - ) - } -} \ No newline at end of file diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 334ee21b..9717cd12 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -152,7 +152,6 @@ dependencies { // CorDapp dependencies. cordapp project(":workflows") cordapp project(":contracts") - cordapp project(":bridging-setup") cordapp project(":bridging-contracts") // Used in the test cordapp project(":bridging-flows") // Used in the test @@ -171,18 +170,14 @@ cordapp { minimumPlatformVersion corda_platform_version.toInteger() } -//TODO for now set manually path to solana-aggregator -def solanaAgregatorPath = "" -def solanaNotaryKeyPath = "${solanaAgregatorPath}/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json" +def solanaNotaryKeyPath = "${project.rootDir}/bridging-flows/src/main/resources/Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json" def custodiedKeysDirectory = "${project.buildDir}/nodes/custodied-keys" def keysDirectory = "${project.buildDir}/nodes/solana-keys" -def funderKeyFile = "${solanaAgregatorPath}/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json" -def bigBankWallet = "${keysDirectory}/big-corp-wallet.json" def bridgeAuthorityWallet = "${custodiedKeysDirectory}/bridge-authority-wallet.json" -def tokenMintFile = "${keysDirectory}/tokenMint.pub" -def bigBankAccount = "${keysDirectory}/big-corp-token.pub" -def solanaUrl = "https://api.devnet.solana.com" -//TODO ser a task parmeter? +def tokenMintFile = "${keysDirectory}/token-mint.pub" +def bigBankFile = "${keysDirectory}/big-corp.pub" +//def solanaUrl = "https://api.devnet.solana.com" + def linearId = '6116560b-c78e-4e13-871d-d666a5d032a3' task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { @@ -203,7 +198,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { solana : [ notaryKeypairFile: file(solanaNotaryKeyPath).absolutePath, custodiedKeysDir : file(custodiedKeysDirectory).absolutePath, - rpcUrl : "https://api.devnet.solana.com" + rpcUrl: "http://localhost:8899" ] ] p2pPort 10002 @@ -256,96 +251,37 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { adminAddress("localhost:10047") } cordapp(project(':bridging-contracts')) - cordapp(project(':bridging-flows')) { - // TODO manual step needed for now - repace placeholders <> with value after deployNodes, before runnnodes - config """\ - participants = { "O=WayneCo,L=SF,C=US" = "" } - mints = { "${linearId}" = "" } - mintAuthorities = { "${linearId}" = "" } - """ - } + cordapp(project(':bridging-flows')) rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]] } } -task installQuasar(type: Copy) { +tasks.register('expandBACordappConfig') { + //dependsOn('deployNodes') + doLast { + def pubkey1 = file(bigBankFile).getText('UTF-8').trim() + def pubkey2 = file(tokenMintFile).getText('UTF-8').trim() + def stdout = new ByteArrayOutputStream() + exec { commandLine 'solana-keygen', 'pubkey', "${bridgeAuthorityWallet}"; standardOutput = stdout } + def pubkey3 = stdout.toString('UTF-8').trim() + + copy { + from "${project.projectDir}/workflows/src/main/resources" + include 'bridging-flows-template.conf' + into "$buildDir/nodes/BridgingAuthority/cordapps/config" + rename { String name -> name.replace('template', '1.0') } + expand(bigBankAccount: pubkey1, + tokenMintAccount: pubkey2, + bridgeAuthorityAccount: pubkey3, + cordaTokenTypeLinearId: linearId, + bigBankCordaIdentity: "O=WayneCo,L=SF,C=US") + } + } +} + +tasks.register('installQuasar', Copy) { destinationDir rootProject.file("lib") from(configurations.quasar) { rename 'quasar-core(.*).jar', 'quasar.jar' } } - -tasks.register('createWallets') { - "solana-keygen new -o ${bridgeAuthorityWallet} --no-bip39-passphrase -f".execute() - "solana-keygen new -o ${bigBankWallet} --no-bip39-passphrase -f".execute() -} - -tasks.register("fundAuthorityWallet", JavaExec) { - dependsOn('createWallets') - classpath = sourceSets.main.runtimeClasspath - mainClass = 'net.corda.bank.BridgingSetup' - - args '--rpcUrl' - args "${solanaUrl}" - args '--role' - args 'FUND_WALLET' - args '--quantity' - args 1 - args '--funder' - args "${funderKeyFile}" - args '--target' - args "${bridgeAuthorityWallet}" -} - -tasks.register("createTokenMint", JavaExec) { - dependsOn('fundAuthorityWallet') - classpath = sourceSets.main.runtimeClasspath - mainClass = 'net.corda.bank.BridgingSetup' - - args '--rpcUrl' - args "${solanaUrl}" - args '--role' - args 'CREATE_TOKEN' - args '--funder' - args "${bridgeAuthorityWallet}" - args '--mint' - args "${tokenMintFile}" -} - -tasks.register("fundBigBankWallet", JavaExec) { - dependsOn('createWallets') - classpath = sourceSets.main.runtimeClasspath - mainClass = 'net.corda.bank.BridgingSetup' - - args '--rpcUrl' - args "${solanaUrl}" - args '--role' - args 'FUND_WALLET' - args '--quantity' - args 1 - args '--funder' - args "${funderKeyFile}" - args '--target' - args "${bigBankWallet}" -} - -tasks.register("createTokenAccount", JavaExec) { - dependsOn('createTokenMint', 'fundAuthorityWallet', 'fundBigBankWallet') - classpath = sourceSets.main.runtimeClasspath - mainClass = 'net.corda.bank.BridgingSetup' - - args '--rpcUrl' - args "${solanaUrl}" - args '--role' - args 'CREATE_TOKEN_ACCOUNT' - args '--funder' - args "${bigBankWallet}" - args '--mint' - args "${tokenMintFile}" - args '--account' - args "${bigBankAccount}" -} - -tasks.register("setupBridging") { - dependsOn('createTokenAccount', 'createTokenMint', 'fundAuthorityWallet', 'fundBigBankWallet', 'createWallets') -} \ No newline at end of file diff --git a/Tokens/stockpaydividend/runDemo.sh b/Tokens/stockpaydividend/runDemo.sh new file mode 100644 index 00000000..1be1a0c0 --- /dev/null +++ b/Tokens/stockpaydividend/runDemo.sh @@ -0,0 +1,78 @@ +cd /Users/szymon.sztuka/workspace/samples-kotlin/Tokens/stockpaydividend + +solana config set --url localhost + +./gradlew deployNodes +# for local net change Notary node.conf rpcUtl = http://localhost:8899 + +NOTARY_FILE=./bridging-flows/src/main/resources/Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json +NOTARY_ACCOUNT=`solana address -k $NOTARY_FILE` +solana airdrop 10 $NOTARY_ACCOUNT + +bridgeAuthorityWallet=./build/nodes/custodied-keys/bridge-authority-wallet.json +solana-keygen new -o $bridgeAuthorityWallet --no-bip39-passphrase -f + +bigBankWallet=./build/nodes/solana-keys/big-corp-wallet.json +solana-keygen new -o $bigBankWallet --no-bip39-passphrase -f + +bridgeAuthorityAccount=`solana address -k $bridgeAuthorityWallet` +funderKeyFile=$NOTARY_FILE +solana transfer $bridgeAuthorityAccount 0.1 --fee-payer $funderKeyFile --from $funderKeyFile --allow-unfunded-recipient + +bigBankAccount=`solana address -k $bigBankWallet` +solana transfer $bigBankAccount 0.1 --fee-payer $funderKeyFile --from $funderKeyFile --allow-unfunded-recipient + +tokenMintFile=./build/nodes/solana-keys/token-mint.json +solana-keygen new -o $tokenMintFile --no-bip39-passphrase + +MINT_ACCOUNT=$(spl-token create-token \ + --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \ + --mint-authority $bridgeAuthorityAccount \ + --fee-payer $bridgeAuthorityWallet \ + --decimals 9 \ + --output json \ + $tokenMintFile | jq -r '.commandOutput.address') + +TOKEN_ACCOUNT=$(spl-token create-account \ + --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \ + --owner $bigBankAccount \ + --fee-payer $NOTARY_FILE \ + $MINT_ACCOUNT | awk '/^Creating account / {print $3}') + +bigBankPubKeyFile=./build/nodes/solana-keys/big-corp.pub +echo $TOKEN_ACCOUNT >> $bigBankPubKeyFile + +tokenMintPubKeyFile=./build/nodes/solana-keys/token-mint.pub +echo $MINT_ACCOUNT >> $tokenMintPubKeyFile + +./gradlew expandBACordappConfig + +./build/nodes/runnodes + +# Check +spl-token balance --address $TOKEN_ACCOUNT +spl-token display $TOKEN_ACCOUNT + + +# On WayneCo node console: +start CreateAndIssueStock \ + symbol: TEST, \ + name: "Test Stock", \ + currency: USD, \ + price: 7.4, \ + issueVol: 2000, \ + notary: "O=Notary Service,L=Zurich,C=CH", \ + linearId: 6116560b-c78e-4e13-871d-d666a5d032a3 + +start MoveStock symbol: TEST, quantity: 1000, recipient: "O=Bridging Authority,L=New York,C=US" + +# On BridgingAuthority node console: +start GetTokenToBridge symbol: TEST + +start BridgeTokenRpc tokenRef: { txhash: , index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" + +start BridgeTokenRpc tokenRef: { txhash: 27D1EA2D305270BD1E31AA89AA1022A209412BA001F4BE46684549E7AA1DC380, index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" + +# Check +spl-token balance --address $TOKEN_ACCOUNT +spl-token display $TOKEN_ACCOUNT \ No newline at end of file diff --git a/Tokens/stockpaydividend/runDevNetSetup.sh b/Tokens/stockpaydividend/runDevNetSetup.sh new file mode 100644 index 00000000..bf8fc4c9 --- /dev/null +++ b/Tokens/stockpaydividend/runDevNetSetup.sh @@ -0,0 +1,7 @@ +#Once off +#cd /bridging-flows/src/main/resources +#solana-keygen grind --starts-with Dev:1 --no-bip39-passphrase + +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar create-network -u http://localhost:8899 -v +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar list-notaries -u http://localhost:8899 \ No newline at end of file diff --git a/Tokens/stockpaydividend/runSolana.sh b/Tokens/stockpaydividend/runSolana.sh new file mode 100644 index 00000000..2982b8de --- /dev/null +++ b/Tokens/stockpaydividend/runSolana.sh @@ -0,0 +1,18 @@ +cd /Users/szymon.sztuka/workspace/enterprise + +#build +./gradlew solana-aggregator:admin-cli:build +cd solana-aggregator/notary-program +anchor build + +#start +solana-test-validator --reset --ledger ../admin-cli/build/test-ledger --bpf-program target/deploy/corda_notary-keypair.json target/deploy/corda_notary.so + +#new terminal +cd /Users/szymon.sztuka/workspace/enterprise +solana airdrop -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json --commitment confirmed 10 +solana airdrop -k solana-aggregator/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json --commitment confirmed 10 +cd solana-aggregator/admin-cli +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar initialize -u http://localhost:8899 -v +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar create-network -u http://localhost:8899 -v +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar authorize --address DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 --network 0 -u http://localhost:8899 -v diff --git a/Tokens/stockpaydividend/settings.gradle b/Tokens/stockpaydividend/settings.gradle index 86a058f1..853ff054 100644 --- a/Tokens/stockpaydividend/settings.gradle +++ b/Tokens/stockpaydividend/settings.gradle @@ -1,4 +1,4 @@ include 'workflows' include 'contracts' include 'clients' -include 'bridging-contracts', 'bridging-flows', 'bridging-setup' \ No newline at end of file +include 'bridging-contracts', 'bridging-flows' \ No newline at end of file diff --git a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt index 7db816f9..70ddd0d6 100644 --- a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt +++ b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt @@ -52,17 +52,17 @@ class GetFiatBalance(private val currencyCode: String) : FlowLogic() { @StartableByRPC class GetTokenToBridge( val symbol: String -) : FlowLogic>>() { +) : FlowLogic>() { override val progressTracker = ProgressTracker() @Suspendable - override fun call(): List> { + override fun call(): List { val page = serviceHub.vaultService.queryBy(StockState::class.java) //TODO + UNCONSUMED query and belonging to our identity val states = page.states.filter { it.state.data.symbol == symbol } val pointer: TokenPointer = states.map { it.state.data.toPointer(StockState::class.java) }.first() - val tokens: List> = serviceHub.vaultService.tokenAmountsByToken(pointer).states - return tokens + val stateRefs: List = serviceHub.vaultService.tokenAmountsByToken(pointer).states.map { "${it.ref}" } + return stateRefs } } \ No newline at end of file diff --git a/Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf b/Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf new file mode 100644 index 00000000..f694c1ec --- /dev/null +++ b/Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf @@ -0,0 +1,3 @@ +participants = {"${bigBankCordaIdentity}" = "${bigBankAccount}"} +mints = {"${cordaTokenTypeLinearId}" = "${tokenMintAccount}"} +mintAuthorities = {"${cordaTokenTypeLinearId}" = "${bridgeAuthorityAccount}"} \ No newline at end of file From 0c67b4ec67497aa3ab8ee02db182706b3b569dad Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Thu, 9 Oct 2025 16:44:13 +0100 Subject: [PATCH 03/11] Setup Solana wia bahs scripts --- Tokens/stockpaydividend/README.md | 2 ++ Tokens/stockpaydividend/build.gradle | 5 ++--- Tokens/stockpaydividend/runDemo.sh | 6 ++++-- Tokens/stockpaydividend/runDevNetSetup.sh | 3 --- Tokens/stockpaydividend/runSolana.sh | 19 ++++++++++--------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Tokens/stockpaydividend/README.md b/Tokens/stockpaydividend/README.md index 5d900446..3242f1ec 100755 --- a/Tokens/stockpaydividend/README.md +++ b/Tokens/stockpaydividend/README.md @@ -129,6 +129,8 @@ An basic sample of how account feature can be integrated with TokenSDK ##### 2. Build network - deploy Nodes, setup Bridging Authority +# for local net change Notary node.conf rpcUtl = http://localhost:8899 + ##### 3. Run nodes ##### 4. IssueStock - Stock Issuer diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 9717cd12..1c34935d 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -176,7 +176,6 @@ def keysDirectory = "${project.buildDir}/nodes/solana-keys" def bridgeAuthorityWallet = "${custodiedKeysDirectory}/bridge-authority-wallet.json" def tokenMintFile = "${keysDirectory}/token-mint.pub" def bigBankFile = "${keysDirectory}/big-corp.pub" -//def solanaUrl = "https://api.devnet.solana.com" def linearId = '6116560b-c78e-4e13-871d-d666a5d032a3' @@ -198,7 +197,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { solana : [ notaryKeypairFile: file(solanaNotaryKeyPath).absolutePath, custodiedKeysDir : file(custodiedKeysDirectory).absolutePath, - rpcUrl: "http://localhost:8899" + rpcUrl: "https://api.devnet.solana.com" ] ] p2pPort 10002 @@ -257,7 +256,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } tasks.register('expandBACordappConfig') { - //dependsOn('deployNodes') + dependsOn('deployNodes') doLast { def pubkey1 = file(bigBankFile).getText('UTF-8').trim() def pubkey2 = file(tokenMintFile).getText('UTF-8').trim() diff --git a/Tokens/stockpaydividend/runDemo.sh b/Tokens/stockpaydividend/runDemo.sh index 1be1a0c0..f5db6905 100644 --- a/Tokens/stockpaydividend/runDemo.sh +++ b/Tokens/stockpaydividend/runDemo.sh @@ -1,9 +1,11 @@ -cd /Users/szymon.sztuka/workspace/samples-kotlin/Tokens/stockpaydividend +#!/bin/bash +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar create-network -u http://localhost:8899 -v +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 +java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar list-notaries -u http://localhost:8899 solana config set --url localhost ./gradlew deployNodes -# for local net change Notary node.conf rpcUtl = http://localhost:8899 NOTARY_FILE=./bridging-flows/src/main/resources/Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json NOTARY_ACCOUNT=`solana address -k $NOTARY_FILE` diff --git a/Tokens/stockpaydividend/runDevNetSetup.sh b/Tokens/stockpaydividend/runDevNetSetup.sh index bf8fc4c9..fbfedcea 100644 --- a/Tokens/stockpaydividend/runDevNetSetup.sh +++ b/Tokens/stockpaydividend/runDevNetSetup.sh @@ -2,6 +2,3 @@ #cd /bridging-flows/src/main/resources #solana-keygen grind --starts-with Dev:1 --no-bip39-passphrase -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar create-network -u http://localhost:8899 -v -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar list-notaries -u http://localhost:8899 \ No newline at end of file diff --git a/Tokens/stockpaydividend/runSolana.sh b/Tokens/stockpaydividend/runSolana.sh index 2982b8de..1c6a51a8 100644 --- a/Tokens/stockpaydividend/runSolana.sh +++ b/Tokens/stockpaydividend/runSolana.sh @@ -1,18 +1,19 @@ -cd /Users/szymon.sztuka/workspace/enterprise +#!/bin/bash + +ADMIN_CLI="./solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" #build -./gradlew solana-aggregator:admin-cli:build -cd solana-aggregator/notary-program -anchor build +#./gradlew solana-aggregator:admin-cli:build +#cd solana-aggregator/notary-program +#anchor build #start solana-test-validator --reset --ledger ../admin-cli/build/test-ledger --bpf-program target/deploy/corda_notary-keypair.json target/deploy/corda_notary.so #new terminal -cd /Users/szymon.sztuka/workspace/enterprise + solana airdrop -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json --commitment confirmed 10 solana airdrop -k solana-aggregator/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json --commitment confirmed 10 -cd solana-aggregator/admin-cli -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar initialize -u http://localhost:8899 -v -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar create-network -u http://localhost:8899 -v -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar authorize --address DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 --network 0 -u http://localhost:8899 -v +java -jar $ADMIN_CLI initialize -u http://localhost:8899 -v +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v +java -jar $ADMIN_CLI authorize --address DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 --network 0 -u http://localhost:8899 -v From 028ce5ad65a465c7f94c2789aae1c5960db9039d Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Thu, 9 Oct 2025 17:41:33 +0100 Subject: [PATCH 04/11] Fixes --- Tokens/stockpaydividend/runDemo.sh | 5 +--- Tokens/stockpaydividend/runDevNetSetup.sh | 4 --- Tokens/stockpaydividend/runSolana.sh | 10 +++---- .../stockpaydividend/flows/QueryFlows.kt | 26 +++++++++++++++---- 4 files changed, 26 insertions(+), 19 deletions(-) delete mode 100644 Tokens/stockpaydividend/runDevNetSetup.sh diff --git a/Tokens/stockpaydividend/runDemo.sh b/Tokens/stockpaydividend/runDemo.sh index f5db6905..6257aa86 100644 --- a/Tokens/stockpaydividend/runDemo.sh +++ b/Tokens/stockpaydividend/runDemo.sh @@ -55,7 +55,6 @@ echo $MINT_ACCOUNT >> $tokenMintPubKeyFile spl-token balance --address $TOKEN_ACCOUNT spl-token display $TOKEN_ACCOUNT - # On WayneCo node console: start CreateAndIssueStock \ symbol: TEST, \ @@ -69,12 +68,10 @@ start CreateAndIssueStock \ start MoveStock symbol: TEST, quantity: 1000, recipient: "O=Bridging Authority,L=New York,C=US" # On BridgingAuthority node console: -start GetTokenToBridge symbol: TEST +start GetTokenToBridgeFormatted symbol: TEST start BridgeTokenRpc tokenRef: { txhash: , index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" -start BridgeTokenRpc tokenRef: { txhash: 27D1EA2D305270BD1E31AA89AA1022A209412BA001F4BE46684549E7AA1DC380, index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" - # Check spl-token balance --address $TOKEN_ACCOUNT spl-token display $TOKEN_ACCOUNT \ No newline at end of file diff --git a/Tokens/stockpaydividend/runDevNetSetup.sh b/Tokens/stockpaydividend/runDevNetSetup.sh deleted file mode 100644 index fbfedcea..00000000 --- a/Tokens/stockpaydividend/runDevNetSetup.sh +++ /dev/null @@ -1,4 +0,0 @@ -#Once off -#cd /bridging-flows/src/main/resources -#solana-keygen grind --starts-with Dev:1 --no-bip39-passphrase - diff --git a/Tokens/stockpaydividend/runSolana.sh b/Tokens/stockpaydividend/runSolana.sh index 1c6a51a8..ab72a424 100644 --- a/Tokens/stockpaydividend/runSolana.sh +++ b/Tokens/stockpaydividend/runSolana.sh @@ -1,17 +1,15 @@ #!/bin/bash -ADMIN_CLI="./solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" - #build -#./gradlew solana-aggregator:admin-cli:build -#cd solana-aggregator/notary-program -#anchor build +./gradlew solana-aggregator:admin-cli:build +cd solana-aggregator/notary-program +anchor build #start solana-test-validator --reset --ledger ../admin-cli/build/test-ledger --bpf-program target/deploy/corda_notary-keypair.json target/deploy/corda_notary.so #new terminal - +ADMIN_CLI="./solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" solana airdrop -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json --commitment confirmed 10 solana airdrop -k solana-aggregator/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json --commitment confirmed 10 java -jar $ADMIN_CLI initialize -u http://localhost:8899 -v diff --git a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt index 70ddd0d6..3caf58ef 100644 --- a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt +++ b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt @@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import com.r3.corda.lib.tokens.contracts.states.FungibleToken import com.r3.corda.lib.tokens.contracts.types.TokenPointer import com.r3.corda.lib.tokens.money.FiatCurrency.Companion.getInstance +import com.r3.corda.lib.tokens.workflows.flows.move.MoveTokensFlowHandler import com.r3.corda.lib.tokens.workflows.utilities.tokenAmountsByToken import com.r3.corda.lib.tokens.workflows.utilities.tokenBalance import net.corda.core.contracts.StateAndRef @@ -46,23 +47,38 @@ class GetFiatBalance(private val currencyCode: String) : FlowLogic() { } } - //TODO this is temporally, used by Bridging Authority @InitiatingFlow @StartableByRPC class GetTokenToBridge( val symbol: String -) : FlowLogic>() { +) : FlowLogic>>() { override val progressTracker = ProgressTracker() @Suspendable - override fun call(): List { + override fun call(): List> { val page = serviceHub.vaultService.queryBy(StockState::class.java) //TODO + UNCONSUMED query and belonging to our identity val states = page.states.filter { it.state.data.symbol == symbol } val pointer: TokenPointer = states.map { it.state.data.toPointer(StockState::class.java) }.first() - val stateRefs: List = serviceHub.vaultService.tokenAmountsByToken(pointer).states.map { "${it.ref}" } - return stateRefs + val tokens: List> = serviceHub.vaultService.tokenAmountsByToken(pointer).states + return tokens + } +} + +@InitiatingFlow +@StartableByRPC +class GetTokenToBridgeFormatted( + val symbol: String +) : FlowLogic>() { + + override val progressTracker = ProgressTracker() + + @Suspendable + override fun call(): List { + val tokens: List> = subFlow(GetTokenToBridge(symbol)) + val formatted: List = tokens.map { "${it.ref}" } + return formatted } } \ No newline at end of file From ef77776e5355bfa22248c07a8fb963970a9e3180 Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Thu, 9 Oct 2025 17:47:19 +0100 Subject: [PATCH 05/11] reduce unnecessary diff --- .../lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt | 2 +- .../lib/tokens/bridging/flows/BridgeTokensUtilities.kt | 7 ++++--- Tokens/stockpaydividend/build.gradle | 2 +- .../net/corda/samples/stockpaydividend/flows/QueryFlows.kt | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt index b7b3e2a4..9145456c 100644 --- a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt +++ b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeFungibleTokenFlow.kt @@ -60,7 +60,7 @@ class BridgeFungibleTokenFlow( destination, bridgeAuthority ) - logger.info("Generating Solana bridging transaction with the following parameters: mint=$mint, mintAuthority=$mintAuthority, destination=$destination.") + return subFlow( InternalBridgeFungibleTokenFlow( participantSessions = participantSessions, diff --git a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt index 3b6dae5c..ecb5f602 100644 --- a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt +++ b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/BridgeTokensUtilities.kt @@ -54,9 +54,10 @@ fun bridgeTokens( addCommand(additionalCommand, keys) } } - require(additionalCommand is BridgingContract.BridgingCommand.BridgeToSolana) { "Only bridging to Solana is supported" } - val instruction = Token2022.mintTo(mint, destination, mintAuthority, quantity) - transactionBuilder.addNotaryInstruction(instruction) + if (additionalCommand is BridgingContract.BridgingCommand.BridgeToSolana) { + val instruction = Token2022.mintTo(mint, destination, mintAuthority, quantity) + transactionBuilder.addNotaryInstruction(instruction) + } return transactionBuilder } diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 1c34935d..9b339fa0 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -278,7 +278,7 @@ tasks.register('expandBACordappConfig') { } } -tasks.register('installQuasar', Copy) { +task installQuasar(type: Copy) { destinationDir rootProject.file("lib") from(configurations.quasar) { rename 'quasar-core(.*).jar', 'quasar.jar' diff --git a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt index 3caf58ef..818b78ec 100644 --- a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt +++ b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt @@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable import com.r3.corda.lib.tokens.contracts.states.FungibleToken import com.r3.corda.lib.tokens.contracts.types.TokenPointer import com.r3.corda.lib.tokens.money.FiatCurrency.Companion.getInstance -import com.r3.corda.lib.tokens.workflows.flows.move.MoveTokensFlowHandler import com.r3.corda.lib.tokens.workflows.utilities.tokenAmountsByToken import com.r3.corda.lib.tokens.workflows.utilities.tokenBalance import net.corda.core.contracts.StateAndRef @@ -47,6 +46,7 @@ class GetFiatBalance(private val currencyCode: String) : FlowLogic() { } } + //TODO this is temporally, used by Bridging Authority @InitiatingFlow @StartableByRPC From 3941e7dfeb8603a2064e242bffd6ccc31c774eb9 Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Thu, 9 Oct 2025 17:52:51 +0100 Subject: [PATCH 06/11] Undo driver version change to fix test --- Tokens/stockpaydividend/build.gradle | 2 +- Tokens/stockpaydividend/workflows/build.gradle | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 9b339fa0..54f73c9a 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -159,7 +159,7 @@ dependencies { cordaProvided "org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}" cordaProvided "org.apache.logging.log4j:log4j-web:${log4j_version}" cordaProvided "org.slf4j:jul-to-slf4j:$slf4j_version" - cordaDriver "com.r3.corda:corda-shell:$corda_release_version" + cordaDriver "net.corda:corda-shell:$corda_release_version" // Token SDK dependencies. cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version" cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version" diff --git a/Tokens/stockpaydividend/workflows/build.gradle b/Tokens/stockpaydividend/workflows/build.gradle index 32a13935..924fb41a 100644 --- a/Tokens/stockpaydividend/workflows/build.gradle +++ b/Tokens/stockpaydividend/workflows/build.gradle @@ -59,8 +59,6 @@ dependencies { // Token SDK dependencies. cordaProvided "$tokens_release_group:tokens-contracts:$tokens_release_version" cordaProvided "$tokens_release_group:tokens-workflows:$tokens_release_version" - - cordaProvided "$corda_core_release_group:corda-rpc:$corda_release_version" } task integrationTest(type: Test, dependsOn: []) { From c6602cb855f69774bb281565d27712d22773a2bb Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Fri, 10 Oct 2025 11:49:12 +0100 Subject: [PATCH 07/11] Fixes to run deployNodes --- Tokens/stockpaydividend/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 54f73c9a..5b8f0b1e 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -159,7 +159,7 @@ dependencies { cordaProvided "org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}" cordaProvided "org.apache.logging.log4j:log4j-web:${log4j_version}" cordaProvided "org.slf4j:jul-to-slf4j:$slf4j_version" - cordaDriver "net.corda:corda-shell:$corda_release_version" + cordaDriver "com.r3.corda:corda-shell:$corda_release_version" // Token SDK dependencies. cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version" cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version" @@ -256,7 +256,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } tasks.register('expandBACordappConfig') { - dependsOn('deployNodes') doLast { def pubkey1 = file(bigBankFile).getText('UTF-8').trim() def pubkey2 = file(tokenMintFile).getText('UTF-8').trim() From 551dc6a3855254a35fca80afb69bbc37dce5e37a Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Fri, 10 Oct 2025 11:50:45 +0100 Subject: [PATCH 08/11] Small fixes --- Tokens/stockpaydividend/runDemo.sh | 8 +++++--- Tokens/stockpaydividend/runSolana.sh | 9 +++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Tokens/stockpaydividend/runDemo.sh b/Tokens/stockpaydividend/runDemo.sh index 6257aa86..1288db2b 100644 --- a/Tokens/stockpaydividend/runDemo.sh +++ b/Tokens/stockpaydividend/runDemo.sh @@ -1,7 +1,9 @@ #!/bin/bash -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar create-network -u http://localhost:8899 -v -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -java -jar build/libs/admin-cli-4.14-SNAPSHOT.jar list-notaries -u http://localhost:8899 +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" + +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json solana config set --url localhost diff --git a/Tokens/stockpaydividend/runSolana.sh b/Tokens/stockpaydividend/runSolana.sh index ab72a424..6f410863 100644 --- a/Tokens/stockpaydividend/runSolana.sh +++ b/Tokens/stockpaydividend/runSolana.sh @@ -5,13 +5,14 @@ cd solana-aggregator/notary-program anchor build + #start solana-test-validator --reset --ledger ../admin-cli/build/test-ledger --bpf-program target/deploy/corda_notary-keypair.json target/deploy/corda_notary.so #new terminal -ADMIN_CLI="./solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" solana airdrop -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json --commitment confirmed 10 solana airdrop -k solana-aggregator/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json --commitment confirmed 10 -java -jar $ADMIN_CLI initialize -u http://localhost:8899 -v -java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -java -jar $ADMIN_CLI authorize --address DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 --network 0 -u http://localhost:8899 -v +java -jar $ADMIN_CLI initialize -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI authorize --address DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 --network 0 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json From 22108e668e2bccb0ab37f19e87c8a553f6e6ea4e Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Fri, 10 Oct 2025 16:00:27 +0100 Subject: [PATCH 09/11] readme --- Tokens/stockpaydividend/README.md | 127 ++++++++++++++---- Tokens/stockpaydividend/addCordaNetwork.sh | 6 + .../{runDemo.sh => setupSolanaAccounts.sh} | 39 +----- 3 files changed, 110 insertions(+), 62 deletions(-) create mode 100644 Tokens/stockpaydividend/addCordaNetwork.sh rename Tokens/stockpaydividend/{runDemo.sh => setupSolanaAccounts.sh} (53%) diff --git a/Tokens/stockpaydividend/README.md b/Tokens/stockpaydividend/README.md index 3242f1ec..7a7c1d2d 100755 --- a/Tokens/stockpaydividend/README.md +++ b/Tokens/stockpaydividend/README.md @@ -125,20 +125,96 @@ An basic sample of how account feature can be integrated with TokenSDK ## Bridging To Solana -##### 1. Create accounts on solana +### Running on Solana Dev Net -##### 2. Build network - deploy Nodes, setup Bridging Authority +Build nodes: -# for local net change Notary node.conf rpcUtl = http://localhost:8899 +```bash +./gradlew deployNodes +``` -##### 3. Run nodes +Create Solana Accounts and Fund accounts of WayneCo and the BridgingAuthority, create Mint and TokenAccount to be +bridged to: -##### 4. IssueStock - Stock Issuer +```bash +./setupSolanaAccounts.sh +``` -WayneCo creates a StockState and issues some stock tokens associated to the created StockState. -> On company WayneCo's node, execute
-`start IssueStock symbol: TEST, name: "Stock, SP500", currency: USD, price: 7.4, issueVol: 500, notary: Notary` +Amend `BrigindAuthority` Cordapp's configuration with mappings to newly created accounts: + +```bash +/gradlew expandBACordappConfig +``` + +Run nodes: + +```bash +./build/nodes/runnodes +``` + +### Running with Solana Local Validator + +Start a local solana validator with the notary program deployed following instruction from Corda Enterprise. +(the below commands can be run in one go from `./addCordaNetowrk.sh` script). + +```bash +Add the new network and Notary key to Solana. This Notary will be used by the Corda nodes. +```bash +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +``` + +The command will output confirmation with network ID `1`: + +``` +Creating network ... +✓ Corda network creation successful - network ID: 1 +``` + +Add the Notary key to the network: + +```bash +> java -jar $ADMIN_CLI authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +``` + +This command will output: + +``` +> Authorizing notary Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5... +> ✓ Notary authorized successfully: Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 +``` + +You can list the authorized notaries with: + +```bash +java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +``` + +``` +Network ID: 0 + 1. Notary: DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 +Network ID: 1 + 1. Notary: Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 +``` + +Follow the steps from Running on Solana Dev Net. +The only change is to replace Solana Dev Net url with local validator url `http://localhost:8899` in notary config in +`depolyNodes` task. . + +#### Using the Cordapps + +You can check the current balance on Solana Account: +```bash +spl-token balance --address $TOKEN_ACCOUNT +spl-token display $TOKEN_ACCOUNT +``` + +##### On WayneCo node console: + +Issue Stock `TEST` with liniearID `6116560b-c78e-4e13-871d-d666a5d032a3` matching configuration in Bridging Authority. + +```bash start CreateAndIssueStock \ symbol: TEST, \ name: "Test Stock", \ @@ -147,27 +223,30 @@ price: 7.4, \ issueVol: 2000, \ notary: "O=Notary Service,L=Zurich,C=CH", \ linearId: 6116560b-c78e-4e13-871d-d666a5d032a3 +``` -##### 5. MoveStock - Stock Issuer - -WayneCo transfers some stock tokens to the Shareholder. -> On company WayneCo's node, execute
-`start MoveStock symbol: TEST, quantity: 100, recipient: "O=Bridging Authority,L=New York,C=US"` - -Now at the Bridging Authority's terminal, we can see that it received 100 stock tokens: -> On shareholder node, execute
`start GetStockBalance symbol: TEST` +Move 1000 stock tokens to self to Bridging Authority: -##### 6. Bridging to Solana - Select Token +```bash +start MoveStock symbol: TEST, quantity: 1000, recipient: "O=Bridging Authority,L=New York,C=US" +``` -> run vaultQuery contractStateType: com.r3.corda.lib.tokens.contracts.states.FungibleToken +##### On BridgingAuthority node console: -> start GetTokenToBridge symbol: TEST +This is currently a manual step to showcase how bridging, normally the bridging flow starts automatically as soon as +a participant moves token(s) to the Bridging Authority: -##### 7. Bridging to Solana - Bridge +```bash +start GetTokenToBridgeFormatted symbol: TEST +``` -> start BridgeTokenRpc tokenRef: { txhash: 11EE3F587AF711F2BADB11DEDAA48A4607F6EF8A4ADEC8A3A50410EAC44AD827, index: -> 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" +```bash +start BridgeTokenRpc tokenRef: { txhash: , index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" +``` -##### 8. Check on solana +You can check the current balance on Solana Account has increased: -##### 9. Redemption \ No newline at end of file +```bash +spl-token balance --address $TOKEN_ACCOUNT +spl-token display $TOKEN_ACCOUNT +``` \ No newline at end of file diff --git a/Tokens/stockpaydividend/addCordaNetwork.sh b/Tokens/stockpaydividend/addCordaNetwork.sh new file mode 100644 index 00000000..c647a017 --- /dev/null +++ b/Tokens/stockpaydividend/addCordaNetwork.sh @@ -0,0 +1,6 @@ +#!/bin/bash +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" + +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json diff --git a/Tokens/stockpaydividend/runDemo.sh b/Tokens/stockpaydividend/setupSolanaAccounts.sh similarity index 53% rename from Tokens/stockpaydividend/runDemo.sh rename to Tokens/stockpaydividend/setupSolanaAccounts.sh index 1288db2b..092cae8d 100644 --- a/Tokens/stockpaydividend/runDemo.sh +++ b/Tokens/stockpaydividend/setupSolanaAccounts.sh @@ -1,14 +1,6 @@ #!/bin/bash -ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" - -java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json -java -jar $ADMIN_CLI authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json -java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json - solana config set --url localhost -./gradlew deployNodes - NOTARY_FILE=./bridging-flows/src/main/resources/Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json NOTARY_ACCOUNT=`solana address -k $NOTARY_FILE` solana airdrop 10 $NOTARY_ACCOUNT @@ -47,33 +39,4 @@ bigBankPubKeyFile=./build/nodes/solana-keys/big-corp.pub echo $TOKEN_ACCOUNT >> $bigBankPubKeyFile tokenMintPubKeyFile=./build/nodes/solana-keys/token-mint.pub -echo $MINT_ACCOUNT >> $tokenMintPubKeyFile - -./gradlew expandBACordappConfig - -./build/nodes/runnodes - -# Check -spl-token balance --address $TOKEN_ACCOUNT -spl-token display $TOKEN_ACCOUNT - -# On WayneCo node console: -start CreateAndIssueStock \ - symbol: TEST, \ - name: "Test Stock", \ - currency: USD, \ - price: 7.4, \ - issueVol: 2000, \ - notary: "O=Notary Service,L=Zurich,C=CH", \ - linearId: 6116560b-c78e-4e13-871d-d666a5d032a3 - -start MoveStock symbol: TEST, quantity: 1000, recipient: "O=Bridging Authority,L=New York,C=US" - -# On BridgingAuthority node console: -start GetTokenToBridgeFormatted symbol: TEST - -start BridgeTokenRpc tokenRef: { txhash: , index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" - -# Check -spl-token balance --address $TOKEN_ACCOUNT -spl-token display $TOKEN_ACCOUNT \ No newline at end of file +echo $MINT_ACCOUNT >> $tokenMintPubKeyFile \ No newline at end of file From 27edc582c5fabc329497e7ea7176c3d814cbf8d4 Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Fri, 10 Oct 2025 20:34:03 +0100 Subject: [PATCH 10/11] Use dev key from toolkit project --- Tokens/stockpaydividend/README.md | 6 ++--- Tokens/stockpaydividend/build.gradle | 26 ++++++++++++++++++- .../stockpaydividend/setupSolanaAccounts.sh | 7 +++-- 3 files changed, 31 insertions(+), 8 deletions(-) mode change 100644 => 100755 Tokens/stockpaydividend/setupSolanaAccounts.sh diff --git a/Tokens/stockpaydividend/README.md b/Tokens/stockpaydividend/README.md index 7a7c1d2d..7e80888c 100755 --- a/Tokens/stockpaydividend/README.md +++ b/Tokens/stockpaydividend/README.md @@ -137,13 +137,13 @@ Create Solana Accounts and Fund accounts of WayneCo and the BridgingAuthority, c bridged to: ```bash -./setupSolanaAccounts.sh +./gradlew setupSolanaAccounts ``` Amend `BrigindAuthority` Cordapp's configuration with mappings to newly created accounts: ```bash -/gradlew expandBACordappConfig +./gradlew expandBACordappConfig ``` Run nodes: @@ -155,7 +155,7 @@ Run nodes: ### Running with Solana Local Validator Start a local solana validator with the notary program deployed following instruction from Corda Enterprise. -(the below commands can be run in one go from `./addCordaNetowrk.sh` script). +(the below commands can be run in one go from `./addCordaNetwork.sh` script). ```bash Add the new network and Notary key to Solana. This Notary will be used by the Corda nodes. diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 5b8f0b1e..9c468be8 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -170,7 +170,8 @@ cordapp { minimumPlatformVersion corda_platform_version.toInteger() } -def solanaNotaryKeyPath = "${project.rootDir}/bridging-flows/src/main/resources/Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json" +def solanaNotaryKeyFileName = 'Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json' +def solanaNotaryKeyPath = "$buildDir/extracted/dev-keys/$solanaNotaryKeyFileName" def custodiedKeysDirectory = "${project.buildDir}/nodes/custodied-keys" def keysDirectory = "${project.buildDir}/nodes/solana-keys" def bridgeAuthorityWallet = "${custodiedKeysDirectory}/bridge-authority-wallet.json" @@ -180,6 +181,7 @@ def bigBankFile = "${keysDirectory}/big-corp.pub" def linearId = '6116560b-c78e-4e13-871d-d666a5d032a3' task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { + dependsOn('extractDevKey') nodeDefaults { projectCordapp { deploy = false @@ -283,3 +285,25 @@ task installQuasar(type: Copy) { rename 'quasar-core(.*).jar', 'quasar.jar' } } + +configurations { + libJar { + transitive = false + } +} + +dependencies { + libJar 'com.r3.corda.lib.solana:token-bridging-workflows:0.1.0-SNAPSHOT@jar' +} + +tasks.register('extractDevKey', Copy) { + from({ zipTree(configurations.libJar.singleFile) }) { + include "dev-keys/$solanaNotaryKeyFileName" + } + into "$buildDir/extracted" +} + +tasks.register('setupSolanaAccounts', Exec) { + dependsOn('extractDevKey') + commandLine 'bash', '-x', "$projectDir/setupSolanaAccounts.sh", "$solanaNotaryKeyPath" +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/setupSolanaAccounts.sh b/Tokens/stockpaydividend/setupSolanaAccounts.sh old mode 100644 new mode 100755 index 092cae8d..c1101b02 --- a/Tokens/stockpaydividend/setupSolanaAccounts.sh +++ b/Tokens/stockpaydividend/setupSolanaAccounts.sh @@ -1,8 +1,7 @@ #!/bin/bash solana config set --url localhost -NOTARY_FILE=./bridging-flows/src/main/resources/Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json -NOTARY_ACCOUNT=`solana address -k $NOTARY_FILE` +NOTARY_ACCOUNT=`solana address -k $1` solana airdrop 10 $NOTARY_ACCOUNT bridgeAuthorityWallet=./build/nodes/custodied-keys/bridge-authority-wallet.json @@ -12,7 +11,7 @@ bigBankWallet=./build/nodes/solana-keys/big-corp-wallet.json solana-keygen new -o $bigBankWallet --no-bip39-passphrase -f bridgeAuthorityAccount=`solana address -k $bridgeAuthorityWallet` -funderKeyFile=$NOTARY_FILE +funderKeyFile=$1 solana transfer $bridgeAuthorityAccount 0.1 --fee-payer $funderKeyFile --from $funderKeyFile --allow-unfunded-recipient bigBankAccount=`solana address -k $bigBankWallet` @@ -32,7 +31,7 @@ MINT_ACCOUNT=$(spl-token create-token \ TOKEN_ACCOUNT=$(spl-token create-account \ --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \ --owner $bigBankAccount \ - --fee-payer $NOTARY_FILE \ + --fee-payer $funderKeyFile \ $MINT_ACCOUNT | awk '/^Creating account / {print $3}') bigBankPubKeyFile=./build/nodes/solana-keys/big-corp.pub From 885205a57b70131f4b52d8a28415cace50ebc79d Mon Sep 17 00:00:00 2001 From: Szymon Sztuka Date: Mon, 13 Oct 2025 13:41:12 +0100 Subject: [PATCH 11/11] Minor fixes --- Tokens/stockpaydividend/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tokens/stockpaydividend/README.md b/Tokens/stockpaydividend/README.md index 7e80888c..c7c45322 100755 --- a/Tokens/stockpaydividend/README.md +++ b/Tokens/stockpaydividend/README.md @@ -157,7 +157,6 @@ Run nodes: Start a local solana validator with the notary program deployed following instruction from Corda Enterprise. (the below commands can be run in one go from `./addCordaNetwork.sh` script). -```bash Add the new network and Notary key to Solana. This Notary will be used by the Corda nodes. ```bash ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" @@ -190,6 +189,7 @@ You can list the authorized notaries with: java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json ``` +The expected output is: ``` Network ID: 0 1. Notary: DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 @@ -199,7 +199,7 @@ Network ID: 1 Follow the steps from Running on Solana Dev Net. The only change is to replace Solana Dev Net url with local validator url `http://localhost:8899` in notary config in -`depolyNodes` task. . +`depolyNodes` task. #### Using the Cordapps @@ -212,7 +212,7 @@ spl-token display $TOKEN_ACCOUNT ##### On WayneCo node console: -Issue Stock `TEST` with liniearID `6116560b-c78e-4e13-871d-d666a5d032a3` matching configuration in Bridging Authority. +Issue Stock `TEST` with linearID `6116560b-c78e-4e13-871d-d666a5d032a3` matching configuration in Bridging Authority. ```bash start CreateAndIssueStock \