Smart Contract Interactions
It is important to understand that on the Cardano blockchain, you don’t directly interact with “smart contracts”, at least not in the traditional sense.
Instead, you work with validators. These validators are responsible for verifying the actions taken in a given transaction, rather than executing or calling any actions themselves. Validators are pure functions that return true or false.
A validator checks whether the transaction meets its requirements, and if it does, the transaction is processed successfully. If the requirements are not met, the transaction fails (is not allowed to execute).
Concepts
Datums
Datums are data attached to UTxOs which are accessible on-chain (like state variables).
const datum = Data.to(new Constr(0, [pubKeyHash]));
You can define datum schemas for type safety:
const DatumSchema = Data.Object({
owner: Data.Bytes(),
});
type DatumType = Data.Static<typeof DatumSchema>;
const DatumType = DatumSchema as unknown as DatumType;
Redeemers
Redeemers are arguments provided from the off-chain when spending UTxOs associated with a script address.
const redeemer = Data.to(new Constr(0, [fromText("Hello, World!")]));
When working with string
, a greater care is needed:
You must provide the hex value in order for it to be serializable into CBOR, that’s why "Hello, World!"
is converted using fromText()
. But if the underlying string is already in hex, then you do NOT call fromText()
as it will treat the input string as a regular string and the result would be doubly hex.
See Datums
example: pubKeyHash
is a hex string, so calling fromText(pubKeyHash)
would be incorrect.
Workflow
Create a Validator Instance
Evolution-SDK consumes compiled validators from languages like PlutusTx, Aiken, or Plutarch:
import { SpendingValidator } from "@evolution-sdk/lucid";
const spendingValidator: SpendingValidator = {
type: "PlutusV3",
script: "59099a590997010000...", // CBOR format from plutus.json
};
const scriptAddress = lucid.utils.validatorToAddress(spendingValidator);
Lock Funds at Script Address
const tx = await lucid
.newTx()
.pay.ToContract(
scriptAddress,
{ kind: "inline", value: datum },
{ lovelace: 10_000_000n }
)
.complete();
.pay.ToContract()
and .pay.ToAddressWithData()
are functionally identical. .pay.ToContract()
is just an alias that better expresses the intent when working with smart contracts.
Spend (redeem) from Script Address
// Find the UTxO we want to spend
const allUTxOs = await lucid.utxosAt(scriptAddress);
const ownerUTxO = allUTxOs.find((utxo) => {
if (utxo.datum) {
const datum = Data.from(utxo.datum, DatumType);
return datum.owner === publicKeyHash;
}
});
// Spend script UTxO
const tx = await lucid
.newTx()
.collectFrom([ownerUTxO], redeemer) // Provide the redeemer argument
.attach.SpendingValidator(spend_val) // Attach validator
.complete();
Validator Types
Evolution-SDK supports various validator types, each serving a specific purpose.
// Attaching different validator types
.attach.SpendingValidator(validator) // For spending UTxOs
.attach.MintingPolicy(validator) // For minting tokens
.attach.CertificateValidator(validator) // For stake operations
.attach.WithdrawalValidator(validator) // For reward withdrawals
.attach.VoteValidator(validator) // For governance votes
.attach.ProposeValidator(validator) // For governance proposals
The corresponding transaction operations:
.collectFrom(utxos, redeemer) // Spending UTxOs
.mintAssets(assets, redeemer) // Minting tokens
.delegateTo(stakeAddress, poolId, redeemer) // Stake delegation
.deRegisterStake(stakeAddress, redeemer) // Stake deregistration
.withdraw(stakeAddress, rewardAmount, redeemer) // Reward withdrawal
Troubleshooting
Common Issue | Best Practice |
---|---|
Datum/Redeemer Format | Ensure they match validator expectations precisely |
Minimum ADA | Have enough ADA as inputs for all outputs |
Collateral | Include appropriate amount of collateral when spending from script addresses |
Signatories | Include all required signers for the validators that require/check them |
Tx Size and Ex Units | Monitor the limits when working with multiple validators |
Reference Scripts | Use reference scripts whenever possible to reduce transaction size and costs |
For a comprehensive documentation of validators, see the official Aiken documentation .