@kaspacom/kcc20-tx-builder 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -1
- package/README.md +2 -2
- package/RELEASING.md +19 -14
- package/artifacts/ArgentKCC20.reference.json +12428 -0
- package/artifacts/ArgentKCC20Wrapper.placeholder.json +12455 -0
- package/artifacts/KCC20.placeholder.json +1447 -1973
- package/artifacts/KCC20FeeTicket.placeholder.json +816 -1447
- package/artifacts/KCC20Orderbook.placeholder.json +1 -2
- package/artifacts/KCC20SnapshotBootstrap.placeholder.json +101 -100
- package/artifacts/KCC20SnapshotController.placeholder.json +101 -100
- package/artifacts/KCC20Vesting.placeholder.json +427 -429
- package/artifacts/KCC20Wrapper.placeholder.json +733 -734
- package/artifacts/kcc20-snapshot-bootstrap/KCC20SnapshotBootstrap.placeholder.json +101 -100
- package/artifacts/kcc20-snapshot-controller/KCC20SnapshotController.placeholder.json +101 -100
- package/artifacts/snapshot-standards.lock.json +6 -3
- package/artifacts/token-profiles.json +109 -0
- package/artifacts/token-profiles.sha256 +1 -0
- package/dist/action-source-resolver.d.ts.map +1 -1
- package/dist/action-source-resolver.js +11 -2
- package/dist/action-source-resolver.js.map +1 -1
- package/dist/artifacts.d.ts +10 -10
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +10 -10
- package/dist/artifacts.js.map +1 -1
- package/dist/cjs/action-source-resolver.js +11 -2
- package/dist/cjs/action-source-resolver.js.map +1 -1
- package/dist/cjs/artifacts.d.ts +10 -10
- package/dist/cjs/artifacts.js +10 -10
- package/dist/cjs/artifacts.js.map +1 -1
- package/dist/cjs/engine.d.ts +10 -0
- package/dist/cjs/engine.js +124 -77
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/profile-transfer.d.ts +60 -0
- package/dist/cjs/profile-transfer.js +248 -0
- package/dist/cjs/profile-transfer.js.map +1 -0
- package/dist/cjs/snapshot-operation.d.ts +0 -1
- package/dist/cjs/snapshot-operation.js +1 -1126
- package/dist/cjs/snapshot-operation.js.map +1 -1
- package/dist/cjs/snapshot-signerless-transactions.d.ts +1 -1
- package/dist/cjs/snapshot-signerless-transactions.js +3 -6
- package/dist/cjs/snapshot-signerless-transactions.js.map +1 -1
- package/dist/cjs/snapshot-signerless.js +5 -3
- package/dist/cjs/snapshot-signerless.js.map +1 -1
- package/dist/cjs/snapshot-verification.d.ts +1 -1
- package/dist/cjs/snapshot-verification.js +16 -12
- package/dist/cjs/snapshot-verification.js.map +1 -1
- package/dist/cjs/token-authorization.d.ts +42 -0
- package/dist/cjs/token-authorization.js +93 -0
- package/dist/cjs/token-authorization.js.map +1 -0
- package/dist/cjs/token-profiles.d.ts +82 -0
- package/dist/cjs/token-profiles.generated.d.ts +3 -0
- package/dist/cjs/token-profiles.generated.js +114 -0
- package/dist/cjs/token-profiles.generated.js.map +1 -0
- package/dist/cjs/token-profiles.js +257 -0
- package/dist/cjs/token-profiles.js.map +1 -0
- package/dist/cjs/transfer-payload.d.ts +9 -0
- package/dist/cjs/transfer-payload.js +37 -0
- package/dist/cjs/transfer-payload.js.map +1 -0
- package/dist/cjs/wrapper-operation.d.ts +1 -0
- package/dist/cjs/wrapper-operation.js +1 -0
- package/dist/cjs/wrapper-operation.js.map +1 -1
- package/dist/engine.d.ts +10 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +125 -78
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/profile-transfer.d.ts +61 -0
- package/dist/profile-transfer.d.ts.map +1 -0
- package/dist/profile-transfer.js +245 -0
- package/dist/profile-transfer.js.map +1 -0
- package/dist/snapshot-operation.d.ts +0 -1
- package/dist/snapshot-operation.d.ts.map +1 -1
- package/dist/snapshot-operation.js +0 -1125
- package/dist/snapshot-operation.js.map +1 -1
- package/dist/snapshot-signerless-transactions.d.ts +1 -1
- package/dist/snapshot-signerless-transactions.d.ts.map +1 -1
- package/dist/snapshot-signerless-transactions.js +3 -6
- package/dist/snapshot-signerless-transactions.js.map +1 -1
- package/dist/snapshot-signerless.d.ts.map +1 -1
- package/dist/snapshot-signerless.js +5 -3
- package/dist/snapshot-signerless.js.map +1 -1
- package/dist/snapshot-verification.d.ts +1 -1
- package/dist/snapshot-verification.d.ts.map +1 -1
- package/dist/snapshot-verification.js +16 -12
- package/dist/snapshot-verification.js.map +1 -1
- package/dist/token-authorization.d.ts +43 -0
- package/dist/token-authorization.d.ts.map +1 -0
- package/dist/token-authorization.js +88 -0
- package/dist/token-authorization.js.map +1 -0
- package/dist/token-profiles.d.ts +83 -0
- package/dist/token-profiles.d.ts.map +1 -0
- package/dist/token-profiles.generated.d.ts +4 -0
- package/dist/token-profiles.generated.d.ts.map +1 -0
- package/dist/token-profiles.generated.js +111 -0
- package/dist/token-profiles.generated.js.map +1 -0
- package/dist/token-profiles.js +245 -0
- package/dist/token-profiles.js.map +1 -0
- package/dist/transfer-payload.d.ts +10 -0
- package/dist/transfer-payload.d.ts.map +1 -0
- package/dist/transfer-payload.js +34 -0
- package/dist/transfer-payload.js.map +1 -0
- package/dist/wrapper-operation.d.ts +1 -0
- package/dist/wrapper-operation.d.ts.map +1 -1
- package/dist/wrapper-operation.js +1 -0
- package/dist/wrapper-operation.js.map +1 -1
- package/docs/API.md +1 -1
- package/docs/COMPATIBILITY.json +24 -33
- package/docs/INTEGRATION.md +2 -2
- package/docs/LAST_CALL.md +50 -0
- package/examples/README.md +5 -2
- package/examples/backend/cli.ts +2 -1
- package/examples/frontend/main.ts +2 -1
- package/examples/shared/build.ts +3 -0
- package/examples/shared/offline-profile.ts +27 -0
- package/examples/shared/recipe-fixtures.ts +3 -2
- package/examples/test/browser.ts +2 -2
- package/examples/test/build.test.ts +21 -8
- package/examples/test/sources.test.ts +3 -2
- package/package.json +2 -2
- package/src/action-source-resolver.ts +22 -4
- package/src/artifacts.ts +12 -10
- package/src/engine.ts +208 -105
- package/src/index.ts +7 -0
- package/src/profile-transfer.ts +425 -0
- package/src/snapshot-operation.ts +59 -1731
- package/src/snapshot-signerless-transactions.ts +8 -17
- package/src/snapshot-signerless.ts +5 -3
- package/src/snapshot-verification.ts +17 -13
- package/src/token-authorization.ts +135 -0
- package/src/token-profiles.generated.ts +112 -0
- package/src/token-profiles.ts +456 -0
- package/src/transfer-payload.ts +45 -0
- package/src/wrapper-operation.ts +2 -0
package/dist/engine.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
+
import { planTokenOwnerWitness } from "./token-authorization.js";
|
|
2
3
|
import { sha256 } from "@noble/hashes/sha256";
|
|
4
|
+
import { authenticateTokenArtifact, authenticateTokenWrapper, validateTokenProfileRegistry, requireTokenCapability, resolveTokenProfilePriceScale, } from "./token-profiles.js";
|
|
5
|
+
import { buildAuthenticatedKcc20TransferPayload } from "./transfer-payload.js";
|
|
6
|
+
import { BUNDLED_TOKEN_PROFILES, BUNDLED_TOKEN_PROFILE_DIGEST, } from "./token-profiles.generated.js";
|
|
3
7
|
import { DEFAULT_COMPUTE_BUDGET, DEFAULT_KCC20_FEE_BPS, DEFAULT_KCC20_MIN_PROTOCOL_FEE_SOMPI, DEFAULT_KCC20_PRICE_SCALE, DEFAULT_TOKEN_OUTPUT_SOMPI, KCC20_ORDERBOOK_EXPANDED_SWEEP_OUTPUT_SOMPI, KCC20_ORDERBOOK_MAX_SWEEP_BID_LEGS, KCC20_ORDERBOOK_MAX_EXPANDED_SWEEP_BID_LEGS, KCC20_PLATFORM_PROTOCOL_FEE_RECIPIENT, KCC20_ORDERBOOK_PROTOCOL_FEE_RECIPIENT, SUBNETWORK_ID_NATIVE, SOMPI_PER_TKAS, asciiToBytes32, buildKcc20FeeTicketScriptForState, buildNativeKcc20ScriptForState, buildKcc20WrapperScriptForState, buildKcc20OrderbookScriptForState, buildKcc20V3CreateBidSigScript, buildKcc20V3SellIntoBidSigScript, buildKcc20OrderbookFillBidFromSellerSweepSigScript, buildKcc20OrderbookFillAskSigScript, buildKcc20OrderbookFillBidSigScript, buildKcc20OrderbookSellIntoBidsSigScript, buildKcc20OrderbookSellIntoBids10SigScript, buildKcc20OrderbookCrossAskSigScript, buildKcc20OrderbookCrossAskLegacySigScript, buildKcc20OrderbookCrossBidSigScript, buildKcc20OrderbookCrossBidLegacySigScript, bytesToHex, calculateFeeSplit, findOutputIndex, hexToBytes, kcc20PsktBuilderError, mintPolicyName, normalizeSignature, p2pkScriptPubKey, p2shScriptHashHex, parseMintPolicy, requireHex32, selectFundingEntry, setVersionOneInputMassFields, stateToReceiptFields, utxoAmountSompi, ZERO_HASH, } from "./sale-common.js";
|
|
4
|
-
import { buildKcc20BurnSigScript, buildKcc20MintByOwnerSigScript, buildKcc20MintPublicSigScript, buildKcc20ScriptForState, buildKcc20SetPublicMintActiveSigScript, buildKcc20TransferSigScript, dispatchTagFor, encodeKcc20StateRecordArrays, holderExtensionCommitment, mintExtensionCommitment, normalizeSilverScriptArtifact, ownerAuthorizationWitness, } from "./abi.js";
|
|
8
|
+
import { buildKcc20BurnSigScript, buildKcc20MintByOwnerSigScript, buildKcc20MintPublicSigScript, buildKcc20ScriptForState, buildKcc20SetPublicMintActiveSigScript, buildKcc20TransferSigScript, dispatchTagFor, templateHash, encodeKcc20StateRecordArrays, holderExtensionCommitment, mintExtensionCommitment, normalizeSilverScriptArtifact, ownerAuthorizationWitness, } from "./abi.js";
|
|
5
9
|
import { KCC20_BUILDER_KEYS } from "./operations.js";
|
|
6
10
|
import { createKcc20SnapshotPsktBuilder } from "./snapshot-operation.js";
|
|
7
11
|
import { splitKcc20MintSupply } from "./protocol.js";
|
|
@@ -102,6 +106,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
102
106
|
const WRAPPED_ARTIFACT_PATH = "KCC20Orderbook.placeholder.json";
|
|
103
107
|
const FEE_TICKET_ARTIFACT_PATH = "KCC20FeeTicket.placeholder.json";
|
|
104
108
|
const staticArtifactCache = new Map();
|
|
109
|
+
const tokenProfiles = validateTokenProfileRegistry(options.tokenProfileRegistry?.manifest ?? BUNDLED_TOKEN_PROFILES, options.tokenProfileRegistry?.sha256 ?? BUNDLED_TOKEN_PROFILE_DIGEST);
|
|
105
110
|
function readStaticArtifact(path) {
|
|
106
111
|
let cached = staticArtifactCache.get(path);
|
|
107
112
|
if (!cached) {
|
|
@@ -114,7 +119,16 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
114
119
|
? (artifact.artifact ?? artifact)
|
|
115
120
|
: artifact;
|
|
116
121
|
const normalized = normalizeSilverScriptArtifact(artifactValue);
|
|
117
|
-
|
|
122
|
+
const profile = tokenProfiles.profiles.find((p) => p.artifactKey === path);
|
|
123
|
+
if (profile)
|
|
124
|
+
authenticateTokenArtifact(profile, artifactValue);
|
|
125
|
+
else {
|
|
126
|
+
const ownerProfile = tokenProfiles.profiles.find((p) => p.wrapper?.artifactKey === path);
|
|
127
|
+
if (ownerProfile)
|
|
128
|
+
authenticateTokenWrapper(ownerProfile, artifactValue);
|
|
129
|
+
else
|
|
130
|
+
assertKcc20ArtifactScriptHash(path, normalized);
|
|
131
|
+
}
|
|
118
132
|
return normalized;
|
|
119
133
|
})
|
|
120
134
|
.catch((error) => {
|
|
@@ -132,15 +146,20 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
132
146
|
};
|
|
133
147
|
}
|
|
134
148
|
async function resolveKcc20ArtifactForState(state, address, network) {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
for (const profile of tokenProfiles.profiles) {
|
|
150
|
+
const artifact = await readStaticArtifact(profile.artifactKey);
|
|
151
|
+
const script = buildKcc20ScriptForState(artifact, state);
|
|
152
|
+
const candidateAddress = kaspaWasm
|
|
153
|
+
.addressFromScriptPublicKey(kaspaWasm.payToScriptHashScript(script), network)
|
|
154
|
+
.toString();
|
|
155
|
+
if (candidateAddress !== address)
|
|
156
|
+
continue;
|
|
157
|
+
requireTokenCapability(profile, "transfer");
|
|
158
|
+
if (!profile.ownerSchemes.includes(Number(state.ownerScheme ?? state.identifierType)))
|
|
159
|
+
throw new Error("owner scheme is unsupported by this token profile");
|
|
160
|
+
return { version: profile.id, profile, artifact, script };
|
|
142
161
|
}
|
|
143
|
-
|
|
162
|
+
throw new Error("KCC20 UTXO address does not match a bundled token profile");
|
|
144
163
|
}
|
|
145
164
|
function artifactEntrypointDispatchTag(artifact, entrypoint) {
|
|
146
165
|
try {
|
|
@@ -175,13 +194,14 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
175
194
|
},
|
|
176
195
|
];
|
|
177
196
|
}
|
|
178
|
-
async function readWrapperArtifacts() {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
197
|
+
async function readWrapperArtifacts(profile) {
|
|
198
|
+
if (!profile.wrapper)
|
|
199
|
+
return [];
|
|
200
|
+
return [{
|
|
201
|
+
artifact: await readStaticArtifact(profile.wrapper.artifactKey),
|
|
182
202
|
version: "standard-native-v1",
|
|
183
|
-
|
|
184
|
-
|
|
203
|
+
tokenProfileId: profile.id,
|
|
204
|
+
}];
|
|
185
205
|
}
|
|
186
206
|
function wrappedArtifactSupportsEntrypoint(info, name) {
|
|
187
207
|
return Array.isArray(info?.artifact?.abi)
|
|
@@ -541,7 +561,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
541
561
|
};
|
|
542
562
|
const kw = kaspaWasm;
|
|
543
563
|
const { CovenantBinding, Encoding, Hash, RpcClient, ScriptBuilder, Transaction, TransactionOutput, addressFromScriptPublicKey, payToAddressScript, payToScriptHashScript, } = kw;
|
|
544
|
-
const utilityScript =
|
|
564
|
+
const utilityScript = buildKcc20ScriptForState(kcc20Artifact, utilityState);
|
|
545
565
|
const rootAddress = requireKaspaAddress(rootUtxo.address);
|
|
546
566
|
const utilityAddress = requireKaspaAddress(utilityUtxo.address);
|
|
547
567
|
const rootArtifactInfo = selectFeeTicketArtifactForState(feeTicketArtifacts, rootState, rootAddress, network, "active FeeTicket root UTXO address");
|
|
@@ -1659,10 +1679,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1659
1679
|
const feeTicketId = requireHex32(params.feeTicketId || ZERO_HASH, "fee ticket id");
|
|
1660
1680
|
const deployerOwner = requireHex32(params.deployerOwner || request.owner?.kcc20Owner, "deployer owner");
|
|
1661
1681
|
const rootAmount = parseU64(params.wrappedRootAmount ?? "0", "wrappedRootAmount");
|
|
1662
|
-
const
|
|
1663
|
-
params.tokenDisplayScale ??
|
|
1664
|
-
DEFAULT_KCC20_PRICE_SCALE, "priceScale");
|
|
1665
|
-
assertWrapperArtifactPriceScale(priceScale);
|
|
1682
|
+
const requestedPriceScale = params.priceScale ?? params.tokenDisplayScale;
|
|
1666
1683
|
const network = request.network || env.KASPA_NETWORK || "testnet-10";
|
|
1667
1684
|
const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
|
|
1668
1685
|
const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
|
|
@@ -1671,7 +1688,19 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1671
1688
|
DEFAULT_DEPLOY_TKAS, "KCC20_WRAPPER_DEPLOY_OUTPUT_TKAS");
|
|
1672
1689
|
const priorityFee = wrapperDeployPriorityFee(env.KCC20_WRAPPER_DEPLOY_PRIORITY_FEE_SOMPI);
|
|
1673
1690
|
const computeBudget = parsePositiveNumber(env.KCC20_WRAPPER_DEPLOY_COMPUTE_BUDGET || 600, "computeBudget");
|
|
1674
|
-
const
|
|
1691
|
+
const evidence = params.tokenUtxo;
|
|
1692
|
+
if (!evidence)
|
|
1693
|
+
throw new Error("authenticated token output is required before wrapper deployment");
|
|
1694
|
+
const sourceState = kcc20StateFromUtxo(evidence, request.owner);
|
|
1695
|
+
const sourceProfile = await resolveKcc20ArtifactForState(sourceState, requireKaspaAddress(evidence.address), network);
|
|
1696
|
+
requireTokenCapability(sourceProfile.profile, "wrap");
|
|
1697
|
+
if (!sourceProfile.profile.wrapper)
|
|
1698
|
+
throw new Error("token profile has no wrapper adapter");
|
|
1699
|
+
const priceScale = resolveTokenProfilePriceScale(sourceProfile.profile, requestedPriceScale === undefined
|
|
1700
|
+
? undefined
|
|
1701
|
+
: parsePositiveU64(requestedPriceScale, "priceScale"), DEFAULT_KCC20_PRICE_SCALE);
|
|
1702
|
+
assertWrapperArtifactPriceScale(priceScale);
|
|
1703
|
+
const wrapperArtifact = await readStaticArtifact(sourceProfile.profile.wrapper.artifactKey);
|
|
1675
1704
|
const wrappedArtifacts = await readWrappedArtifacts();
|
|
1676
1705
|
const wrappedArtifact = wrappedArtifacts[0].artifact;
|
|
1677
1706
|
const wrapperState = {
|
|
@@ -1704,6 +1733,18 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1704
1733
|
});
|
|
1705
1734
|
await rpc.connect();
|
|
1706
1735
|
try {
|
|
1736
|
+
const evidenceUtxos = await getUtxosByAddresses(rpc, [evidence.address]);
|
|
1737
|
+
const sourceEntry = findUtxoEntry(evidenceUtxos.entries, requireHex32(evidence.txidHex, "token evidence txid"), parseVout(evidence.vout, "token evidence vout"));
|
|
1738
|
+
if (!sourceEntry)
|
|
1739
|
+
throw new Error("token evidence is spent or unavailable");
|
|
1740
|
+
requireMatchingCovenantId(sourceEntry, contractCanonicalTokenId, "wrapper token evidence");
|
|
1741
|
+
const sourceSpk = sourceEntry.scriptPublicKey ??
|
|
1742
|
+
sourceEntry.entry?.scriptPublicKey ??
|
|
1743
|
+
sourceEntry.utxoEntry?.scriptPublicKey;
|
|
1744
|
+
if (!sourceSpk ||
|
|
1745
|
+
kw.addressFromScriptPublicKey(sourceSpk, network)?.toString() !==
|
|
1746
|
+
evidence.address)
|
|
1747
|
+
throw new Error("token evidence script does not match RPC");
|
|
1707
1748
|
const utxos = await getUtxosByAddresses(rpc, [walletAddress]);
|
|
1708
1749
|
const fundingEntry = selectFundingEntry(utxos.entries, deploySompi * 2n + priorityFee + 10000000n);
|
|
1709
1750
|
const fundingChangeOutputIndex = 2;
|
|
@@ -1784,6 +1825,9 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1784
1825
|
submitTransactionSupported: true,
|
|
1785
1826
|
metadata: {
|
|
1786
1827
|
builderKey: "kcc20wrapper.deploy-market",
|
|
1828
|
+
tokenProfileId: sourceProfile.profile.id,
|
|
1829
|
+
tokenProfileRegistryHash: options.tokenProfileRegistry?.sha256 ??
|
|
1830
|
+
BUNDLED_TOKEN_PROFILE_DIGEST,
|
|
1787
1831
|
contract: "KCC20Wrapper",
|
|
1788
1832
|
network,
|
|
1789
1833
|
walletAddress,
|
|
@@ -1863,10 +1907,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1863
1907
|
DEFAULT_TOKEN_OUTPUT_SOMPI.toString(), "tokenOutputSompi");
|
|
1864
1908
|
const priorityFee = parseU64(env.KCC20_WRAP_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(), "priorityFee");
|
|
1865
1909
|
const computeBudget = parsePositiveNumber(env.KCC20_WRAP_COMPUTE_BUDGET || 700, "computeBudget");
|
|
1866
|
-
const
|
|
1867
|
-
readWrapperArtifacts(),
|
|
1868
|
-
readWrappedArtifacts(),
|
|
1869
|
-
]);
|
|
1910
|
+
const wrappedArtifacts = await readWrappedArtifacts();
|
|
1870
1911
|
const holderState = kcc20StateFromUtxo(holderUtxo, request.owner);
|
|
1871
1912
|
if (holderState.identifierType !== 0) {
|
|
1872
1913
|
throw new Error("wrap requires pubkey-owned KCC20 holder UTXO");
|
|
@@ -1881,6 +1922,8 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1881
1922
|
}
|
|
1882
1923
|
const kcc20ArtifactInfo = await resolveKcc20ArtifactForState(holderState, requireKaspaAddress(holderUtxo.address), network);
|
|
1883
1924
|
const kcc20Artifact = kcc20ArtifactInfo.artifact;
|
|
1925
|
+
requireTokenCapability(kcc20ArtifactInfo.profile, "wrap");
|
|
1926
|
+
const wrapperArtifacts = await readWrapperArtifacts(kcc20ArtifactInfo.profile);
|
|
1884
1927
|
const remaining = holderState.amount - tokenAmount;
|
|
1885
1928
|
const priceScale = parsePositiveU64(wrapperUtxo.state?.priceScale ??
|
|
1886
1929
|
wrapperUtxo.state?.tokenDisplayScale ??
|
|
@@ -1907,7 +1950,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1907
1950
|
const { CovenantBinding, Encoding, Hash, RpcClient, ScriptBuilder, Transaction, TransactionOutput, payToAddressScript, payToScriptHashScript, } = kw;
|
|
1908
1951
|
const wrapperArtifactInfo = selectWrapperArtifactForUtxo({
|
|
1909
1952
|
kw,
|
|
1910
|
-
artifacts: wrapperArtifacts,
|
|
1953
|
+
artifacts: wrapperArtifacts.filter((info) => info.tokenProfileId === kcc20ArtifactInfo.profile.id),
|
|
1911
1954
|
state: wrapperState,
|
|
1912
1955
|
utxo: wrapperUtxo,
|
|
1913
1956
|
network,
|
|
@@ -1920,7 +1963,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
1920
1963
|
const wrapperArtifact = wrapperArtifactInfo.artifact;
|
|
1921
1964
|
const wrappedArtifact = wrappedArtifactInfo.artifact;
|
|
1922
1965
|
const wrapperScript = wrapperArtifactInfo.script;
|
|
1923
|
-
const holderScript =
|
|
1966
|
+
const holderScript = buildKcc20ScriptForState(kcc20Artifact, holderState);
|
|
1924
1967
|
const wrapperAddress = requireKaspaAddress(wrapperUtxo.address);
|
|
1925
1968
|
const holderAddress = requireKaspaAddress(holderUtxo.address);
|
|
1926
1969
|
assertScriptAddress(kw, holderScript, holderAddress, network, "KCC20 holder UTXO address");
|
|
@@ -2133,10 +2176,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
2133
2176
|
DEFAULT_TOKEN_OUTPUT_SOMPI.toString(), "tokenOutputSompi");
|
|
2134
2177
|
const priorityFee = parseU64(env.KCC20_UNWRAP_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(), "priorityFee");
|
|
2135
2178
|
const computeBudget = parsePositiveNumber(env.KCC20_UNWRAP_COMPUTE_BUDGET || 700, "computeBudget");
|
|
2136
|
-
const
|
|
2137
|
-
readWrapperArtifacts(),
|
|
2138
|
-
readWrappedArtifacts(),
|
|
2139
|
-
]);
|
|
2179
|
+
const wrappedArtifacts = await readWrappedArtifacts();
|
|
2140
2180
|
const reserveState = kcc20StateFromUtxo(reserveUtxo, request.owner);
|
|
2141
2181
|
if (reserveState.ownerScheme !== 4 || reserveState.isMintAuthority) {
|
|
2142
2182
|
throw new Error("KCC20 reserve UTXO must be covenant-owned holder state");
|
|
@@ -2146,6 +2186,8 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
2146
2186
|
}
|
|
2147
2187
|
const kcc20ArtifactInfo = await resolveKcc20ArtifactForState(reserveState, requireKaspaAddress(reserveUtxo.address), network);
|
|
2148
2188
|
const kcc20Artifact = kcc20ArtifactInfo.artifact;
|
|
2189
|
+
requireTokenCapability(kcc20ArtifactInfo.profile, "unwrap");
|
|
2190
|
+
const wrapperArtifacts = await readWrapperArtifacts(kcc20ArtifactInfo.profile);
|
|
2149
2191
|
const wrappedState = kcc20V3WrappedStateFromUtxo(wrappedUtxo, canonicalTokenId, request.owner);
|
|
2150
2192
|
if (bytesToHex(wrappedState.ownerIdentifier) !== recipientOwner) {
|
|
2151
2193
|
throw new Error("KCC20 orderbook holder UTXO is not owned by authenticated wallet");
|
|
@@ -2189,7 +2231,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
2189
2231
|
const { CovenantBinding, Encoding, Hash, RpcClient, ScriptBuilder, Transaction, TransactionOutput, payToAddressScript, payToScriptHashScript, } = kw;
|
|
2190
2232
|
const wrapperArtifactInfo = selectWrapperArtifactForUtxo({
|
|
2191
2233
|
kw,
|
|
2192
|
-
artifacts: wrapperArtifacts,
|
|
2234
|
+
artifacts: wrapperArtifacts.filter((info) => info.tokenProfileId === kcc20ArtifactInfo.profile.id),
|
|
2193
2235
|
state: wrapperState,
|
|
2194
2236
|
utxo: wrapperUtxo,
|
|
2195
2237
|
network,
|
|
@@ -2211,7 +2253,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
2211
2253
|
const wrapperScript = wrapperArtifactInfo.script;
|
|
2212
2254
|
const wrappedScript = wrappedArtifactInfo.script;
|
|
2213
2255
|
const refundAware = wrappedArtifactRefundsHolderDeposits(wrappedArtifactInfo);
|
|
2214
|
-
const reserveScript =
|
|
2256
|
+
const reserveScript = buildKcc20ScriptForState(kcc20Artifact, reserveState);
|
|
2215
2257
|
const wrapperAddress = wrapperArtifactInfo.address;
|
|
2216
2258
|
const wrappedAddress = wrappedArtifactInfo.address;
|
|
2217
2259
|
const reserveAddress = requireKaspaAddress(reserveUtxo.address);
|
|
@@ -5665,6 +5707,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
5665
5707
|
extensionCommitment: mintExtensionCommitment(extension),
|
|
5666
5708
|
};
|
|
5667
5709
|
const resolvedArtifact = await resolveKcc20ArtifactForState(inputState, minterAddress, network);
|
|
5710
|
+
requireTokenCapability(resolvedArtifact.profile, "mint");
|
|
5668
5711
|
const artifact = resolvedArtifact.artifact;
|
|
5669
5712
|
const recipientState = {
|
|
5670
5713
|
amount: mintAmount,
|
|
@@ -5954,6 +5997,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
5954
5997
|
extensionCommitment: mintExtensionCommitment(nextExtension),
|
|
5955
5998
|
};
|
|
5956
5999
|
const resolvedArtifact = await resolveKcc20ArtifactForState(inputState, minterAddress, network);
|
|
6000
|
+
requireTokenCapability(resolvedArtifact.profile, "mint");
|
|
5957
6001
|
dispatchTagFor(resolvedArtifact.artifact, "set_public_mint_active");
|
|
5958
6002
|
const minterTxid = requireHex32(activeMinterUtxo.txidHex, "minter txid");
|
|
5959
6003
|
const minterVout = parseVout(activeMinterUtxo.vout, "minter vout");
|
|
@@ -6177,6 +6221,10 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6177
6221
|
if (resolvedArtifacts.some((candidate) => candidate.version !== resolvedArtifacts[0].version)) {
|
|
6178
6222
|
throw new Error("consolidation requires holder UTXOs from one artifact");
|
|
6179
6223
|
}
|
|
6224
|
+
const profile = resolvedArtifacts[0].profile;
|
|
6225
|
+
if (holderUtxos.length > profile.maxTokenInputs) {
|
|
6226
|
+
throw new Error("consolidation exceeds the token profile input bound");
|
|
6227
|
+
}
|
|
6180
6228
|
const artifact = resolvedArtifacts[0].artifact;
|
|
6181
6229
|
for (const peerState of states.slice(1)) {
|
|
6182
6230
|
assertKcc20MergeableStates(primaryState, peerState, owner);
|
|
@@ -6480,12 +6528,6 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6480
6528
|
if (inputState.isMintAuthority || inputState.amount <= 0n) {
|
|
6481
6529
|
throw new Error("KCC20 transfer requires a holder UTXO");
|
|
6482
6530
|
}
|
|
6483
|
-
if (inputState.ownerScheme !== 0) {
|
|
6484
|
-
throw new Error("KCC20 transfer requires a Schnorr P2PK holder UTXO");
|
|
6485
|
-
}
|
|
6486
|
-
if (bytesToHex(inputState.ownerIdentifier) !== owner) {
|
|
6487
|
-
throw new Error("KCC20 transfer requires wallet to own the active holder UTXO");
|
|
6488
|
-
}
|
|
6489
6531
|
if (transferAmount > inputState.amount) {
|
|
6490
6532
|
throw new Error("tokenAmount exceeds active holder balance");
|
|
6491
6533
|
}
|
|
@@ -6494,6 +6536,21 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6494
6536
|
const tokenAddress = requireKaspaAddress(activeHolderUtxo.address);
|
|
6495
6537
|
const resolvedArtifact = await resolveKcc20ArtifactForState(inputState, tokenAddress, network);
|
|
6496
6538
|
const artifact = resolvedArtifact.artifact;
|
|
6539
|
+
if (params.mode && params.mode !== "owner")
|
|
6540
|
+
throw new Error("borrowed receive requires the explicit borrow transaction builder");
|
|
6541
|
+
const authorization = planTokenOwnerWitness({
|
|
6542
|
+
profile: resolvedArtifact.profile,
|
|
6543
|
+
owner: bytesToHex(inputState.ownerIdentifier),
|
|
6544
|
+
ownerScheme: inputState.ownerScheme,
|
|
6545
|
+
publicKey: request.owner?.publicKey ?? request.owner?.kcc20Owner,
|
|
6546
|
+
capabilities: options.walletSigningCapabilities ?? {
|
|
6547
|
+
schnorr: true,
|
|
6548
|
+
ecdsa: false,
|
|
6549
|
+
},
|
|
6550
|
+
role: "leader",
|
|
6551
|
+
});
|
|
6552
|
+
if (!resolvedArtifact.profile.ownerSchemes.includes(recipientOwnerScheme))
|
|
6553
|
+
throw new Error("recipient owner scheme is unsupported by the token profile");
|
|
6497
6554
|
const recipientState = {
|
|
6498
6555
|
...inputState,
|
|
6499
6556
|
owner: hexToBytes(recipientOwner),
|
|
@@ -6525,8 +6582,11 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6525
6582
|
throw new Error(`active holder UTXO ${tokenTxid}:${tokenVout} not found`);
|
|
6526
6583
|
}
|
|
6527
6584
|
const rpcScript = tokenEntry.scriptPublicKey ??
|
|
6528
|
-
tokenEntry.entry?.scriptPublicKey ??
|
|
6529
|
-
|
|
6585
|
+
tokenEntry.entry?.scriptPublicKey ??
|
|
6586
|
+
tokenEntry.utxoEntry?.scriptPublicKey;
|
|
6587
|
+
if (!rpcScript ||
|
|
6588
|
+
kw.addressFromScriptPublicKey(rpcScript, network)?.toString() !==
|
|
6589
|
+
tokenAddress) {
|
|
6530
6590
|
throw new Error("active holder RPC script does not match rebuilt covenant state");
|
|
6531
6591
|
}
|
|
6532
6592
|
if (activeHolderUtxo.amountSompi != null &&
|
|
@@ -6535,7 +6595,7 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6535
6595
|
}
|
|
6536
6596
|
const spendCovenantId = params.activeHolderNativeCovenantId
|
|
6537
6597
|
? requireMatchingCovenantId(tokenEntry, covenantId, "active holder")
|
|
6538
|
-
: covenantIdFromUtxoEntry(tokenEntry) ?? covenantId;
|
|
6598
|
+
: (covenantIdFromUtxoEntry(tokenEntry) ?? covenantId);
|
|
6539
6599
|
let outputSompi = 0n;
|
|
6540
6600
|
const recipientOutputIndex = 0;
|
|
6541
6601
|
const outputs = [
|
|
@@ -6587,9 +6647,9 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6587
6647
|
outputs,
|
|
6588
6648
|
subnetworkId: SUBNETWORK_ID_NATIVE,
|
|
6589
6649
|
gas: 0n,
|
|
6590
|
-
payload:
|
|
6650
|
+
payload: buildAuthenticatedKcc20TransferPayload({
|
|
6591
6651
|
covenantId: spendCovenantId,
|
|
6592
|
-
|
|
6652
|
+
fromOwnerIdentifier: bytesToHex(inputState.ownerIdentifier),
|
|
6593
6653
|
recipientOwner,
|
|
6594
6654
|
recipientOwnerScheme,
|
|
6595
6655
|
tokenAmount: transferAmount,
|
|
@@ -6602,9 +6662,10 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6602
6662
|
inputIndex: 0,
|
|
6603
6663
|
scriptHex: bytesToHex(tokenScript),
|
|
6604
6664
|
signType: 1,
|
|
6665
|
+
signatureAlgorithm: authorization.signatureAlgorithm,
|
|
6605
6666
|
signatureScript: {
|
|
6606
6667
|
mode: "ordered-args",
|
|
6607
|
-
args: kcc20TransferOrderedArgs(artifact, changeState ? [recipientState, changeState] : [recipientState]),
|
|
6668
|
+
args: kcc20TransferOrderedArgs(artifact, changeState ? [recipientState, changeState] : [recipientState], authorization.argument),
|
|
6608
6669
|
},
|
|
6609
6670
|
},
|
|
6610
6671
|
];
|
|
@@ -6620,6 +6681,9 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
6620
6681
|
scripts,
|
|
6621
6682
|
submitTransactionSupported: true,
|
|
6622
6683
|
metadata: {
|
|
6684
|
+
tokenProfileId: resolvedArtifact.profile.id,
|
|
6685
|
+
tokenProfileRegistryHash: options.tokenProfileRegistry?.sha256 ??
|
|
6686
|
+
BUNDLED_TOKEN_PROFILE_DIGEST,
|
|
6623
6687
|
builderKey: "kcc20.transfer",
|
|
6624
6688
|
operation: "transfer-token",
|
|
6625
6689
|
action: "transfer",
|
|
@@ -7614,34 +7678,6 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
7614
7678
|
},
|
|
7615
7679
|
}));
|
|
7616
7680
|
}
|
|
7617
|
-
function buildTransferPayload(fields) {
|
|
7618
|
-
return new TextEncoder().encode(JSON.stringify({
|
|
7619
|
-
tn12: {
|
|
7620
|
-
v: 1,
|
|
7621
|
-
tmpl: "KCC20",
|
|
7622
|
-
op: "transfer",
|
|
7623
|
-
args: [
|
|
7624
|
-
{ name: "covenantId", type: "byte[32]", value: fields.covenantId },
|
|
7625
|
-
{ name: "fromOwner", type: "byte[32]", value: fields.fromOwner },
|
|
7626
|
-
{
|
|
7627
|
-
name: "recipientOwner",
|
|
7628
|
-
type: "byte[32]",
|
|
7629
|
-
value: fields.recipientOwner,
|
|
7630
|
-
},
|
|
7631
|
-
{
|
|
7632
|
-
name: "recipientOwnerScheme",
|
|
7633
|
-
type: "u64",
|
|
7634
|
-
value: String(fields.recipientOwnerScheme),
|
|
7635
|
-
},
|
|
7636
|
-
{
|
|
7637
|
-
name: "tokenAmount",
|
|
7638
|
-
type: "u64",
|
|
7639
|
-
value: fields.tokenAmount.toString(),
|
|
7640
|
-
},
|
|
7641
|
-
],
|
|
7642
|
-
},
|
|
7643
|
-
}));
|
|
7644
|
-
}
|
|
7645
7681
|
function tokenOutput(kw, artifact, state, sompi, covenantId, authorizingInput) {
|
|
7646
7682
|
return new kw.TransactionOutput(sompi, kw.payToScriptHashScript(buildKcc20ScriptForState(artifact, state)), new kw.CovenantBinding(authorizingInput, new kw.Hash(covenantId)));
|
|
7647
7683
|
}
|
|
@@ -8937,18 +8973,29 @@ export function createKcc20PsktBuilderEngine(options) {
|
|
|
8937
8973
|
values.push({ type: "signature" }, i64TemplateArg(active ? 1 : 0), i64TemplateArg(minterOutputIndex), dataTemplateArg(dispatchTagFor(artifact, "set_public_mint_active")));
|
|
8938
8974
|
return values;
|
|
8939
8975
|
}
|
|
8940
|
-
function
|
|
8976
|
+
function profileForArtifact(artifact) {
|
|
8977
|
+
const parts = templateParts(artifact);
|
|
8978
|
+
const hash = bytesToHex(templateHash(parts.prefix, parts.suffix));
|
|
8979
|
+
const profile = tokenProfiles.profiles.find((p) => p.templateHash === hash);
|
|
8980
|
+
if (!profile)
|
|
8981
|
+
throw new Error("token artifact has no bundled profile");
|
|
8982
|
+
return profile;
|
|
8983
|
+
}
|
|
8984
|
+
function kcc20TransferOrderedArgs(artifact, nextStates, witness) {
|
|
8985
|
+
const profile = profileForArtifact(artifact);
|
|
8986
|
+
if (nextStates.length > profile.maxTokenOutputs)
|
|
8987
|
+
throw new Error("transfer exceeds profile output bound");
|
|
8941
8988
|
const fields = encodeKcc20StateRecordArrays(nextStates).map(dataTemplateArg);
|
|
8942
8989
|
return [
|
|
8943
8990
|
...fields,
|
|
8944
|
-
{ type: "signature", prefixHex: "00" },
|
|
8945
|
-
dataTemplateArg(dispatchTagFor(artifact,
|
|
8991
|
+
witness ?? { type: "signature", prefixHex: "00" },
|
|
8992
|
+
dataTemplateArg(dispatchTagFor(artifact, profileForArtifact(artifact).transferEntrypoint)),
|
|
8946
8993
|
];
|
|
8947
8994
|
}
|
|
8948
8995
|
function kcc20TransferDelegatorOrderedArgs(artifact) {
|
|
8949
8996
|
return [
|
|
8950
8997
|
{ type: "signature" },
|
|
8951
|
-
dataTemplateArg(dispatchTagFor(artifact,
|
|
8998
|
+
dataTemplateArg(dispatchTagFor(artifact, profileForArtifact(artifact).delegatorEntrypoint)),
|
|
8952
8999
|
];
|
|
8953
9000
|
}
|
|
8954
9001
|
function kcc20BurnOrderedArgs(artifact, nextStates, burnAmount) {
|