Skip to content

Instantly share code, notes, and snippets.

@pyropy
Created December 8, 2021 13:10
Show Gist options
  • Save pyropy/c25f3c331699f197c6a10767f353daca to your computer and use it in GitHub Desktop.
Save pyropy/c25f3c331699f197c6a10767f353daca to your computer and use it in GitHub Desktop.
import type * as WasmNamespace from "@emurgo/cardano-serialization-lib-browser";
import { Vkeywitnesses } from "@emurgo/cardano-serialization-lib-browser";
import { Wallet, WasmT } from "../shared/testing/types";
const MAX_VALUE_BYTES = 5000;
const MAX_TX_BYTES = 16384;
const FEE = 800_000;
const SPEND_AMOUNT = 6_666_666;
const protocolParameters = {
linearFee: {
minFeeA: "44",
minFeeB: "155381",
},
minUtxo: "34482",
poolDeposit: "500000000",
keyDeposit: "2000000",
maxValSize: 5000,
maxTxSize: 16384,
priceMem: 0.0577,
priceStep: 7.21e-5,
utxoCostPerWord: 34482,
};
export const getRedeemer = (wasm: WasmT) => {
const data = wasm.PlutusData.new_constr_plutus_data(
wasm.ConstrPlutusData.new(wasm.BigNum.from_str("42"), wasm.PlutusList.new())
);
return wasm.Redeemer.new(
wasm.RedeemerTag.new_spend(),
wasm.BigNum.from_str("0"),
data,
wasm.ExUnits.new(
wasm.BigNum.from_str("7000000"),
wasm.BigNum.from_str("3000000000")
)
);
};
const initTx = (wasm: WasmT) => {
const transactionWitnessSet = wasm.TransactionWitnessSet.new();
const redeemers = wasm.Redeemers.new();
const scripts = wasm.PlutusScripts.new();
const scriptDatums = wasm.PlutusList.new();
const auxiliaryData = wasm.AuxiliaryData.new();
const collateralInputs = wasm.TransactionInputs.new();
const inputs = wasm.TransactionInputs.new();
const outputs = wasm.TransactionOutputs.new();
const txBuilder = wasm.TransactionBuilder.new(
wasm.TransactionBuilderConfigBuilder.new()
.fee_algo(
wasm.LinearFee.new(
wasm.BigNum.from_str(protocolParameters.linearFee.minFeeA),
wasm.BigNum.from_str(protocolParameters.linearFee.minFeeB)
)
)
.pool_deposit(wasm.BigNum.from_str(protocolParameters.poolDeposit))
.key_deposit(wasm.BigNum.from_str(protocolParameters.keyDeposit))
.coins_per_utxo_word(
wasm.BigNum.from_str(protocolParameters.utxoCostPerWord.toString())
)
.max_value_size(protocolParameters.maxValSize ?? MAX_VALUE_BYTES)
.max_tx_size(protocolParameters.maxTxSize ?? MAX_TX_BYTES)
.prefer_pure_change(true)
.build()
);
return {
txBuilder,
transactionWitnessSet,
redeemers,
scripts,
inputs,
outputs,
scriptDatums,
auxiliaryData,
collateralInputs,
};
};
export const encodeCbor = (val: any): string =>
Buffer.from(val.to_bytes()).toString("hex");
export const decodeAddress =
(wasm: WasmT) =>
(encodedAddress: string): WasmNamespace.Address =>
wasm.Address.from_bytes(Buffer.from(encodedAddress, "hex"));
const decodeUtxo =
(wasm: WasmT) =>
(encodedUtxo: string): WasmNamespace.TransactionUnspentOutput =>
wasm.TransactionUnspentOutput.from_bytes(Buffer.from(encodedUtxo, "hex"));
const fromHex = (hex: string) => Buffer.from(hex, "hex");
const toHex = (str: Record<"to_bytes", Function>) =>
Buffer.from(str.to_bytes()).toString("hex");
const getScript = (wasm: WasmT, scriptCbor: string) => {
return wasm.PlutusScript.new(fromHex(scriptCbor));
};
const getDatum = (wasm: WasmT) => {
const datumVals = wasm.PlutusList.new();
datumVals.add(wasm.PlutusData.new_integer(wasm.BigInt.from_str("42")));
return wasm.PlutusData.new_constr_plutus_data(
wasm.ConstrPlutusData.new(wasm.BigNum.from_str("0"), datumVals)
);
};
const getSpendingOutput = (wasm: WasmT, address: string, amount: number) => {
return wasm.TransactionOutput.new(
wasm.Address.from_bech32(address),
wasm.Value.new(wasm.BigNum.from_str(amount.toString()))
);
};
const getChangeOutput = (wasm: WasmT, address: string, balance: number) => {
return wasm.TransactionOutput.new(
wasm.Address.from_bech32(address),
wasm.Value.new(
wasm.BigNum.from_str((balance - SPEND_AMOUNT - FEE).toString())
)
);
};
export const lockFundsOnScript =
(wasm: WasmT, wallet: Wallet) =>
async (scriptCbor: string, scriptAddress: string) => {
const {
txBuilder,
scripts,
scriptDatums,
transactionWitnessSet,
collateralInputs
redeemers, // this shall stay empty
} = initTx(wasm);
const encodedUtxos = await wallet?.getUtxos();
const encodedAddress = (await wallet?.getUsedAddresses())[0];
const decodedAddress = decodeAddress(wasm)(encodedAddress);
const decodedUtxos: WasmNamespace.TransactionUnspentOutput[] =
encodedUtxos.map(decodeUtxo(wasm));
// create collateral inputs
const collateralUtxos = (await wallet.getCollateral()).map((utxo) =>
wasm.TransactionUnspentOutput.from_bytes(Buffer.from(utxo, "hex"))
);
collateralUtxos.forEach((utxo) => {
collateralInputs.add(utxo.input());
});
// my wallet balance in lovelace
const balance = 66860068;
// add script to list of scripts
const script = getScript(wasm, scriptCbor);
scripts.add(script);
// create datum
const datum = getDatum(wasm);
scriptDatums.add(datum);
// create spending output and set datum hash
const spendingOutput = getSpendingOutput(wasm, scriptAddress, SPEND_AMOUNT);
spendingOutput.set_data_hash(wasm.hash_plutus_data(datum));
// create change output
const changeOutput = getChangeOutput(wasm, decodedAddress.to_bech32(), balance);
// add all decoded wallet utxos as input
decodedUtxos.forEach((utxo) => {
txBuilder.add_input(
utxo.output().address(),
utxo.input(),
utxo.output().amount()
);
});
// set outputs and fee
txBuilder.add_output(spendingOutput);
txBuilder.add_output(changeOutput);
txBuilder.set_fee(wasm.BigNum.from_str(FEE.toString()));
// set datum
transactionWitnessSet.set_plutus_data(scriptDatums);
// build tx body
const txBody = txBuilder.build();
// set collateral on tx body
txBody.set_collateral(collateralInputs);
// add required signatures, leave it empty
const requiredSigners = wasm.Ed25519KeyHashes.new();
txBody.set_required_signers(requiredSigners);
// create cost models for plutus
const arr = [
4, 1000, 100, 103599, 1, 621, 29175, 150000, 1000, 150000, 61516, 100,
150000, 150000, 150000, 32, 150000, 29773, 150000, 0, 150000, 0, 1, 118,
150000, 150000, 32, 136542, 82363, 5000, 150000, 179690, 150000, 0, 118,
1, 150000, 145276, 1, 32, 32, 150000, 1, 1, 0, 4, 32, 32, 150000, 32, 1,
32, 248, 0, 100, 0, 32, 118, 29773, 1, 29773, 29175, 1, 1, 1, 150000,
150000, 29773, 150000, 1, 1000, 1, 1366, 32, 0, 150000, 1, 32, 32, 197209,
8, 150000, 150000, 150000, 148000, 1, 100, 150000, 150000, 1326, 100,
197209, 425507, 0, 100, 2477736, 148000, 150000, 150000, 1000, 1, 11218,
396231, 248, 1, 0, 10000, 0, 150000, 150000, 1, 29773, 1, 3345831, 32, 32,
1, 4, 1, 32, 247, 150000, 118, 100, 1, 1, 100, 0, 2477736, 425507, 1, 32,
150000, 150000, 32, 4, 32, 32, 29773, 1, 103599, 1000, 1, 32, 148000,
29773, 8, 425507, 32, 1000, 148000, 1, 32, 0, 150000, 0, 32, 112536, 1,
497, 425507, 1, 0, 1, 100, 150000,
];
const costModel = wasm.CostModel.new();
arr.forEach((x, i) => costModel.set(i, wasm.Int.new_i32(x)));
const costModels = wasm.Costmdls.new();
costModels.insert(wasm.Language.new_plutus_v1(), costModel);
// set script data hash
txBody.set_script_data_hash(
wasm.hash_script_data(redeemers, costModels, scriptDatums)
);
// create unsigned tx
const tx = wasm.Transaction.new(
txBody,
wasm.TransactionWitnessSet.from_bytes(transactionWitnessSet.to_bytes())
);
// sign tx
const encodedTxVkeyWitnesses = await wallet.signTx(toHex(tx), true);
const txVkeyWitnesses = wasm.TransactionWitnessSet.from_bytes(
Buffer.from(encodedTxVkeyWitnesses, "hex")
);
// set vkeys to witness
transactionWitnessSet.set_vkeys(txVkeyWitnesses.vkeys() as Vkeywitnesses);
// create signed tx
const txSigned = wasm.Transaction.new(tx.body(), transactionWitnessSet);
// encode signed tx
const encodedTx = toHex(txSigned);
console.log(encodedTx)
const txHash = await wallet.submitTx(encodedTx);
console.log("Tx hash:", txHash);
return txHash;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment