Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

contracts/*.flat
40 changes: 0 additions & 40 deletions contracts/helloPluts.ts

This file was deleted.

3,459 changes: 2,209 additions & 1,250 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
"@chakra-ui/react": "^2.5.5",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@harmoniclabs/blockfrost-pluts": "^0.1.16",
"@harmoniclabs/buildooor": "^0.1.10",
"@harmoniclabs/plu-ts": "^0.9.0",
"@harmoniclabs/pluts-emulator": "^0.0.1-dev8",
"@harmoniclabs/uint8array-utils": "^1.0.0",
"@harmoniclabs/blockfrost-pluts": "^0.3.2",
"@harmoniclabs/buildooor": "^0.1.20",
"@harmoniclabs/pebble": "^0.1.3-dev0",
"@harmoniclabs/pluts-emulator": "^0.0.1-dev10",
"@harmoniclabs/uint8array-utils": "^1.0.4",
"@meshsdk/core": "^1.6.2",
"@meshsdk/react": "^1.6.2",
"@types/node": "18.15.11",
Expand Down
96 changes: 49 additions & 47 deletions src/offchain/commons.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { VKeyWitness, Hash32, Data, Signature, IUTxO, Value, dataFromJson, Script } from "@harmoniclabs/buildooor";
import { CborArray, CborBytes } from "@harmoniclabs/cbor";
import { UTxO, Hash32 as LedgerHash32 } from "@harmoniclabs/cardano-ledger-ts";
import { Data as LedgerData } from "@harmoniclabs/plutus-data";
import { decode } from "cbor";
import { VKey, VKeyWitness, Signature} from "@harmoniclabs/plu-ts";

/**
* Converts a hexadecimal string to a `Uint8Array` of bytes.
Expand All @@ -10,13 +12,13 @@ import { VKey, VKeyWitness, Signature} from "@harmoniclabs/plu-ts";
* @throws {Error} If the input hexadecimal string has an uneven length.
*/
export function hexToBytes(hex: string): Uint8Array {
if (hex.length % 2 !== 0) throw new Error("hexToBytes: uneven hex string length");
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; ++i) {
bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
if (hex.length % 2 !== 0) throw new Error("hexToBytes: uneven hex string length");
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; ++i) {
bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
}


/**
Expand All @@ -28,20 +30,20 @@ export function hexToBytes(hex: string): Uint8Array {
* @throws {Error} If the extracted signature is not a `Uint8Array` or does not have a length of 64 bytes.
*/
export function extractSignatureFromCbor(hex: string): Uint8Array {
const decoded = decode(hexToBytes(hex));

if (!Array.isArray(decoded)) {
throw new Error("Decoded signature is not an array");
}

const sigBytes = decoded[3];

if (!(sigBytes instanceof Uint8Array) || sigBytes.length !== 64) {
throw new Error("Invalid signature length; expected 64 bytes");
}

return sigBytes;
const decoded = decode(hexToBytes(hex));

if (!Array.isArray(decoded)) {
throw new Error("Decoded signature is not an array");
}

const sigBytes = decoded[3];

if (!(sigBytes instanceof Uint8Array) || sigBytes.length !== 64) {
throw new Error("Invalid signature length; expected 64 bytes");
}

return sigBytes;
}

/**
* Extracts the public key from a COSE key represented as a hexadecimal string.
Expand All @@ -51,13 +53,13 @@ export function extractSignatureFromCbor(hex: string): Uint8Array {
* @throws {Error} If the extracted public key is not a valid 32-byte `Uint8Array`.
*/
export function extractPubKeyFromCoseKey(hex: string): Uint8Array {
const decoded = decode(hexToBytes(hex));
const pubKeyBytes = decoded.get(-2);
if (!(pubKeyBytes instanceof Uint8Array) || pubKeyBytes.length !== 32) {
throw new Error("Invalid public key extracted from COSE");
}
return pubKeyBytes;
const decoded = decode(hexToBytes(hex));
const pubKeyBytes = decoded.get(-2);
if (!(pubKeyBytes instanceof Uint8Array) || pubKeyBytes.length !== 32) {
throw new Error("Invalid public key extracted from COSE");
}
return pubKeyBytes;
}


/**
Expand All @@ -68,26 +70,26 @@ export function extractPubKeyFromCoseKey(hex: string): Uint8Array {
* @returns A `VKeyWitness` object containing the extracted public key and signature.
*/
export function vkeyWitnessFromSignData(key: string, signature: string): VKeyWitness {
const pubKeyBytes = extractPubKeyFromCoseKey(key);
const sigBytes = extractSignatureFromCbor(signature);
const pubKeyBytes = extractPubKeyFromCoseKey(key);
const sigBytes = extractSignatureFromCbor(signature);

return new VKeyWitness(
new VKey(pubKeyBytes),
new Signature(sigBytes)
);
}
return new VKeyWitness({
vkey: new Hash32(pubKeyBytes),
signature: new Signature(sigBytes)
});
}

/**
* Converts a wallet.signData signature into a CBOR vkey_witness
* to be used in a transaction witness set.
* TODO: Move to someplace else, or maybe it already exists in a library?
* @param key - hex-encoded public key (from `signData`)
* @param signature - hex-encoded Ed25519 signature (from `signData`)
* @returns a CBOR array representing a single `vkey_witness`
*/
export function witnessFromSignData(key: string, signature: string): CborArray {
return new CborArray([
new CborBytes(hexToBytes(key)), // vkey
new CborBytes(hexToBytes(signature)) // signature
]);
}
/**
* Converts a wallet.signData signature into a CBOR vkey_witness
* to be used in a transaction witness set.
* TODO: Move to someplace else, or maybe it already exists in a library?
* @param key - hex-encoded public key (from `signData`)
* @param signature - hex-encoded Ed25519 signature (from `signData`)
* @returns a CBOR array representing a single `vkey_witness`
*/
export function witnessFromSignData(key: string, signature: string): CborArray {
return new CborArray([
new CborBytes(hexToBytes(key)), // vkey
new CborBytes(hexToBytes(signature)) // signature
]);
}
2 changes: 1 addition & 1 deletion src/offchain/getTxBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TxBuilder, defaultProtocolParameters, toCostModelArrV3 } from "@harmoniclabs/plu-ts";
import { TxBuilder, defaultProtocolParameters } from "@harmoniclabs/buildooor";
import { BlockfrostPluts } from "@harmoniclabs/blockfrost-pluts";
import { Emulator } from "@harmoniclabs/pluts-emulator";

Expand Down
34 changes: 22 additions & 12 deletions src/offchain/lockTx.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Value, DataB, Address, Tx, forceTxOutRefStr } from "@harmoniclabs/plu-ts";
import { Value, DataB, Address, Tx, UTxO } from "@harmoniclabs/buildooor";
import { BlockfrostPluts } from "@harmoniclabs/blockfrost-pluts";
import { BrowserWallet, IWallet, UTxO } from "@meshsdk/core";
import { scriptTestnetAddr } from "../../contracts/helloPluts";
import { toPlutsUtxo } from "./mesh-utils";
import getTxBuilder from "./getTxBuilder";
import { BrowserWallet, IWallet } from "@meshsdk/core";
import { Emulator } from "@harmoniclabs/pluts-emulator";

import { vkeyWitnessFromSignData } from "./commons";
import { loadContract } from "../onchain/contract";
import getTxBuilder from "./getTxBuilder";

export async function getLockTx(wallet: IWallet | BrowserWallet, provider: BlockfrostPluts | Emulator, isEmulator: boolean): Promise<Tx> {
// creates an address form the bech32 form
Expand All @@ -15,7 +15,7 @@ export async function getLockTx(wallet: IWallet | BrowserWallet, provider: Block

const txBuilder = await getTxBuilder(provider);

const utxos = await provider.getUtxos(myAddr);
const utxos = await provider.getUtxos(myAddr.toString());
if (utxos.length === 0) {
throw new Error(isEmulator ? "No UTxOs have been found at this address on the emulated ledger" : "Have you requested funds from the faucet?");
}
Expand All @@ -25,10 +25,12 @@ export async function getLockTx(wallet: IWallet | BrowserWallet, provider: Block
throw new Error("not enough ada");
}

const { testnetAddress } = await loadContract();

return txBuilder.buildSync({
inputs: [{ utxo }],
inputs: [{ utxo: utxo as UTxO }],
outputs: [{ // output holding the founds that we'll spend later
address: scriptTestnetAddr,
address: testnetAddress,
// 10M lovelaces === 10 ADA
value: Value.lovelaces(10_000_000),
// remember to include a datum
Expand All @@ -49,21 +51,29 @@ export async function lockTx(wallet: IWallet | BrowserWallet, provider: Emulator
}

const myAddr = Address.fromString(await wallet.getChangeAddress());

console.log("About to get lock tx");
const unsignedTx = await getLockTx(wallet, provider, isEmulator);
console.log("Unsigned Tx:", unsignedTx.toJson());

// Sign the tx body hash
const txHashHex = unsignedTx.body.hash.toString();

console.log("Unsigned");
console.log(unsignedTx.body.hash.toString());
console.log(unsignedTx.body.toCbor().toString());
console.log(unsignedTx.toJson());

// Build the witness set data
const {key, signature} = await wallet.signData(txHashHex, myAddr.toString());
const witness = vkeyWitnessFromSignData(key, signature);

// inject it to the unsigned tx
unsignedTx.addVKeyWitness(witness);

const txHash = await provider.submitTx(unsignedTx);
console.log("Signed");
console.log(unsignedTx.body.hash.toString());
console.log(unsignedTx.body.toCbor().toString());
console.log(unsignedTx.toJson());

const txHash = await provider.submitTx(unsignedTx.toCbor().toString());
console.log("Transaction Hash:", txHash);

if (provider instanceof Emulator) { // emulator
Expand Down
2 changes: 1 addition & 1 deletion src/offchain/mesh-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address, Hash28, Hash32, Script, UTxO, Value, dataFromCbor } from "@harmoniclabs/plu-ts";
import { Address, Hash28, Hash32, Script, UTxO, Value, dataFromCbor } from "@harmoniclabs/buildooor";
import { fromHex } from "@harmoniclabs/uint8array-utils";
import { Asset, UTxO as MeshUTxO } from "@meshsdk/core";

Expand Down
30 changes: 20 additions & 10 deletions src/offchain/unlockTx.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { Address, isData, DataB, Tx } from "@harmoniclabs/plu-ts";
import { Address, isData, DataB, Tx, UTxO } from "@harmoniclabs/buildooor";
import { fromAscii, uint8ArrayEq } from "@harmoniclabs/uint8array-utils";
import { BlockfrostPluts } from "@harmoniclabs/blockfrost-pluts";
import { BrowserWallet, IWallet } from "@meshsdk/core";
import { script, scriptTestnetAddr } from "../../contracts/helloPluts";
import { toPlutsUtxo } from "./mesh-utils";
import getTxBuilder from "./getTxBuilder";
import { Emulator } from "@harmoniclabs/pluts-emulator";
import { vkeyWitnessFromSignData } from "./commons";
import { loadContract } from "../onchain/contract";
import getTxBuilder from "./getTxBuilder";

export async function getUnlockTx(wallet: IWallet | BrowserWallet, provider: BlockfrostPluts | Emulator, isEmulator: boolean): Promise<Tx> {
const txBuilder = await getTxBuilder(provider);
const myAddrs = (await wallet.getUsedAddresses()).map(Address.fromString);

const walletAddress = Address.fromString(await wallet.getChangeAddress());

const utxos = await provider.getUtxos(walletAddress);
const utxos = await provider.getUtxos(walletAddress.toString());
if (utxos.length === 0) {
throw new Error(isEmulator ? "No UTxOs have been found at this address on the emulated ledger" : "Have you requested funds from the faucet?");
}

let myAddr!: Address;

const { testnetAddress, script } = await loadContract();

/**
* Wallets might have multiple addresses;
*
Expand All @@ -30,7 +31,7 @@ export async function getUnlockTx(wallet: IWallet | BrowserWallet, provider: Blo
**/

// reassign utxoToSpend with only the responses with valid datum
const utxoToSpend = (await provider.addressUtxos(scriptTestnetAddr)).find(utxo => {
const utxoToSpend = (await provider.addressUtxos(testnetAddress.toString())).find(utxo => {
const datum = utxo.resolved.datum;

// datum is inline and is only bytes
Expand Down Expand Up @@ -73,7 +74,7 @@ export async function getUnlockTx(wallet: IWallet | BrowserWallet, provider: Blo
}],
requiredSigners: [myAddr.paymentCreds.hash],
// make sure to include collateral when using contracts
collaterals: [utxos[0]],
collaterals: [utxos[0] as UTxO],
// send everything back to us
changeAddress: myAddr
});
Expand All @@ -85,19 +86,28 @@ export async function unlockTx(wallet: IWallet | BrowserWallet, provider: Emulat
}

const myAddr = Address.fromString(await wallet.getChangeAddress());

console.log("About to unlock tx");
const unsignedTx = await getUnlockTx(wallet, provider, isEmulator);

// Sign the tx body hash
const txHashHex = unsignedTx.body.hash.toString();

console.log("Unsigned");
console.log(unsignedTx.body.hash.toString());
console.log(unsignedTx.body.toCbor().toString());
console.log(unsignedTx.toJson());

// Build the witness set data
const {key, signature} = await wallet.signData(txHashHex, myAddr.toString());
const witness = vkeyWitnessFromSignData(key, signature);

unsignedTx.addVKeyWitness(witness);

const txHash = await provider.submitTx(unsignedTx);
console.log("Signed");
console.log(unsignedTx.body.hash.toString());
console.log(unsignedTx.body.toCbor().toString());
console.log(unsignedTx.toJson());

const txHash = await provider.submitTx(unsignedTx.toCbor().toString());
console.log("Transaction Hash:", txHash);

if (provider instanceof Emulator) {
Expand Down
Loading