Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(wallet): add TxBuilder types prototype
- Loading branch information
1 parent
29d042e
commit b9e6d65
Showing
5 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { NotImplementedError } from '@cardano-sdk/core'; | ||
import { ObservableWallet } from '../types'; | ||
import { TxBuilder } from './types'; | ||
|
||
/** | ||
* Minimal sub-type of ObservableWallet that is used by TxBuilder | ||
*/ | ||
export type TxBuilderObservableWallet = Pick<ObservableWallet, 'utxo' | 'protocolParameters$' | 'finalizeTx'>; | ||
|
||
/** | ||
* REVIEW: `ObservableWallet.buildTx()` would be nice, but it adds quite a lot of complexity | ||
* to web-extension messaging. I suggest we only do this as a separate util like this one for MVP. | ||
*/ | ||
export const buildTx = (_observableWallet: ObservableWallet): TxBuilder => { | ||
throw new NotImplementedError('TODO'); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './types'; | ||
export * from './buildTx'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { Cardano } from '@cardano-sdk/core'; | ||
import { InputSelectionError } from '@cardano-sdk/cip2'; | ||
import { SignTransactionOptions } from '../KeyManagement'; | ||
|
||
type DeepPartial<T> = T extends object | ||
? { | ||
[P in keyof T]?: DeepPartial<T[P]>; | ||
} | ||
: T; | ||
|
||
export type WalletNotInitializedError = {}; | ||
export type ValueValidationError = {} | WalletNotInitializedError; | ||
export type TxOutValidationError = ValueValidationError; | ||
export type TxBodyValidationError = TxOutValidationError | InputSelectionError; | ||
|
||
export type Valid<TValid> = TValid & { | ||
isValid: true; | ||
}; | ||
|
||
export interface Invalid<TError> { | ||
isValid: false; | ||
errors: TError[]; | ||
} | ||
|
||
export interface ValidTxOutData { | ||
readonly txOut: Cardano.TxOut; | ||
} | ||
|
||
export type ValidTxOut = Valid<ValidTxOutData>; | ||
export type InvalidTxOut = Invalid<TxOutValidationError>; | ||
export type MaybeValidTxOut = ValidTxOut | InvalidTxOut; | ||
|
||
export interface OutputBuilder { | ||
partialOutput: DeepPartial<Cardano.TxOut>; | ||
value(value: Cardano.Value): OutputBuilder; | ||
/** | ||
* Does not setValue | ||
*/ | ||
coin(coin: Cardano.Lovelace): OutputBuilder; | ||
assets(assets: Cardano.TokenMap): OutputBuilder; | ||
/** | ||
* @param quantity To remove an asset, set quantity to 0 | ||
*/ | ||
asset(assetId: Cardano.AssetId, quantity: bigint): OutputBuilder; | ||
address(address: Cardano.Address): OutputBuilder; | ||
datum(datum: Cardano.util.Hash32ByteBase16): OutputBuilder; | ||
build(): Promise<MaybeValidTxOut>; | ||
} | ||
|
||
export interface SignedTx { | ||
readonly tx: Cardano.NewTxAlonzo; | ||
submit(): Promise<void>; | ||
} | ||
|
||
export interface ValidTxBody { | ||
readonly body: Cardano.NewTxBodyAlonzo; | ||
readonly auxiliaryData?: Cardano.AuxiliaryData; | ||
readonly extraWitness?: Partial<Cardano.Witness>; | ||
|
||
sign(props?: SignTransactionOptions): Promise<SignedTx>; | ||
} | ||
|
||
export type ValidTx = Valid<ValidTxBody>; | ||
export type InvalidTx = Invalid<TxBodyValidationError>; | ||
export type MaybeValidTx = ValidTx | InvalidTx; | ||
|
||
export interface TxBuilder { | ||
partialTxBody: Partial<Cardano.NewTxBodyAlonzo>; | ||
auxiliaryData?: Cardano.AuxiliaryData; | ||
extraWitness?: Partial<Cardano.Witness>; | ||
|
||
addOutput(txOut: Cardano.TxOut): TxBuilder; | ||
/** | ||
* @param txOut must be in partialTxBody.outputs (===) | ||
*/ | ||
removeOutput(txOut: Cardano.TxOut): TxBuilder; | ||
/** | ||
* Does not addOutput | ||
*/ | ||
buildOutput(txOut?: DeepPartial<Cardano.TxOut>): OutputBuilder; | ||
/** | ||
* Add StakeDelegation and (if needed) StakeKeyRegistration certificate | ||
*/ | ||
delegate(poolId: Cardano.PoolId): TxBuilder; | ||
setMetadata(metadata: Cardano.TxMetadata): TxBuilder; | ||
// REVIEW: a design decision here: | ||
// - if this is async, then | ||
// - buildTx(wallet) can be sync. | ||
// - it uses more up-to-date wallet state when building a tx, which is good | ||
// - if this is sync, then | ||
// - buildTx(wallet) must be async and capture a snapshot of wallet state | ||
// at the point when builder is created | ||
build(): Promise<MaybeValidTx>; | ||
|
||
// REVIEW: assuming fields below are not needed for Lace right now, so out of scope of MVP: | ||
// - setMint | ||
// TODO: maybe this, or maybe datum should be added together with an output? | ||
// collaterals should be automatically computed and added to tx when you add scripts | ||
// - setScripts(scripts: Array<{script, datum, redeemer}>) | ||
// - setValidityInterval | ||
// TODO: figure out what script_data_hash is used for | ||
// - setScriptIntegrityHash(hash: Cardano.Hash32ByteBase16 | null); | ||
// - setRequiredExtraSignatures(keyHashes: Cardano.Ed25519KeyHash[]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* eslint-disable func-style */ | ||
/* eslint-disable jsdoc/require-jsdoc */ | ||
import * as mocks from '../mocks'; | ||
import { Cardano } from '@cardano-sdk/core'; | ||
import { MaybeValidTx, MaybeValidTxOut, ObservableWallet, ValidTx, ValidTxOut, buildTx } from '../../src'; | ||
import { createWallet } from './util'; | ||
|
||
function assertTxIsValid(tx: MaybeValidTx): asserts tx is ValidTx { | ||
expect(tx.isValid).toBe(true); | ||
} | ||
function assertTxOutIsValid(txOut: MaybeValidTxOut): asserts txOut is ValidTxOut { | ||
expect(txOut.isValid).toBe(true); | ||
} | ||
|
||
describe('buildTx', () => { | ||
let wallet: ObservableWallet; | ||
|
||
beforeAll(async () => { | ||
({ wallet } = await createWallet()); | ||
}); | ||
|
||
describe('outputs', () => { | ||
it.skip('can add outputs one by one', async () => { | ||
const tx = await buildTx(wallet).addOutput(mocks.utxo[0][1]).addOutput(mocks.utxo[1][1]).build(); | ||
assertTxIsValid(tx); | ||
expect(tx.body.outputs.length).toBe(2); | ||
}); | ||
|
||
describe('buildOutput', () => { | ||
it.skip('can build a valid output', async () => { | ||
const assetId = Cardano.AssetId('1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c960150584c'); | ||
const assetQuantity = 100n; | ||
const assets = new Map([[assetId, assetQuantity]]); | ||
const address = Cardano.Address('addr_test1vr8nl4u0u6fmtfnawx2rxfz95dy7m46t6dhzdftp2uha87syeufdg'); | ||
const datum = Cardano.Hash32ByteBase16('3e33018e8293d319ef5b3ac72366dd28006bd315b715f7e7cfcbd3004129b80d'); | ||
const output1Coin = 10_000_000n; | ||
const output2Base = mocks.utxo[0][1]; | ||
|
||
const txBuilder = buildTx(wallet); | ||
|
||
const output1 = await txBuilder | ||
.buildOutput() | ||
.address(address) | ||
.coin(output1Coin) | ||
.asset(assetId, assetQuantity) | ||
.build(); | ||
assertTxOutIsValid(output1); | ||
expect(output1.txOut).toEqual({ address, value: { assets, coin: output1Coin } }); | ||
|
||
const output2 = await txBuilder.buildOutput(output2Base).assets(assets).datum(datum).build(); | ||
assertTxOutIsValid(output2); | ||
expect(output2.txOut).toEqual({ datum, ...output2Base, value: { ...output2Base.value, assets } }); | ||
}); | ||
|
||
it.todo('validates required output properties and value constraints'); | ||
}); | ||
}); | ||
}); |