solana.transaction accepts an unsigned transaction in Base64 and requires an Ed25519 key. TKeeper checks that the signing key is a required signer of the message. Build with -Pkeeper.features=solana -Pkeeper.platforms=ecc.
Example: one native transfer
This authority permits one required signer and a transfer of at most 100 lamports to one recipient:
schemaVersion: verdict.authority/v1
id: test:solana/native-transfer
type: solana.transaction
version: 1.0.0
config: {}
policy:
id: solana-native-transfer
fallback: DENY
variables:
recipient: "8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR"
allow:
- id: small-transfer
where:
- "requiredSignatures == 1"
- "effect.onlyTypes(effects, ['native.transfer'])"
- "effect.one(effects, 'native.transfer')"
- "effect.all(effects, 'native.transfer', {'asset': 'sol', 'to': recipient})"
- "bigint.gt(effect.amount(effects, 'native.transfer'), '0')"
- "bigint.lte(effect.amount(effects, 'native.transfer'), '100')"
Build the message with the TKeeper key as a required signer, then compose:
var command = Command.of("test:solana/native-transfer",
new UnsignedSolanaTransaction(unsignedSolana64));
var signed = client.signature().compose(
Sign.of(solanaKeyId, command), SignedSolanaTransaction.class);
if (!signed.complete()) throw new IllegalStateException("Missing signatures");
String transaction64 = signed.rawTransaction();
String transactionHash = signed.transactionHash();
For policies that allow multiple signers, the composer can return a partial transaction with complete: false and no hash. With a tweak, build the message using central().getPublicKey(solanaKeyId, tweak). The caller submits the complete transaction. Composer describes the common response contract.