-
Notifications
You must be signed in to change notification settings - Fork 3
High Level Functionality
This page describes some of the high-level functionality of the Radix Engine toolkit and shows examples of how various concepts in the toolkit can be used together to construct manifests and transactions that can be submitted to the network.
The Radix Engine Toolkit comes with a manifest builder which is heavily inspired by the builder present in the Scrypto repository and commonly seen in unit tests. This manifest builder has an ID allocator which allows users of the builder to not specify the ids for various buckets and proofs created in the manifest. Additionally, the manifest builder is able to handle blobs in a more developer friendly way for instructions such as PublishPackage
.
The following example constructs a transaction manifest for a resource transfer where some tokens are withdrawn from an account, put in a bucket, and the deposited into another account.
import {
ManifestBuilder,
address,
bucket,
decimal,
} from "@radixdlt/radix-engine-toolkit";
let manifest = new ManifestBuilder()
.callMethod(
"account_sim1q3cztnp4h232hsfmu0j63f7f7mz5wxhd0n0hqax6smjqznhzrp",
"withdraw",
[
address(
"resource_sim1qf7mtmy9a6eczv9km4j4ul38cfvap0zy6juuj8m8xnxqlla6pd"
),
decimal(10),
]
)
.takeAllFromWorktop(
"resource_sim1qf7mtmy9a6eczv9km4j4ul38cfvap0zy6juuj8m8xnxqlla6pd",
(builder, bucketId) =>
builder.callMethod(
"account_sim1qs5mg6tcehg95mugc9d3lpl90clnl787zmhc92cf04wqvqvztr",
"try_deposit_or_abort",
[bucket(bucketId)]
)
)
.build();
console.log(manifest.toString());
The TransactionBuilder
class allows clients to construct transactions in a simple way where the transaction builder takes care of the construction of the various transaction structures needed for a complete transaction. Additionally, this class exposes an interface that allows for easy signing of the transaction through a private key or through any object that implements the Signer
interface.
Thee following are examples of the construction of transactions in the cases when the private key is available to load into memory and other cases where the private key might only be available in some hardware device and can not be accessed.
This approach is ideal to use when you're still in your early prototyping phases and experimenting with the library and have access to your private key and can load it into memory. For this, the PrivateKey
class provides the abstractions that you might require. This class has two children classes: PrivateKey.Secp256k1
and PrivateKey.Ed25519
. Additionally, this class implements the Signer
interface, meaning that all objects that accept a Signer
also accept PrivateKey
.
import {
Convert,
ManifestBuilder,
NetworkId,
PrivateKey,
RadixEngineToolkit,
TransactionBuilder,
TransactionHeader,
address,
bucket,
decimal,
defaultValidationConfig,
generateRandomNonce,
} from "@radixdlt/radix-engine-toolkit";
// For this transaction, we wish to have multiple signers (and of course, a single notary). So, we
// define their cryptographic private keys.
const notaryPrivateKey = new PrivateKey.Secp256k1(
"40c1b9deccc56c0da69821dd652782887b5d31fe6bf6ead519a23f9e9472b49b"
);
const signer1PrivateKey = new PrivateKey.Ed25519(
"69366e446ad19a7540b4272c614bbc2b242656815eb03b1d29a53c950201ae76"
);
const signer2PrivateKey = new PrivateKey.Secp256k1(
"5068952ca5aa655fe9257bf2d89f3b86f4dda6be6f5b76e4ed104c38fd21e8d7"
);
// We first begin by creating the transaction header that will be used for the transaction.
const transactionHeader: TransactionHeader = {
networkId:
NetworkId.Simulator /* The network that this transaction is destined to */,
startEpochInclusive: 3910 /* The start epoch (inclusive) of when this transaction becomes valid */,
endEpochExclusive: 3920 /* The end epoch (exclusive) of when this transaction is no longer valid */,
nonce: generateRandomNonce() /* A random nonce */,
notaryPublicKey:
notaryPrivateKey.publicKey() /* The public key of the notary */,
notaryIsSignatory:
true /* Whether the notary signature is also considered as an intent signature */,
tipPercentage: 0 /* The percentage of fees that goes to validators */,
};
// We then build the transaction manifest
const transactionManifest = new ManifestBuilder()
.callMethod(
"account_sim1q3cztnp4h232hsfmu0j63f7f7mz5wxhd0n0hqax6smjqznhzrp",
"withdraw",
[
address(
"resource_sim1qf7mtmy9a6eczv9km4j4ul38cfvap0zy6juuj8m8xnxqlla6pd"
),
decimal(10),
]
)
.takeAllFromWorktop(
"resource_sim1qf7mtmy9a6eczv9km4j4ul38cfvap0zy6juuj8m8xnxqlla6pd",
(builder, bucketId) =>
builder.callMethod(
"account_sim1qs5mg6tcehg95mugc9d3lpl90clnl787zmhc92cf04wqvqvztr",
"try_deposit_or_abort",
[bucket(bucketId)]
)
)
.build();
// We may now build the complete transaction through the transaction builder.
const transaction = await TransactionBuilder.new().then((builder) =>
builder
.header(transactionHeader)
.manifest(transactionManifest)
.sign(signer1PrivateKey)
.sign(signer2PrivateKey)
.notarize(notaryPrivateKey)
);
const transactionId: Uint8Array = await RadixEngineToolkit.Intent.hash(
transaction.signedIntent.intent
);
console.log(Convert.Uint8Array.toHexString(transactionId));
// Check that the transaction that we've just built is statically valid.
const staticValidity =
await RadixEngineToolkit.NotarizedTransaction.staticallyValidate(
transaction,
defaultValidationConfig(NetworkId.Simulator)
);
console.log(staticValidity);