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
44 changes: 36 additions & 8 deletions cmd/wavecli/waveclicommands/cmd_taproot_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ func newTaprootAssetOnboardCmd() *cobra.Command {
Long: "Move one complete, isolated, confirmed Taproot " +
"Asset proof into a standard Wavelength VTXO policy. " +
"The current prototype requires tapd and waved to " +
"use the same LND wallet. Pass --wait, or preserve " +
"use the same LND wallet, which supplies any " +
"additional Bitcoin needed for the visible carrier " +
"value, miner fee, and wallet change. Pass --wait, " +
"or preserve " +
"every flag and rerun this command after " +
"confirmation when the response state is pending.",
Args: cobra.NoArgs,
Expand All @@ -64,8 +67,19 @@ func newTaprootAssetOnboardCmd() *cobra.Command {
"path to the complete confirmed Taproot Asset proof file",
)
flags.Uint64(
"max-fee-sat", 0,
"exact Bitcoin fee subtracted from the current anchor",
"carrier-value-sat", 0, "Bitcoin value carried by the "+
"asset VTXO (zero uses the operator minimum)",
)
flags.Uint64(
"sat-per-vbyte", 0, "explicit on-chain fee rate (mutually "+
"exclusive with --target-conf)",
)
flags.Uint32(
"target-conf", 0, "on-chain confirmation target (mutually "+
"exclusive with --sat-per-vbyte)",
)
flags.Uint64(
"max-fee-sat", 0, "hard upper bound for the on-chain miner fee",
)
flags.Bool(
"wait", false,
Expand Down Expand Up @@ -153,6 +167,9 @@ func taprootAssetOnboardingRequest(cmd *cobra.Command) (
assetRef, _ := cmd.Flags().GetString("asset-ref")
assetAmount, _ := cmd.Flags().GetUint64("asset-amount")
proofPath, _ := cmd.Flags().GetString("proof-file")
carrierValueSat, _ := cmd.Flags().GetUint64("carrier-value-sat")
feeRateSatPerVByte, _ := cmd.Flags().GetUint64("sat-per-vbyte")
targetConf, _ := cmd.Flags().GetUint32("target-conf")
maxFeeSat, _ := cmd.Flags().GetUint64("max-fee-sat")

switch {
Expand All @@ -170,6 +187,14 @@ func taprootAssetOnboardingRequest(cmd *cobra.Command) (

case maxFeeSat == 0:
return nil, fmt.Errorf("--max-fee-sat must be positive")

case feeRateSatPerVByte == 0 && targetConf == 0:
return nil, fmt.Errorf("exactly one of --sat-per-vbyte and " +
"--target-conf is required")

case feeRateSatPerVByte != 0 && targetConf != 0:
return nil, fmt.Errorf("--sat-per-vbyte and --target-conf " +
"are mutually exclusive")
}

if err := validateFreeText(
Expand All @@ -195,10 +220,13 @@ func taprootAssetOnboardingRequest(cmd *cobra.Command) (
}

return &waverpc.OnboardTaprootAssetRequest{
IdempotencyKey: idempotencyKey,
AssetRef: assetRef,
AssetAmount: assetAmount,
InputProofFile: proofFile,
MaxFeeSat: maxFeeSat,
IdempotencyKey: idempotencyKey,
AssetRef: assetRef,
AssetAmount: assetAmount,
InputProofFile: proofFile,
MaxFeeSat: maxFeeSat,
CarrierValueSat: carrierValueSat,
FeeRateSatPerVbyte: feeRateSatPerVByte,
TargetConf: targetConf,
}, nil
}
38 changes: 38 additions & 0 deletions cmd/wavecli/waveclicommands/cmd_taproot_assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ func TestTaprootAssetOnboardingRequest(t *testing.T) {
require.NoError(t, cmd.Flags().Set("asset-ref", "asset-id"))
require.NoError(t, cmd.Flags().Set("asset-amount", "42"))
require.NoError(t, cmd.Flags().Set("proof-file", proofPath))
require.NoError(t, cmd.Flags().Set("carrier-value-sat", "1000"))
require.NoError(t, cmd.Flags().Set("sat-per-vbyte", "2"))
require.NoError(t, cmd.Flags().Set("max-fee-sat", "500"))

request, err := taprootAssetOnboardingRequest(cmd)
Expand All @@ -34,9 +36,31 @@ func TestTaprootAssetOnboardingRequest(t *testing.T) {
require.Equal(t, "asset-id", request.AssetRef)
require.Equal(t, uint64(42), request.AssetAmount)
require.Equal(t, proof, request.InputProofFile)
require.Equal(t, uint64(1_000), request.CarrierValueSat)
require.Equal(t, uint64(2), request.FeeRateSatPerVbyte)
require.Zero(t, request.TargetConf)
require.Equal(t, uint64(500), request.MaxFeeSat)
}

// TestTaprootAssetOnboardingRequestTargetConf verifies the estimator mode and
// the daemon-side operator-minimum carrier default remain explicit on the
// wire.
func TestTaprootAssetOnboardingRequestTargetConf(t *testing.T) {
t.Parallel()

proofPath := filepath.Join(t.TempDir(), "asset-proof.tasset")
require.NoError(t, os.WriteFile(proofPath, []byte("proof"), 0o600))
cmd := validTaprootAssetOnboardCmd(t, proofPath)
require.NoError(t, cmd.Flags().Set("sat-per-vbyte", "0"))
require.NoError(t, cmd.Flags().Set("target-conf", "6"))

request, err := taprootAssetOnboardingRequest(cmd)
require.NoError(t, err)
require.Zero(t, request.CarrierValueSat)
require.Zero(t, request.FeeRateSatPerVbyte)
require.Equal(t, uint32(6), request.TargetConf)
}

// TestWaitForTaprootAssetOnboarding verifies --wait reuses one byte-identical
// request until the daemon reports that registration is ready.
func TestWaitForTaprootAssetOnboarding(t *testing.T) {
Expand Down Expand Up @@ -116,6 +140,19 @@ func TestTaprootAssetOnboardingRequestRejectsInvalidInput(t *testing.T) {
value: "0",
wantErr: "--max-fee-sat must be positive",
},
{
name: "fee selector",
flag: "sat-per-vbyte",
value: "0",
wantErr: "exactly one of --sat-per-vbyte and " +
"--target-conf is required",
},
{
name: "two fee selectors",
flag: "target-conf",
value: "6",
wantErr: "mutually exclusive",
},
}

for _, test := range tests {
Expand Down Expand Up @@ -143,6 +180,7 @@ func validTaprootAssetOnboardCmd(t *testing.T,
require.NoError(t, cmd.Flags().Set("asset-ref", "asset-id"))
require.NoError(t, cmd.Flags().Set("asset-amount", "42"))
require.NoError(t, cmd.Flags().Set("proof-file", proofPath))
require.NoError(t, cmd.Flags().Set("sat-per-vbyte", "2"))
require.NoError(t, cmd.Flags().Set("max-fee-sat", "500"))

return cmd
Expand Down
Loading