diff --git a/src/translucent/translucent.ts b/src/translucent/translucent.ts
index de9c58d..6cbaf58 100644
--- a/src/translucent/translucent.ts
+++ b/src/translucent/translucent.ts
@@ -1,11 +1,9 @@
import { C } from "../core/mod.ts";
import {
- coreToUtxo,
createCostModels,
fromHex,
fromUnit,
paymentCredentialOf,
- toHex,
toUnit,
Utils,
utxoToCore,
@@ -14,7 +12,6 @@ import {
Address,
Credential,
Delegation,
- ExternalWallet,
Json,
KeyHash,
Network,
@@ -29,7 +26,6 @@ import {
TxHash,
Unit,
UTxO,
- Wallet,
WalletApi,
} from "../types/mod.ts";
import { Tx } from "./tx.ts";
@@ -40,10 +36,16 @@ import { Message } from "./message.ts";
import { SLOT_CONFIG_NETWORK } from "../plutus/time.ts";
import { Constr, Data } from "../plutus/data.ts";
import { Emulator } from "../provider/emulator.ts";
+import { toCore } from "../utils/to.ts";
+import { WalletConnector } from "../wallets/wallet_connector.ts";
+import { AbstractWallet } from "../wallets/abstract.ts";
+import { PrivateKeyWallet } from "../wallets/private_key.ts";
+import { SeedWallet } from "../wallets/seed.ts";
+import { ExternalWallet } from "../wallets/public_wallet.ts";
export class Translucent {
txBuilderConfig!: C.TransactionBuilderConfig;
- wallet!: Wallet;
+ wallet!: AbstractWallet;
provider!: Provider;
network: Network = "Mainnet";
utils!: Utils;
@@ -214,226 +216,24 @@ export class Translucent {
}
}
- /**
- * Cardano Private key in bech32; not the BIP32 private key or any key that is not fully derived.
- * Only an Enteprise address (without stake credential) is derived.
- */
selectWalletFromPrivateKey(privateKey: PrivateKey): Translucent {
- const priv = C.PrivateKey.from_bech32(privateKey);
- const pubKeyHash = priv.to_public().hash();
-
- this.wallet = {
- // deno-lint-ignore require-await
- address: async (): Promise
=>
- C.EnterpriseAddress.new(
- this.network === "Mainnet" ? 1 : 0,
- C.StakeCredential.from_keyhash(pubKeyHash),
- )
- .to_address()
- .to_bech32(undefined),
- // deno-lint-ignore require-await
- rewardAddress: async (): Promise => null,
- getUtxos: async (): Promise => {
- return await this.utxosAt(
- paymentCredentialOf(await this.wallet.address()),
- );
- },
- getUtxosCore: async (): Promise => {
- const utxos = await this.utxosAt(
- paymentCredentialOf(await this.wallet.address()),
- );
- const coreUtxos = C.TransactionUnspentOutputs.new();
- utxos.forEach((utxo) => {
- coreUtxos.add(utxoToCore(utxo));
- });
- return coreUtxos;
- },
- // deno-lint-ignore require-await
- getDelegation: async (): Promise => {
- return { poolId: null, rewards: 0n };
- },
- // deno-lint-ignore require-await
- signTx: async (tx: C.Transaction): Promise => {
- const witness = C.make_vkey_witness(
- C.hash_transaction(tx.body()),
- priv,
- );
- const txWitnessSetBuilder = C.TransactionWitnessSetBuilder.new();
- txWitnessSetBuilder.add_vkey(witness);
- return txWitnessSetBuilder.build();
- },
- // deno-lint-ignore require-await
- signMessage: async (
- address: Address | RewardAddress,
- payload: Payload,
- ): Promise => {
- const {
- paymentCredential,
- address: { hex: hexAddress },
- } = this.utils.getAddressDetails(address);
- const keyHash = paymentCredential?.hash;
-
- const originalKeyHash = pubKeyHash.to_hex();
-
- if (!keyHash || keyHash !== originalKeyHash) {
- throw new Error(`Cannot sign message for address: ${address}.`);
- }
-
- return signData(hexAddress, payload, privateKey);
- },
- submitTx: async (tx: Transaction): Promise => {
- return await this.provider.submitTx(tx);
- },
- };
- return this;
+ return this.useWallet(new PrivateKeyWallet(this, privateKey));
}
selectWallet(api: WalletApi): Translucent {
- const getAddressHex = async () => {
- const [addressHex] = await api.getUsedAddresses();
- if (addressHex) return addressHex;
-
- const [unusedAddressHex] = await api.getUnusedAddresses();
- return unusedAddressHex;
- };
-
- this.wallet = {
- address: async (): Promise =>
- C.Address.from_bytes(fromHex(await getAddressHex())).to_bech32(
- undefined,
- ),
- rewardAddress: async (): Promise => {
- const [rewardAddressHex] = await api.getRewardAddresses();
- const rewardAddress = rewardAddressHex
- ? C.RewardAddress.from_address(
- C.Address.from_bytes(fromHex(rewardAddressHex)),
- )!
- .to_address()
- .to_bech32(undefined)
- : null;
- return rewardAddress;
- },
- getUtxos: async (): Promise => {
- const utxos = ((await api.getUtxos()) || []).map((utxo) => {
- const parsedUtxo = C.TransactionUnspentOutput.from_bytes(
- fromHex(utxo),
- );
- return coreToUtxo(parsedUtxo);
- });
- return utxos;
- },
- getUtxosCore: async (): Promise => {
- const utxos = C.TransactionUnspentOutputs.new();
- ((await api.getUtxos()) || []).forEach((utxo) => {
- utxos.add(C.TransactionUnspentOutput.from_bytes(fromHex(utxo)));
- });
- return utxos;
- },
- getDelegation: async (): Promise => {
- const rewardAddr = await this.wallet.rewardAddress();
-
- return rewardAddr
- ? await this.delegationAt(rewardAddr)
- : { poolId: null, rewards: 0n };
- },
- signTx: async (tx: C.Transaction): Promise => {
- const witnessSet = await api.signTx(toHex(tx.to_bytes()), true);
- return C.TransactionWitnessSet.from_bytes(fromHex(witnessSet));
- },
- signMessage: async (
- address: Address | RewardAddress,
- payload: Payload,
- ): Promise => {
- const hexAddress = toHex(C.Address.from_bech32(address).to_bytes());
- return await api.signData(hexAddress, payload);
- },
- submitTx: async (tx: Transaction): Promise => {
- const txHash = await api.submitTx(tx);
- return txHash;
- },
- };
- return this;
+ return this.useWallet(new WalletConnector(this, api));
}
- /**
- * Emulates a wallet by constructing it with the utxos and an address.
- * If utxos are not set, utxos are fetched from the provided address.
- */
- selectWalletFrom({
- address,
- utxos,
- rewardAddress,
- }: ExternalWallet): Translucent {
- const addressDetails = this.utils.getAddressDetails(address);
- this.wallet = {
- // deno-lint-ignore require-await
- address: async (): Promise => address,
- // deno-lint-ignore require-await
- rewardAddress: async (): Promise => {
- const rewardAddr =
- !rewardAddress && addressDetails.stakeCredential
- ? (() => {
- if (addressDetails.stakeCredential.type === "Key") {
- return C.RewardAddress.new(
- this.network === "Mainnet" ? 1 : 0,
- C.StakeCredential.from_keyhash(
- C.Ed25519KeyHash.from_hex(
- addressDetails.stakeCredential.hash,
- ),
- ),
- )
- .to_address()
- .to_bech32(undefined);
- }
- return C.RewardAddress.new(
- this.network === "Mainnet" ? 1 : 0,
- C.StakeCredential.from_scripthash(
- C.ScriptHash.from_hex(addressDetails.stakeCredential.hash),
- ),
- )
- .to_address()
- .to_bech32(undefined);
- })()
- : rewardAddress;
- return rewardAddr || null;
- },
- getUtxos: async (): Promise => {
- return utxos ? utxos : await this.utxosAt(paymentCredentialOf(address));
- },
- getUtxosCore: async (): Promise => {
- const coreUtxos = C.TransactionUnspentOutputs.new();
- (utxos
- ? utxos
- : await this.utxosAt(paymentCredentialOf(address))
- ).forEach((utxo) => coreUtxos.add(utxoToCore(utxo)));
- return coreUtxos;
- },
- getDelegation: async (): Promise => {
- const rewardAddr = await this.wallet.rewardAddress();
-
- return rewardAddr
- ? await this.delegationAt(rewardAddr)
- : { poolId: null, rewards: 0n };
- },
- // deno-lint-ignore require-await
- signTx: async (): Promise => {
- throw new Error("Not implemented");
- },
- // deno-lint-ignore require-await
- signMessage: async (): Promise => {
- throw new Error("Not implemented");
- },
- submitTx: async (tx: Transaction): Promise => {
- return await this.provider.submitTx(tx);
- },
- };
- return this;
+ selectWalletFrom(
+ address: Address,
+ utxos?: UTxO[],
+ rewardAddress?: RewardAddress,
+ ): Translucent {
+ return this.useWallet(
+ new ExternalWallet(this, address, utxos, rewardAddress),
+ );
}
- /**
- * Select wallet from a seed phrase (e.g. 15 or 24 words). You have the option to choose between a Base address (with stake credential)
- * and Enterprise address (without stake credential). You can also decide which account index to derive. By default account 0 is derived.
- */
selectWalletFromSeed(
seed: string,
options?: {
@@ -442,98 +242,11 @@ export class Translucent {
password?: string;
},
): Translucent {
- const { address, rewardAddress, paymentKey, stakeKey } = walletFromSeed(
- seed,
- {
- addressType: options?.addressType || "Base",
- accountIndex: options?.accountIndex || 0,
- password: options?.password,
- network: this.network,
- },
- );
-
- const paymentKeyHash = C.PrivateKey.from_bech32(paymentKey)
- .to_public()
- .hash()
- .to_hex();
- const stakeKeyHash = stakeKey
- ? C.PrivateKey.from_bech32(stakeKey).to_public().hash().to_hex()
- : "";
-
- const privKeyHashMap = {
- [paymentKeyHash]: paymentKey,
- [stakeKeyHash]: stakeKey,
- };
-
- this.wallet = {
- // deno-lint-ignore require-await
- address: async (): Promise => address,
- // deno-lint-ignore require-await
- rewardAddress: async (): Promise =>
- rewardAddress || null,
- // deno-lint-ignore require-await
- getUtxos: async (): Promise =>
- this.utxosAt(paymentCredentialOf(address)),
- getUtxosCore: async (): Promise => {
- const coreUtxos = C.TransactionUnspentOutputs.new();
- (await this.utxosAt(paymentCredentialOf(address))).forEach((utxo) =>
- coreUtxos.add(utxoToCore(utxo)),
- );
- return coreUtxos;
- },
- getDelegation: async (): Promise => {
- const rewardAddr = await this.wallet.rewardAddress();
-
- return rewardAddr
- ? await this.delegationAt(rewardAddr)
- : { poolId: null, rewards: 0n };
- },
- signTx: async (tx: C.Transaction): Promise => {
- const utxos = await this.utxosAt(address);
-
- const ownKeyHashes: Array = [paymentKeyHash, stakeKeyHash];
-
- const usedKeyHashes = discoverOwnUsedTxKeyHashes(
- tx,
- ownKeyHashes,
- utxos,
- );
-
- const txWitnessSetBuilder = C.TransactionWitnessSetBuilder.new();
- usedKeyHashes.forEach((keyHash) => {
- const witness = C.make_vkey_witness(
- C.hash_transaction(tx.body()),
- C.PrivateKey.from_bech32(privKeyHashMap[keyHash]!),
- );
- txWitnessSetBuilder.add_vkey(witness);
- });
- return txWitnessSetBuilder.build();
- },
- // deno-lint-ignore require-await
- signMessage: async (
- address: Address | RewardAddress,
- payload: Payload,
- ): Promise => {
- const {
- paymentCredential,
- stakeCredential,
- address: { hex: hexAddress },
- } = this.utils.getAddressDetails(address);
-
- const keyHash = paymentCredential?.hash || stakeCredential?.hash;
-
- const privateKey = privKeyHashMap[keyHash!];
-
- if (!privateKey) {
- throw new Error(`Cannot sign message for address: ${address}.`);
- }
+ return this.useWallet(new SeedWallet(this, seed, options));
+ }
- return signData(hexAddress, payload, privateKey);
- },
- submitTx: async (tx: Transaction): Promise => {
- return await this.provider.submitTx(tx);
- },
- };
+ useWallet(wallet: AbstractWallet) {
+ this.wallet = wallet;
return this;
}
}
diff --git a/src/translucent/tx.ts b/src/translucent/tx.ts
index bb594f1..948fe87 100644
--- a/src/translucent/tx.ts
+++ b/src/translucent/tx.ts
@@ -36,6 +36,7 @@ import { applyDoubleCborEncoding } from "../utils/utils.ts";
import { Translucent } from "./translucent.ts";
import { TxComplete } from "./tx_complete.ts";
import { SLOT_CONFIG_NETWORK } from "../plutus/time.ts";
+import { toCore } from "../utils/to.ts";
type ScriptOrRef =
| { inlineScript: C.PlutusScript }
@@ -365,18 +366,7 @@ export class Tx {
if (addressDetails.type !== "Reward" || !addressDetails.stakeCredential) {
throw new Error("Not a reward address provided.");
}
- const credential =
- addressDetails.stakeCredential.type === "Key"
- ? C.StakeCredential.from_keyhash(
- C.Ed25519KeyHash.from_bytes(
- fromHex(addressDetails.stakeCredential.hash),
- ),
- )
- : C.StakeCredential.from_scripthash(
- C.ScriptHash.from_bytes(
- fromHex(addressDetails.stakeCredential.hash),
- ),
- );
+ const credential = toCore.credential(addressDetails.stakeCredential);
let certBuilder = C.SingleCertificateBuilder.new(
C.Certificate.new_stake_delegation(
@@ -439,18 +429,7 @@ export class Tx {
if (addressDetails.type !== "Reward" || !addressDetails.stakeCredential) {
throw new Error("Not a reward address provided.");
}
- const credential =
- addressDetails.stakeCredential.type === "Key"
- ? C.StakeCredential.from_keyhash(
- C.Ed25519KeyHash.from_bytes(
- fromHex(addressDetails.stakeCredential.hash),
- ),
- )
- : C.StakeCredential.from_scripthash(
- C.ScriptHash.from_bytes(
- fromHex(addressDetails.stakeCredential.hash),
- ),
- );
+ const credential = toCore.credential(addressDetails.stakeCredential);
that.txBuilder.add_cert(
C.SingleCertificateBuilder.new(
diff --git a/src/types/types.ts b/src/types/types.ts
index fb924b7..8547fdf 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -159,32 +159,8 @@ export type Delegation = {
rewards: Lovelace;
};
-/**
- * A wallet that can be constructed from external data e.g utxos and an address.
- * It doesn't allow you to sign transactions/messages. This needs to be handled separately.
- */
-export interface ExternalWallet {
- address: Address;
- utxos?: UTxO[];
- rewardAddress?: RewardAddress;
-}
-
export type SignedMessage = { signature: string; key: string };
-export interface Wallet {
- address(): Promise;
- rewardAddress(): Promise;
- getUtxos(): Promise;
- getUtxosCore(): Promise;
- getDelegation(): Promise;
- signTx(tx: C.Transaction): Promise;
- signMessage(
- address: Address | RewardAddress,
- payload: Payload,
- ): Promise;
- submitTx(signedTx: Transaction): Promise;
-}
-
/** JSON object */
// deno-lint-ignore no-explicit-any
export type Json = any;
diff --git a/src/utils/to.ts b/src/utils/to.ts
new file mode 100644
index 0000000..ca0ac7c
--- /dev/null
+++ b/src/utils/to.ts
@@ -0,0 +1,17 @@
+import * as C from "@dcspark/cardano-multiplatform-lib-nodejs";
+import { Credential } from "../mod";
+
+export const toCore = {
+ credential(credential: Credential): C.StakeCredential {
+ if (credential.type == "Key") {
+ return C.StakeCredential.from_keyhash(
+ C.Ed25519KeyHash.from_hex(credential.hash),
+ );
+ } else if (credential.type == "Script") {
+ return C.StakeCredential.from_scripthash(
+ C.ScriptHash.from_hex(credential.hash),
+ );
+ }
+ throw new Error("Lucid credential type mismatch");
+ },
+};
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 27d3e3e..ec9eff3 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -35,6 +35,8 @@ import {
unixTimeToEnclosingSlot,
} from "../plutus/time.ts";
import { Data } from "../plutus/data.ts";
+import { toCore } from "./to";
+
export class Utils {
private translucent: Translucent;
constructor(translucent: Translucent) {
@@ -50,13 +52,7 @@ export class Utils {
return C.BaseAddress.new(
networkToId(this.translucent.network),
C.StakeCredential.from_scripthash(C.ScriptHash.from_hex(validatorHash)),
- stakeCredential.type === "Key"
- ? C.StakeCredential.from_keyhash(
- C.Ed25519KeyHash.from_hex(stakeCredential.hash),
- )
- : C.StakeCredential.from_scripthash(
- C.ScriptHash.from_hex(stakeCredential.hash),
- ),
+ toCore.credential(stakeCredential),
)
.to_address()
.to_bech32(undefined);
diff --git a/src/wallets/abstract.ts b/src/wallets/abstract.ts
new file mode 100644
index 0000000..8de38e3
--- /dev/null
+++ b/src/wallets/abstract.ts
@@ -0,0 +1,25 @@
+import {
+ UTxO,
+ C,
+ Delegation,
+ Payload,
+ TxHash,
+ Address,
+ RewardAddress,
+ Transaction,
+ SignedMessage,
+} from "../mod";
+
+export abstract class AbstractWallet {
+ abstract address(): Promise;
+ abstract rewardAddress(): Promise;
+ abstract getUtxos(): Promise;
+ abstract getUtxosCore(): Promise;
+ abstract getDelegation(): Promise;
+ abstract signTx(tx: C.Transaction): Promise;
+ abstract signMessage(
+ address: Address | RewardAddress,
+ payload: Payload,
+ ): Promise;
+ abstract submitTx(signedTx: Transaction): Promise;
+}
diff --git a/src/wallets/private_key.ts b/src/wallets/private_key.ts
new file mode 100644
index 0000000..2b42428
--- /dev/null
+++ b/src/wallets/private_key.ts
@@ -0,0 +1,97 @@
+import { signData } from "../misc/sign_data";
+import {
+ SignedMessage,
+ PrivateKey,
+ Address,
+ RewardAddress,
+ Transaction,
+ Translucent,
+ WalletApi,
+ C,
+ UTxO,
+ paymentCredentialOf,
+ utxoToCore,
+ Delegation,
+ Payload,
+ TxHash,
+} from "../mod";
+import { AbstractWallet } from "./abstract";
+
+/**
+ * Cardano Private key in bech32; not the BIP32 private key or any key that is not fully derived.
+ * Only an Enteprise address (without stake credential) is derived.
+ */
+export class PrivateKeyWallet implements AbstractWallet {
+ translucent: Translucent;
+ private privateKey: PrivateKey;
+ private priv: C.PrivateKey;
+ pubKeyHash: C.Ed25519KeyHash;
+
+ constructor(translucent: Translucent, privateKey: PrivateKey) {
+ this.translucent = translucent;
+ this.privateKey = privateKey;
+ this.priv = C.PrivateKey.from_bech32(privateKey);
+ this.pubKeyHash = this.priv.to_public().hash();
+ }
+
+ async address(): Promise {
+ return C.EnterpriseAddress.new(
+ this.translucent.network === "Mainnet" ? 1 : 0,
+ C.StakeCredential.from_keyhash(this.pubKeyHash),
+ )
+ .to_address()
+ .to_bech32(undefined);
+ }
+ // deno-lint-ignore require-await
+ async rewardAddress(): Promise {
+ return null;
+ }
+ async getUtxos(): Promise {
+ return await this.translucent.utxosAt(
+ paymentCredentialOf(await this.address()),
+ );
+ }
+ async getUtxosCore(): Promise {
+ const utxos = await this.translucent.utxosAt(
+ paymentCredentialOf(await this.address()),
+ );
+ const coreUtxos = C.TransactionUnspentOutputs.new();
+ utxos.forEach((utxo) => {
+ coreUtxos.add(utxoToCore(utxo));
+ });
+ return coreUtxos;
+ }
+ // deno-lint-ignore require-await
+ async getDelegation(): Promise {
+ return { poolId: null, rewards: 0n };
+ }
+ // deno-lint-ignore require-await
+ async signTx(tx: C.Transaction): Promise {
+ const witness = C.make_vkey_witness(
+ C.hash_transaction(tx.body()),
+ this.priv,
+ );
+ const txWitnessSetBuilder = C.TransactionWitnessSetBuilder.new();
+ txWitnessSetBuilder.add_vkey(witness);
+ return txWitnessSetBuilder.build();
+ }
+ // deno-lint-ignore require-await
+ async signMessage(
+ address: Address | RewardAddress,
+ payload: Payload,
+ ): Promise {
+ const {
+ paymentCredential,
+ address: { hex: hexAddress },
+ } = this.translucent.utils.getAddressDetails(address);
+ const keyHash = paymentCredential?.hash;
+ const originalKeyHash = this.pubKeyHash.to_hex();
+ if (!keyHash || keyHash !== originalKeyHash) {
+ throw new Error(`Cannot sign message for address: ${address}.`);
+ }
+ return signData(hexAddress, payload, this.privateKey);
+ }
+ async submitTx(tx: Transaction): Promise {
+ return await this.translucent.provider.submitTx(tx);
+ }
+}
diff --git a/src/wallets/public_wallet.ts b/src/wallets/public_wallet.ts
new file mode 100644
index 0000000..9a79ab3
--- /dev/null
+++ b/src/wallets/public_wallet.ts
@@ -0,0 +1,96 @@
+import {
+ C,
+ UTxO,
+ paymentCredentialOf,
+ utxoToCore,
+ Delegation,
+ TxHash,
+ Address,
+ RewardAddress,
+ Transaction,
+ SignedMessage,
+ Translucent,
+ AddressDetails,
+} from "../mod";
+import { toCore } from "../utils/to";
+import { AbstractWallet } from "./abstract";
+
+/**
+ * A wallet that can be constructed from external data e.g utxos and an address.
+ * It doesn't allow you to sign transactions/messages. This needs to be handled separately.
+ */
+export class ExternalWallet implements AbstractWallet {
+ translucent: Translucent;
+ walletDetails: {
+ address: Address;
+ utxos?: UTxO[];
+ rewardAddress?: RewardAddress;
+ };
+ addressDetails: AddressDetails;
+
+ constructor(
+ translucent: Translucent,
+ address: Address,
+ utxos?: UTxO[],
+ rewardAddress?: RewardAddress,
+ ) {
+ this.translucent = translucent;
+ this.walletDetails = {
+ address,
+ utxos,
+ rewardAddress,
+ };
+ const addressDetails = this.translucent.utils.getAddressDetails(address);
+ this.addressDetails = addressDetails;
+ }
+
+ async address(): Promise {
+ return this.walletDetails.address;
+ }
+ async rewardAddress(): Promise {
+ const rewardAddr =
+ !this.walletDetails.rewardAddress && this.addressDetails.stakeCredential
+ ? (() =>
+ C.RewardAddress.new(
+ this.translucent.network === "Mainnet" ? 1 : 0,
+ toCore.credential(this.addressDetails.stakeCredential),
+ )
+ .to_address()
+ .to_bech32(undefined))()
+ : this.walletDetails.rewardAddress;
+ return rewardAddr || null;
+ }
+ async getUtxos(): Promise {
+ return this.walletDetails.utxos
+ ? this.walletDetails.utxos
+ : await this.translucent.utxosAt(
+ paymentCredentialOf(this.walletDetails.address),
+ );
+ }
+ async getUtxosCore(): Promise {
+ const coreUtxos = C.TransactionUnspentOutputs.new();
+ (this.walletDetails.utxos
+ ? this.walletDetails.utxos
+ : await this.translucent.utxosAt(
+ paymentCredentialOf(this.walletDetails.address),
+ )
+ ).forEach((utxo) => coreUtxos.add(utxoToCore(utxo)));
+ return coreUtxos;
+ }
+ async getDelegation(): Promise {
+ const rewardAddr = await this.rewardAddress();
+
+ return rewardAddr
+ ? await this.translucent.delegationAt(rewardAddr)
+ : { poolId: null, rewards: 0n };
+ }
+ async signTx(): Promise {
+ throw new Error("Not implemented");
+ }
+ async signMessage(): Promise {
+ throw new Error("Not implemented");
+ }
+ async submitTx(tx: Transaction): Promise {
+ return await this.translucent.provider.submitTx(tx);
+ }
+}
diff --git a/src/wallets/seed.ts b/src/wallets/seed.ts
new file mode 100644
index 0000000..265f661
--- /dev/null
+++ b/src/wallets/seed.ts
@@ -0,0 +1,136 @@
+import { signData } from "../misc/sign_data";
+import { discoverOwnUsedTxKeyHashes, walletFromSeed } from "../misc/wallet";
+import {
+ Address,
+ C,
+ Delegation,
+ KeyHash,
+ Payload,
+ RewardAddress,
+ SignedMessage,
+ Transaction,
+ Translucent,
+ TxHash,
+ UTxO,
+ paymentCredentialOf,
+ utxoToCore,
+} from "../mod";
+import { AbstractWallet } from "./abstract";
+
+/**
+ * Select wallet from a seed phrase (e.g. 15 or 24 words). You have the option to choose between a Base address (with stake credential)
+ * and Enterprise address (without stake credential). You can also decide which account index to derive. By default account 0 is derived.
+ */
+export class SeedWallet implements AbstractWallet {
+ translucent: Translucent;
+ private address_: string;
+ private rewardAddress_: string | null;
+ private paymentKey: string;
+ private stakeKey: string | null;
+ private paymentKeyHash: string;
+ private stakeKeyHash: string;
+ private privKeyHashMap: Record;
+ constructor(
+ translucent: Translucent,
+ seed: string,
+ options?: {
+ addressType?: "Base" | "Enterprise";
+ accountIndex?: number;
+ password?: string;
+ },
+ ) {
+ this.translucent = translucent;
+ const { address, rewardAddress, paymentKey, stakeKey } = walletFromSeed(
+ seed,
+ {
+ addressType: options?.addressType || "Base",
+ accountIndex: options?.accountIndex || 0,
+ password: options?.password,
+ network: this.translucent.network,
+ },
+ );
+ this.address_ = address;
+ this.rewardAddress_ = rewardAddress;
+ this.paymentKey = paymentKey;
+ this.stakeKey = stakeKey;
+ const paymentKeyHash = C.PrivateKey.from_bech32(paymentKey)
+ .to_public()
+ .hash()
+ .to_hex();
+ this.paymentKeyHash = paymentKeyHash;
+ const stakeKeyHash = stakeKey
+ ? C.PrivateKey.from_bech32(stakeKey).to_public().hash().to_hex()
+ : "";
+ this.stakeKeyHash = stakeKeyHash;
+ const privKeyHashMap = {
+ [paymentKeyHash]: paymentKey,
+ [stakeKeyHash]: stakeKey,
+ };
+ this.privKeyHashMap = privKeyHashMap;
+ }
+
+ // deno-lint-ignore require-await
+ async address(): Promise {
+ return this.address_;
+ }
+ // deno-lint-ignore require-await
+ async rewardAddress(): Promise {
+ return this.rewardAddress_ || null;
+ }
+ // deno-lint-ignore require-await
+ async getUtxos(): Promise {
+ return this.translucent.utxosAt(paymentCredentialOf(this.address_));
+ }
+ async getUtxosCore(): Promise {
+ const coreUtxos = C.TransactionUnspentOutputs.new();
+ (
+ await this.translucent.utxosAt(paymentCredentialOf(this.address_))
+ ).forEach((utxo) => {
+ coreUtxos.add(utxoToCore(utxo));
+ });
+ return coreUtxos;
+ }
+ async getDelegation(): Promise {
+ const rewardAddr = await this.rewardAddress();
+ return rewardAddr
+ ? await this.translucent.delegationAt(rewardAddr)
+ : { poolId: null, rewards: 0n };
+ }
+ async signTx(tx: C.Transaction): Promise {
+ const utxos = await this.translucent.utxosAt(this.address_);
+ const ownKeyHashes: Array = [
+ this.paymentKeyHash,
+ this.stakeKeyHash,
+ ];
+ const usedKeyHashes = discoverOwnUsedTxKeyHashes(tx, ownKeyHashes, utxos);
+ const txWitnessSetBuilder = C.TransactionWitnessSetBuilder.new();
+ usedKeyHashes.forEach((keyHash) => {
+ const witness = C.make_vkey_witness(
+ C.hash_transaction(tx.body()),
+ C.PrivateKey.from_bech32(this.privKeyHashMap[keyHash]!),
+ );
+ txWitnessSetBuilder.add_vkey(witness);
+ });
+ return txWitnessSetBuilder.build();
+ }
+ // deno-lint-ignore require-await
+ async signMessage(
+ address: Address | RewardAddress,
+ payload: Payload,
+ ): Promise {
+ const {
+ paymentCredential,
+ stakeCredential,
+ address: { hex: hexAddress },
+ } = this.translucent.utils.getAddressDetails(address);
+ const keyHash = paymentCredential?.hash || stakeCredential?.hash;
+ const privateKey = this.privKeyHashMap[keyHash!];
+ if (!privateKey) {
+ throw new Error(`Cannot sign message for address: ${address}.`);
+ }
+ return signData(hexAddress, payload, privateKey);
+ }
+ async submitTx(tx: Transaction): Promise {
+ return await this.translucent.provider.submitTx(tx);
+ }
+}
diff --git a/src/wallets/wallet_connector.ts b/src/wallets/wallet_connector.ts
new file mode 100644
index 0000000..e16f081
--- /dev/null
+++ b/src/wallets/wallet_connector.ts
@@ -0,0 +1,87 @@
+import {
+ Address,
+ Delegation,
+ Payload,
+ RewardAddress,
+ SignedMessage,
+ Transaction,
+ TxHash,
+ UTxO,
+ WalletApi,
+ coreToUtxo,
+ fromHex,
+ toHex,
+ C,
+ Translucent,
+} from "../mod";
+import { AbstractWallet } from "./abstract";
+
+export class WalletConnector implements AbstractWallet {
+ translucent: Translucent;
+ api: WalletApi;
+
+ constructor(translucent: Translucent, api: WalletApi) {
+ this.translucent = translucent;
+ this.api = api;
+ }
+
+ async getAddressHex() {
+ const [addressHex] = await this.api.getUsedAddresses();
+ if (addressHex) return addressHex;
+
+ const [unusedAddressHex] = await this.api.getUnusedAddresses();
+ return unusedAddressHex;
+ }
+
+ async address(): Promise {
+ return C.Address.from_bytes(fromHex(await this.getAddressHex())).to_bech32(
+ undefined,
+ );
+ }
+ async rewardAddress(): Promise {
+ const [rewardAddressHex] = await this.api.getRewardAddresses();
+ const rewardAddress = rewardAddressHex
+ ? C.RewardAddress.from_address(
+ C.Address.from_bytes(fromHex(rewardAddressHex)),
+ )!
+ .to_address()
+ .to_bech32(undefined)
+ : null;
+ return rewardAddress;
+ }
+ async getUtxos(): Promise {
+ const utxos = ((await this.api.getUtxos()) || []).map((utxo) => {
+ const parsedUtxo = C.TransactionUnspentOutput.from_bytes(fromHex(utxo));
+ return coreToUtxo(parsedUtxo);
+ });
+ return utxos;
+ }
+ async getUtxosCore(): Promise {
+ const utxos = C.TransactionUnspentOutputs.new();
+ ((await this.api.getUtxos()) || []).forEach((utxo) => {
+ utxos.add(C.TransactionUnspentOutput.from_bytes(fromHex(utxo)));
+ });
+ return utxos;
+ }
+ async getDelegation(): Promise {
+ const rewardAddr = await this.rewardAddress();
+ return rewardAddr
+ ? await this.translucent.delegationAt(rewardAddr)
+ : { poolId: null, rewards: 0n };
+ }
+ async signTx(tx: C.Transaction): Promise {
+ const witnessSet = await this.api.signTx(toHex(tx.to_bytes()), true);
+ return C.TransactionWitnessSet.from_bytes(fromHex(witnessSet));
+ }
+ async signMessage(
+ address: Address | RewardAddress,
+ payload: Payload,
+ ): Promise {
+ const hexAddress = toHex(C.Address.from_bech32(address).to_bytes());
+ return await this.api.signData(hexAddress, payload);
+ }
+ async submitTx(tx: Transaction): Promise {
+ const txHash = await this.api.submitTx(tx);
+ return txHash;
+ }
+}
diff --git a/tests/mod.test.ts b/tests/mod.test.ts
index 9074aa9..6e6cbf4 100644
--- a/tests/mod.test.ts
+++ b/tests/mod.test.ts
@@ -109,13 +109,12 @@ it("Wallet from utxos roundtrip (legacy utxos)", async () => {
"8282582040d1e106e587e6123aef50fa4347e34ec8c76f405ee9802ce90a345077b4e264018258390093de07f8feeda49b93c4cfd6817ebcfb7bfc4a0b18237c0196b205da3446b33ae55d27691cbbed080df5182262c307cdab62d3a4883f39df821a009a29bab82d581c126b8676446c84a5cd6e3259223b16a2314c5676b88ae1c1f8579a8fa144744d494e199d72581c3044e869070c8acf8f0ee6c87c285008fde6990f956a07cb9fd1e070a1444e616d6901581c34d1adbf3a7e95b253fd0999fb85e2d41d4121b36b834b83ac069ebba144414749581903ef581c370e055e07af274b588dc9bf3ef3203ec8b6830b7cdfaca9de38fc8fa146506c616e657401581c4122bca203030a36d8b605ab488335b1f2ee40dd7cd1454ce42f545ea143666c7901581c416e45fb08dc61a59a6bea82bde19e3014cef27ac4616c4f583adc15a144436f6f6c01581c4371ba767200e3e50e5dc1ff17f3f8d5154fa606534b03251a58e273a146436f6c6f727301581c46eea81aeed38d5e3595c4bf148a60f28006332ea9721a55748758f4a148456c657068616e7401581c54c1878edf4e6d49eab5b47b95c1bb9c6cf0622bf8dc5df1de6dc51fa1426a6f01581c55b2100ca1ac2f6ad955c5cc8c1c87d0f6155fd6ccb6857b7da5754ca145746f75636801581c57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522a54443484f431a0096dfcb444d494e541b0000000129f4a1764556414e494c1a013373b6465242455252591a3b9ac9d4465342455252591a3b9ac985581c5b7e2b5608c0f38eb186241f8b883d2e7bcad382f78c1e4e8993e513a14763616c76696e3101581c5c1c784a488bde15f9519142a9ee07886266598052f3dcc2105a6329a146526f636b657401581c60614c06bc93079c4e884db4dd966d2912aaae32a07a4506f749426ca1445472656501581c64834db34a8719177ee5ce1eda1c4cb91d2dca28bfc536073ba2176aa1445761766501581c648fd48f82c0a0687fb3d48178d0bcb3f0c5fe6cec68a1385129d4faa1444e616d6901581c68fdca4411f9a96df98e5517db3b49be01afc306655bf5e17ace78aea1477363656e65727901581c6998f4f9478186f3c182a4b334ef4e7d440658af06562ace55f74587a1444475636b01581c715b818224e0f83c747e8ab443a647ccf4dd2a6f0b2b37d7af8b3a60a149427574746572666c7901581c7509d2c7f0f3eb36d0336fcb7127bf3db1d5336583908d049e5a3716a1444e616d6901581c75ac2ca896ba71faee950eff23ad269b8dbd7c66e0806164ae74352ca14b526f636b6574416761696e01581c766bb5e84dc3438a5d7b2d686da04047f5c3ea981eac03de63071444a145576174657201581c83e1588347feaa9183030a82aa301b6a60b97c3844cdfd56b4bde133a144526f736501581c844c30a27b7756379ddae50e0dbe86c001d1a576d7adfdccc42c0b0da1444e616d6901581c868c245e1d99b32509542230fe744c120358353f195b6c19845134aca151526f636b6574202121202320242066736401581c890b7c122f3383fc859eecec99fa0ced3521b6e96f521a37de49e8baa1414b01581c9733e6e8f97a60b3a5c1ce6fe82f5c75b030766330bc5b4ea92b8b07a14a416e6f746865724170651842581c9d6eed0860acf0020af0d1f42c4c5eca26a2fb65aa09ed06bfe8d6ffa146666f7265737401581c9e6e63bbd52f0106716c6c920dcc9ad716ac9b001a18259eb20948e2a144436f6f6c01581c9ee64af489f0f8c6b64524988397b17269137d69830ae36c8b1530eba1414101581ca302467c86dfa2ae00e391745ba2de255ea2ec6f634e97a0a8c8bf27a1445374617201581ca380eb65f09337b3a562647823bc7edc9200f1fb8ff3bf1007263b69a1444a4f4f4f01581cbef61059036b3a7dbdb881d8239994f481d521c52908ad4c07421ce0a145506172697301581cc0355288172685f5732362b2bb4b8524223ad80b9cd97510b1460ef0a1414601581cc2f3a2c1af2d8cf25a89c4ed6c5c3bec4a4881341722ff42a31bf8cca1444d6f6f6e01581cc316c4d73ecb737ec21145a2dab42b4ad0dd5409118b482d7c601861a1445465737401581cc5d47df9211545b6b3bcb72e16654b1e46c9151fddbbe87ca0569e2ea14753636f6f74657201581cc921e0caf5ea1d4e313c96a4b416a91e085ba1355d9cd51e1d1e51f0a1414a01581ccf997119d2bf1fcbf73da2648e7eb047d4d0c5d8da04a426f46ee1a3a14472696e6701581cd311d3488cc4fef19d05634adce8534977a3bc6fc18136ad65df1d4fa1446c7120041a00b5b5b9581cd3840433dfd5720677fbcd3a4e14f44b249e3327fe1b8821f549bfd0a14349636501581ce3f3eb3f4f343fae935cea3a95f13cfba4eb7262919aec4b6d497d3aa1414901581ce7273c2510d45ec13847d2371a5289b2e3c132af29a1f8571bc15625a14f526f636b65744c61756e636831323301581cea15a43c13f242786c69c2ad448739430a666d66adf19c1701b1cbefa1414b01581cf334fced32a07a832b57d8795994e59e49c3d7dd8809748ff2eb3f0ea1434a4f4f01",
"82825820109fbbedaa6d8c0ed0262411802ddaf9e8596396cb9e010248f52df65a7d0d87018258390093de07f8feeda49b93c4cfd6817ebcfb7bfc4a0b18237c0196b205da3446b33ae55d27691cbbed080df5182262c307cdab62d3a4883f39df1a1f069542",
];
- translucent.selectWalletFrom({
- address:
- "addr_test1qzfauplclmk6fxuncn8adqt7hnahhlz2pvvzxlqpj6eqtk35g6en4e2aya53ewldpqxl2xpzvtps0ndtvtf6fzpl880srm02gc",
- utxos: rawUtxos.map((raw) =>
+ translucent.selectWalletFrom(
+ "addr_test1qzfauplclmk6fxuncn8adqt7hnahhlz2pvvzxlqpj6eqtk35g6en4e2aya53ewldpqxl2xpzvtps0ndtvtf6fzpl880srm02gc",
+ rawUtxos.map((raw) =>
coreToUtxo(C.TransactionUnspentOutput.from_bytes(fromHex(raw))),
),
- });
+ );
const rawUtxos_ = (await translucent.wallet.getUtxos()).map((utxo) =>
toHex(utxoToCore(utxo).to_bytes()),
@@ -383,10 +382,9 @@ it("toUnit/fromUnit property test", () => {
});
it("Preserve task/transaction order", async () => {
- translucent.selectWalletFrom({
- address:
- "addr_test1qq90qrxyw5qtkex0l7mc86xy9a6xkn5t3fcwm6wq33c38t8nhh356yzp7k3qwmhe4fk0g5u6kx5ka4rz5qcq4j7mvh2sts2cfa",
- utxos: [
+ translucent.selectWalletFrom(
+ "addr_test1qq90qrxyw5qtkex0l7mc86xy9a6xkn5t3fcwm6wq33c38t8nhh356yzp7k3qwmhe4fk0g5u6kx5ka4rz5qcq4j7mvh2sts2cfa",
+ [
{
txHash:
"2eefc93bc0dda80e78890f1f965733239e1f64f76555e8dcde1a4aa7db67b129",
@@ -399,7 +397,7 @@ it("Preserve task/transaction order", async () => {
scriptRef: null,
},
],
- });
+ );
const txCompA = translucent
.newTx()