@kaspacom/kcc20-tx-builder 0.4.0 → 0.5.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 +6 -1
- package/README.md +2 -2
- package/RELEASING.md +3 -3
- 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 +1 -1
- package/dist/cjs/snapshot-signerless-transactions.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/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 +1 -1
- package/dist/snapshot-signerless-transactions.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/docs/API.md +1 -1
- package/docs/COMPATIBILITY.json +2 -2
- package/docs/INTEGRATION.md +2 -2
- package/examples/README.md +2 -2
- package/package.json +1 -1
- package/src/snapshot-operation.ts +59 -1731
- package/src/snapshot-signerless-transactions.ts +1 -1
- package/src/snapshot-verification.ts +17 -13
|
@@ -130,9 +130,6 @@ export interface Kcc20SnapshotBuilderOptions {
|
|
|
130
130
|
transactionWithCalculatedFeeChange: (...args: any[]) => any;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
export const SNAPSHOT_CANCEL_SINK_P2SH =
|
|
134
|
-
"d379e50f3e2203f9a92584916335bb7cd014b237ceaa8b36f1d3d80d0cc88b32";
|
|
135
|
-
|
|
136
133
|
export function createKcc20SnapshotPsktBuilder(
|
|
137
134
|
options: Kcc20SnapshotBuilderOptions,
|
|
138
135
|
): (input: unknown) => Promise<Record<string, unknown>> {
|
|
@@ -686,1246 +683,68 @@ export function createKcc20SnapshotPsktBuilder(
|
|
|
686
683
|
...common,
|
|
687
684
|
shard,
|
|
688
685
|
deploymentGuardCommitment: "01".repeat(32),
|
|
689
|
-
});
|
|
690
|
-
const peerPlans = allShards.slice(1).map((peer) => {
|
|
691
|
-
if (!peer.controllerId)
|
|
692
|
-
throw new Error(
|
|
693
|
-
"deploy shard zero only after every peer controller is indexed",
|
|
694
|
-
);
|
|
695
|
-
const plan = createSignerlessControllerPlan({
|
|
696
|
-
...common,
|
|
697
|
-
shard: peer,
|
|
698
|
-
deploymentGuardCommitment: ZERO_HASH,
|
|
699
|
-
});
|
|
700
|
-
return { controllerId: peer.controllerId, state: plan.controllerState };
|
|
701
|
-
});
|
|
702
|
-
const guard = snapshotPeerPlanCommitment({
|
|
703
|
-
migrationCommitment: common.migrationCommitment,
|
|
704
|
-
manifestCommitment: common.manifestCommitment,
|
|
705
|
-
shards: [
|
|
706
|
-
{ controllerId: "01".repeat(32), state: provisional.controllerState },
|
|
707
|
-
...peerPlans,
|
|
708
|
-
],
|
|
709
|
-
});
|
|
710
|
-
return createSignerlessControllerPlan({
|
|
711
|
-
...common,
|
|
712
|
-
shard,
|
|
713
|
-
deploymentGuardCommitment: guard,
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
function normalizedSignerlessShards(values) {
|
|
718
|
-
if (!Array.isArray(values) || values.length < 1 || values.length > 8) {
|
|
719
|
-
throw new Error("signerless snapshot requires 1..8 ordered shards");
|
|
720
|
-
}
|
|
721
|
-
const shards = values.map((value) => ({
|
|
722
|
-
...snapshotShard(value),
|
|
723
|
-
controllerId: value?.controllerId
|
|
724
|
-
? requireHex32(value.controllerId, "controller id")
|
|
725
|
-
: undefined,
|
|
726
|
-
controllerSource: value?.controllerSource,
|
|
727
|
-
}));
|
|
728
|
-
shards.sort((left, right) => left.shardIndex - right.shardIndex);
|
|
729
|
-
if (shards.some((shard, index) => shard.shardIndex !== index)) {
|
|
730
|
-
throw new Error(
|
|
731
|
-
"signerless snapshot shards must be contiguous from zero",
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
return shards;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function portableInput(entry, network, script) {
|
|
738
|
-
return {
|
|
739
|
-
previousOutpoint: entry.outpoint,
|
|
740
|
-
utxo: entry,
|
|
741
|
-
networkId: network,
|
|
742
|
-
...(script ? { script } : {}),
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
function outpointOfSource(source, label) {
|
|
747
|
-
return {
|
|
748
|
-
transactionId: requireHex32(source?.txidHex, `${label} txid`),
|
|
749
|
-
index: unsignedInteger(source?.vout, `${label} vout`),
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
async function buildBootstrapCancellation(input) {
|
|
754
|
-
const request = input.request || {};
|
|
755
|
-
const params = request.params || {};
|
|
756
|
-
const network = networkOf(request);
|
|
757
|
-
const walletAddress = walletAddressOf(request);
|
|
758
|
-
const artifacts = await readPinnedArtifacts(
|
|
759
|
-
params.artifactPins,
|
|
760
|
-
false,
|
|
761
|
-
true,
|
|
762
|
-
);
|
|
763
|
-
const template = templateParts(artifacts.kcc20);
|
|
764
|
-
const base = bootstrapBaseState(params, template);
|
|
765
|
-
const migrationId = requireHex32(params.migrationId, "migration ID");
|
|
766
|
-
const bootstrapId = requireHex32(params.bootstrapId, "bootstrap ID");
|
|
767
|
-
const phase = String(params.bootstrapPhase || "").toUpperCase();
|
|
768
|
-
if (!["UNBOUND", "TOKEN_BOUND", "SEALED"].includes(phase)) {
|
|
769
|
-
throw new Error(
|
|
770
|
-
"bootstrap cancellation phase is not terminally cancellable",
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
const bound = phase !== "UNBOUND";
|
|
774
|
-
const tokenId = bound
|
|
775
|
-
? requireHex32(params.tokenId, "token ID")
|
|
776
|
-
: ZERO_HASH;
|
|
777
|
-
const beforeState = {
|
|
778
|
-
...base,
|
|
779
|
-
phase: phase === "UNBOUND" ? 0n : phase === "TOKEN_BOUND" ? 1n : 2n,
|
|
780
|
-
tokenId: hexToBytes(tokenId),
|
|
781
|
-
shardPlanCommitment:
|
|
782
|
-
phase === "SEALED"
|
|
783
|
-
? hexToBytes(
|
|
784
|
-
requireHex32(
|
|
785
|
-
params.bootstrapShardPlanCommitment,
|
|
786
|
-
"bootstrap shard plan commitment",
|
|
787
|
-
),
|
|
788
|
-
)
|
|
789
|
-
: hexToBytes(ZERO_HASH),
|
|
790
|
-
activationDigest:
|
|
791
|
-
phase === "SEALED"
|
|
792
|
-
? hexToBytes(
|
|
793
|
-
requireHex32(
|
|
794
|
-
params.bootstrapActivationDigest,
|
|
795
|
-
"bootstrap activation digest",
|
|
796
|
-
),
|
|
797
|
-
)
|
|
798
|
-
: hexToBytes(ZERO_HASH),
|
|
799
|
-
shardCount:
|
|
800
|
-
phase === "SEALED"
|
|
801
|
-
? positiveI64(params.bootstrapShardCount, "bootstrap shard count")
|
|
802
|
-
: 0n,
|
|
803
|
-
};
|
|
804
|
-
if (beforeState.shardCount > 8n) {
|
|
805
|
-
throw new Error("bootstrap cancellation shard count exceeds policy");
|
|
806
|
-
}
|
|
807
|
-
const bootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
808
|
-
artifacts.bootstrap,
|
|
809
|
-
beforeState,
|
|
810
|
-
);
|
|
811
|
-
const bootstrapSource = sourceDescriptor(
|
|
812
|
-
params.bootstrapSource,
|
|
813
|
-
"bootstrap source",
|
|
814
|
-
scriptAddress(bootstrapScript, network),
|
|
815
|
-
);
|
|
816
|
-
const tokenState = bound
|
|
817
|
-
? kcc20State(params.tokenSource?.state)
|
|
818
|
-
: undefined;
|
|
819
|
-
if (
|
|
820
|
-
tokenState &&
|
|
821
|
-
(tokenState.amount !== base.totalSupply ||
|
|
822
|
-
tokenState.ownerScheme !== 4 ||
|
|
823
|
-
bytesToHex(tokenState.owner) !== bootstrapId ||
|
|
824
|
-
bytesToHex(tokenState.extensionCommitment) !==
|
|
825
|
-
bytesToHex(base.holderExtensionCommitment))
|
|
826
|
-
) {
|
|
827
|
-
throw new Error("bootstrap cancellation token source does not match");
|
|
828
|
-
}
|
|
829
|
-
const tokenScript = tokenState
|
|
830
|
-
? buildKcc20ScriptForState(artifacts.kcc20, tokenState)
|
|
831
|
-
: undefined;
|
|
832
|
-
const tokenSource = tokenScript
|
|
833
|
-
? sourceDescriptor(
|
|
834
|
-
params.tokenSource,
|
|
835
|
-
"token source",
|
|
836
|
-
scriptAddress(tokenScript, network),
|
|
837
|
-
)
|
|
838
|
-
: undefined;
|
|
839
|
-
if (!bound && params.tokenSource != null) {
|
|
840
|
-
throw new Error(
|
|
841
|
-
"unbound bootstrap cancellation must not include a token",
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
const sinkTokenState = tokenState
|
|
845
|
-
? {
|
|
846
|
-
amount: base.totalSupply,
|
|
847
|
-
owner: hexToBytes(SNAPSHOT_CANCEL_SINK_P2SH),
|
|
848
|
-
ownerScheme: 3,
|
|
849
|
-
borrowScheme: 0,
|
|
850
|
-
borrowGuard: hexToBytes(ZERO_HASH),
|
|
851
|
-
extensionCommitment: base.holderExtensionCommitment,
|
|
852
|
-
}
|
|
853
|
-
: undefined;
|
|
854
|
-
const sinkTokenScript = sinkTokenState
|
|
855
|
-
? buildKcc20ScriptForState(artifacts.kcc20, sinkTokenState)
|
|
856
|
-
: undefined;
|
|
857
|
-
const rpc = rpcFor(network);
|
|
858
|
-
await rpc.connect();
|
|
859
|
-
try {
|
|
860
|
-
const bootstrapPromise = rpc.getUtxosByAddresses({
|
|
861
|
-
addresses: [bootstrapSource.address],
|
|
862
|
-
});
|
|
863
|
-
const tokenPromise = tokenSource
|
|
864
|
-
? rpc.getUtxosByAddresses({ addresses: [tokenSource.address] })
|
|
865
|
-
: Promise.resolve({ entries: [] });
|
|
866
|
-
const [bootstrapUtxos, tokenUtxos, walletUtxos] = await Promise.all([
|
|
867
|
-
bootstrapPromise,
|
|
868
|
-
tokenPromise,
|
|
869
|
-
rpc.getUtxosByAddresses({ addresses: [walletAddress] }),
|
|
870
|
-
]);
|
|
871
|
-
const bootstrapEntry = findExactEntry(
|
|
872
|
-
bootstrapUtxos.entries,
|
|
873
|
-
bootstrapSource,
|
|
874
|
-
);
|
|
875
|
-
assertCovenantSource(bootstrapEntry, {
|
|
876
|
-
covenantId: bootstrapId,
|
|
877
|
-
script: bootstrapScript,
|
|
878
|
-
amount: base.bootstrapCellKas,
|
|
879
|
-
label: "bootstrap cancellation source",
|
|
880
|
-
});
|
|
881
|
-
const tokenEntry = tokenSource
|
|
882
|
-
? findExactEntry(tokenUtxos.entries, tokenSource)
|
|
883
|
-
: undefined;
|
|
884
|
-
if (tokenEntry) {
|
|
885
|
-
assertCovenantSource(tokenEntry, {
|
|
886
|
-
covenantId: tokenId,
|
|
887
|
-
script: tokenScript,
|
|
888
|
-
amount: base.tokenCellKas,
|
|
889
|
-
label: "bootstrap cancellation token",
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
const consumed = [bootstrapEntry, ...(tokenEntry ? [tokenEntry] : [])];
|
|
893
|
-
const fundingEntry = selectFundingEntry(
|
|
894
|
-
withoutOutpoints(
|
|
895
|
-
walletUtxos.entries,
|
|
896
|
-
consumed.map(({ outpoint }) => outpoint),
|
|
897
|
-
),
|
|
898
|
-
priorityFee() + 10_000n,
|
|
899
|
-
[],
|
|
900
|
-
params.fundingOutpoint,
|
|
901
|
-
);
|
|
902
|
-
const outputs = [];
|
|
903
|
-
if (sinkTokenScript) {
|
|
904
|
-
outputs.push(
|
|
905
|
-
new kaspaWasm.TransactionOutput(
|
|
906
|
-
base.tokenCellKas,
|
|
907
|
-
kaspaWasm.payToScriptHashScript(sinkTokenScript),
|
|
908
|
-
new kaspaWasm.CovenantBinding(1, new kaspaWasm.Hash(tokenId)),
|
|
909
|
-
),
|
|
910
|
-
);
|
|
911
|
-
}
|
|
912
|
-
const sinkOutputIndex = outputs.length;
|
|
913
|
-
outputs.push(
|
|
914
|
-
new kaspaWasm.TransactionOutput(
|
|
915
|
-
base.bootstrapCellKas,
|
|
916
|
-
kaspaWasm.payToScriptHashScript(Uint8Array.of(0x6a)),
|
|
917
|
-
),
|
|
918
|
-
);
|
|
919
|
-
const fundingChangeOutputIndex = appendFundingChange(
|
|
920
|
-
outputs,
|
|
921
|
-
[...consumed, fundingEntry],
|
|
922
|
-
walletAddress,
|
|
923
|
-
base.bootstrapCellKas + (bound ? base.tokenCellKas : 0n),
|
|
924
|
-
priorityFee(),
|
|
925
|
-
);
|
|
926
|
-
const inputs = [
|
|
927
|
-
covenantInput(bootstrapEntry, BOOTSTRAP_COMPUTE_BUDGET),
|
|
928
|
-
...(tokenEntry
|
|
929
|
-
? [covenantInput(tokenEntry, KCC20_COMPUTE_BUDGET)]
|
|
930
|
-
: []),
|
|
931
|
-
publicInput(fundingEntry, PUBLIC_INPUT_COMPUTE_BUDGET),
|
|
932
|
-
];
|
|
933
|
-
const transaction = new kaspaWasm.Transaction({
|
|
934
|
-
version: 1,
|
|
935
|
-
lockTime: 0n,
|
|
936
|
-
inputs,
|
|
937
|
-
outputs,
|
|
938
|
-
subnetworkId: NATIVE_SUBNETWORK_ID,
|
|
939
|
-
gas: 0n,
|
|
940
|
-
payload: "",
|
|
941
|
-
});
|
|
942
|
-
setVersionOneInputMassFields(transaction, PUBLIC_INPUT_COMPUTE_BUDGET);
|
|
943
|
-
transaction.inputs[0].computeBudget = BOOTSTRAP_COMPUTE_BUDGET;
|
|
944
|
-
if (tokenEntry) {
|
|
945
|
-
transaction.inputs[1].computeBudget = KCC20_COMPUTE_BUDGET;
|
|
946
|
-
}
|
|
947
|
-
const recipes = bootstrapApprovalRecipes(
|
|
948
|
-
bytesToHex(bootstrapScript),
|
|
949
|
-
bootstrapCancellationArgs(
|
|
950
|
-
artifacts,
|
|
951
|
-
bound,
|
|
952
|
-
sinkOutputIndex,
|
|
953
|
-
sinkTokenState,
|
|
954
|
-
),
|
|
955
|
-
);
|
|
956
|
-
transaction.inputs[0].signatureScript = encodeP2sh(
|
|
957
|
-
signatureScriptArgsBytes(recipes.placeholder.args),
|
|
958
|
-
bootstrapScript,
|
|
959
|
-
);
|
|
960
|
-
if (tokenEntry && tokenScript && sinkTokenState) {
|
|
961
|
-
transaction.inputs[1].signatureScript = encodeP2sh(
|
|
962
|
-
buildKcc20TransferSigScript(kaspaWasm, artifacts.kcc20, {
|
|
963
|
-
nextStates: [sinkTokenState],
|
|
964
|
-
witness: Uint8Array.of(0),
|
|
965
|
-
}),
|
|
966
|
-
tokenScript,
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
const fundingInputIndex = inputs.length - 1;
|
|
970
|
-
const adjusted = transactionWithCalculatedFeeChange(
|
|
971
|
-
kaspaWasm,
|
|
972
|
-
transaction,
|
|
973
|
-
fundingChangeOutputIndex,
|
|
974
|
-
network,
|
|
975
|
-
"snapshot bootstrap cancellation",
|
|
976
|
-
[recipes.approval],
|
|
977
|
-
);
|
|
978
|
-
return outputEnvelope(
|
|
979
|
-
adjusted,
|
|
980
|
-
[{ index: fundingInputIndex }],
|
|
981
|
-
[recipes.approval],
|
|
982
|
-
{
|
|
983
|
-
migrationId,
|
|
984
|
-
stage: "bootstrap-cancel",
|
|
985
|
-
bootstrapId,
|
|
986
|
-
...(bound ? { tokenId } : {}),
|
|
987
|
-
cancellationMode: bound ? "BOUND" : "UNBOUND",
|
|
988
|
-
cancellationSinkP2sh: SNAPSHOT_CANCEL_SINK_P2SH,
|
|
989
|
-
bootstrapState: {
|
|
990
|
-
...bootstrapStateReceipt(beforeState),
|
|
991
|
-
phase,
|
|
992
|
-
},
|
|
993
|
-
bootstrapOutcome: "CANCELLED",
|
|
994
|
-
maximumFeeSompi: feeOf(adjusted).toString(),
|
|
995
|
-
intentInputs: [
|
|
996
|
-
{
|
|
997
|
-
role: "snapshot-bootstrap",
|
|
998
|
-
index: 0,
|
|
999
|
-
covenantId: bootstrapId,
|
|
1000
|
-
revealScriptHex: bytesToHex(bootstrapScript),
|
|
1001
|
-
},
|
|
1002
|
-
...(tokenEntry && tokenScript
|
|
1003
|
-
? [
|
|
1004
|
-
{
|
|
1005
|
-
role: "fixed-supply-token",
|
|
1006
|
-
index: 1,
|
|
1007
|
-
covenantId: tokenId,
|
|
1008
|
-
revealScriptHex: bytesToHex(tokenScript),
|
|
1009
|
-
staticSignatureScriptHex: signatureScriptHex(adjusted, 1),
|
|
1010
|
-
},
|
|
1011
|
-
]
|
|
1012
|
-
: []),
|
|
1013
|
-
{ role: "creator-funding", index: fundingInputIndex },
|
|
1014
|
-
],
|
|
1015
|
-
intentOutputs: [
|
|
1016
|
-
...(sinkTokenScript && sinkTokenState
|
|
1017
|
-
? [
|
|
1018
|
-
{
|
|
1019
|
-
role: "cancelled-token",
|
|
1020
|
-
index: 0,
|
|
1021
|
-
covenantId: tokenId,
|
|
1022
|
-
revealScriptHex: bytesToHex(sinkTokenScript),
|
|
1023
|
-
owner: SNAPSHOT_CANCEL_SINK_P2SH,
|
|
1024
|
-
ownerScheme: 3,
|
|
1025
|
-
borrowScheme: 0,
|
|
1026
|
-
borrowGuard: ZERO_HASH,
|
|
1027
|
-
extensionCommitment: bytesToHex(
|
|
1028
|
-
sinkTokenState.extensionCommitment,
|
|
1029
|
-
),
|
|
1030
|
-
tokenAmount: sinkTokenState.amount.toString(),
|
|
1031
|
-
},
|
|
1032
|
-
]
|
|
1033
|
-
: []),
|
|
1034
|
-
{
|
|
1035
|
-
role: "cancellation-sink",
|
|
1036
|
-
index: sinkOutputIndex,
|
|
1037
|
-
revealScriptHex: "6a",
|
|
1038
|
-
},
|
|
1039
|
-
],
|
|
1040
|
-
},
|
|
1041
|
-
);
|
|
1042
|
-
} finally {
|
|
1043
|
-
await rpc.disconnect();
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
async function buildBootstrapBind(input) {
|
|
1048
|
-
const request = input.request || {};
|
|
1049
|
-
const params = request.params || {};
|
|
1050
|
-
const network = networkOf(request);
|
|
1051
|
-
const walletAddress = walletAddressOf(request);
|
|
1052
|
-
const artifacts = await readPinnedArtifacts(
|
|
1053
|
-
params.artifactPins,
|
|
1054
|
-
false,
|
|
1055
|
-
true,
|
|
1056
|
-
);
|
|
1057
|
-
const template = templateParts(artifacts.kcc20);
|
|
1058
|
-
const base = bootstrapBaseState(params, template);
|
|
1059
|
-
const bootstrapId = requireHex32(params.bootstrapId, "bootstrap ID");
|
|
1060
|
-
const tokenId = requireHex32(params.tokenId, "token ID");
|
|
1061
|
-
const tokenState = kcc20State(params.tokenSource?.state);
|
|
1062
|
-
if (
|
|
1063
|
-
tokenState.amount !== base.totalSupply ||
|
|
1064
|
-
tokenState.ownerScheme !== 4 ||
|
|
1065
|
-
bytesToHex(tokenState.owner) !== bootstrapId ||
|
|
1066
|
-
bytesToHex(tokenState.extensionCommitment) !==
|
|
1067
|
-
bytesToHex(base.holderExtensionCommitment)
|
|
1068
|
-
) {
|
|
1069
|
-
throw new Error(
|
|
1070
|
-
"bootstrap bind token source does not match the migration",
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
const beforeState = {
|
|
1074
|
-
...base,
|
|
1075
|
-
phase: 0n,
|
|
1076
|
-
tokenId: hexToBytes(ZERO_HASH),
|
|
1077
|
-
shardPlanCommitment: hexToBytes(ZERO_HASH),
|
|
1078
|
-
activationDigest: hexToBytes(ZERO_HASH),
|
|
1079
|
-
shardCount: 0n,
|
|
1080
|
-
};
|
|
1081
|
-
const afterState = {
|
|
1082
|
-
...base,
|
|
1083
|
-
phase: 1n,
|
|
1084
|
-
tokenId: hexToBytes(tokenId),
|
|
1085
|
-
shardPlanCommitment: hexToBytes(ZERO_HASH),
|
|
1086
|
-
activationDigest: hexToBytes(ZERO_HASH),
|
|
1087
|
-
shardCount: 0n,
|
|
1088
|
-
};
|
|
1089
|
-
const bootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
1090
|
-
artifacts.bootstrap,
|
|
1091
|
-
beforeState,
|
|
1092
|
-
);
|
|
1093
|
-
const nextBootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
1094
|
-
artifacts.bootstrap,
|
|
1095
|
-
afterState,
|
|
1096
|
-
);
|
|
1097
|
-
const tokenScript = buildKcc20ScriptForState(artifacts.kcc20, tokenState);
|
|
1098
|
-
const bootstrapSource = sourceDescriptor(
|
|
1099
|
-
params.bootstrapSource,
|
|
1100
|
-
"bootstrap source",
|
|
1101
|
-
scriptAddress(bootstrapScript, network),
|
|
1102
|
-
);
|
|
1103
|
-
const tokenSource = sourceDescriptor(
|
|
1104
|
-
params.tokenSource,
|
|
1105
|
-
"token source",
|
|
1106
|
-
scriptAddress(tokenScript, network),
|
|
1107
|
-
);
|
|
1108
|
-
const rpc = rpcFor(network);
|
|
1109
|
-
await rpc.connect();
|
|
1110
|
-
try {
|
|
1111
|
-
const [bootstrapUtxos, tokenUtxos, walletUtxos] = await Promise.all([
|
|
1112
|
-
rpc.getUtxosByAddresses({ addresses: [bootstrapSource.address] }),
|
|
1113
|
-
rpc.getUtxosByAddresses({ addresses: [tokenSource.address] }),
|
|
1114
|
-
rpc.getUtxosByAddresses({ addresses: [walletAddress] }),
|
|
1115
|
-
]);
|
|
1116
|
-
const bootstrapEntry = findExactEntry(
|
|
1117
|
-
bootstrapUtxos.entries,
|
|
1118
|
-
bootstrapSource,
|
|
1119
|
-
);
|
|
1120
|
-
assertCovenantSource(bootstrapEntry, {
|
|
1121
|
-
covenantId: bootstrapId,
|
|
1122
|
-
script: bootstrapScript,
|
|
1123
|
-
amount: base.bootstrapCellKas,
|
|
1124
|
-
label: "bootstrap bind source",
|
|
1125
|
-
});
|
|
1126
|
-
const tokenEntry = findExactEntry(tokenUtxos.entries, tokenSource);
|
|
1127
|
-
assertCovenantSource(tokenEntry, {
|
|
1128
|
-
covenantId: tokenId,
|
|
1129
|
-
script: tokenScript,
|
|
1130
|
-
amount: base.tokenCellKas,
|
|
1131
|
-
label: "bootstrap bind token source",
|
|
1132
|
-
});
|
|
1133
|
-
const fundingEntry = selectFundingEntry(
|
|
1134
|
-
withoutOutpoints(walletUtxos.entries, [
|
|
1135
|
-
bootstrapEntry.outpoint,
|
|
1136
|
-
tokenEntry.outpoint,
|
|
1137
|
-
]),
|
|
1138
|
-
priorityFee() + 10_000n,
|
|
1139
|
-
[],
|
|
1140
|
-
params.fundingOutpoint,
|
|
1141
|
-
);
|
|
1142
|
-
const outputs = [
|
|
1143
|
-
new kaspaWasm.TransactionOutput(
|
|
1144
|
-
base.tokenCellKas,
|
|
1145
|
-
kaspaWasm.payToScriptHashScript(tokenScript),
|
|
1146
|
-
new kaspaWasm.CovenantBinding(1, new kaspaWasm.Hash(tokenId)),
|
|
1147
|
-
),
|
|
1148
|
-
new kaspaWasm.TransactionOutput(
|
|
1149
|
-
base.bootstrapCellKas,
|
|
1150
|
-
kaspaWasm.payToScriptHashScript(nextBootstrapScript),
|
|
1151
|
-
new kaspaWasm.CovenantBinding(0, new kaspaWasm.Hash(bootstrapId)),
|
|
1152
|
-
),
|
|
1153
|
-
];
|
|
1154
|
-
const fundingChangeOutputIndex = appendFundingChange(
|
|
1155
|
-
outputs,
|
|
1156
|
-
[bootstrapEntry, tokenEntry, fundingEntry],
|
|
1157
|
-
walletAddress,
|
|
1158
|
-
base.tokenCellKas + base.bootstrapCellKas,
|
|
1159
|
-
priorityFee(),
|
|
1160
|
-
);
|
|
1161
|
-
const transaction = new kaspaWasm.Transaction({
|
|
1162
|
-
version: 1,
|
|
1163
|
-
lockTime: 0n,
|
|
1164
|
-
inputs: [
|
|
1165
|
-
covenantInput(bootstrapEntry, BOOTSTRAP_COMPUTE_BUDGET),
|
|
1166
|
-
covenantInput(tokenEntry, KCC20_COMPUTE_BUDGET),
|
|
1167
|
-
publicInput(fundingEntry, PUBLIC_INPUT_COMPUTE_BUDGET),
|
|
1168
|
-
],
|
|
1169
|
-
outputs,
|
|
1170
|
-
subnetworkId: NATIVE_SUBNETWORK_ID,
|
|
1171
|
-
gas: 0n,
|
|
1172
|
-
payload: "",
|
|
1173
|
-
});
|
|
1174
|
-
setVersionOneInputMassFields(transaction, PUBLIC_INPUT_COMPUTE_BUDGET);
|
|
1175
|
-
transaction.inputs[0].computeBudget = BOOTSTRAP_COMPUTE_BUDGET;
|
|
1176
|
-
transaction.inputs[1].computeBudget = KCC20_COMPUTE_BUDGET;
|
|
1177
|
-
const recipes = bootstrapApprovalRecipes(
|
|
1178
|
-
bytesToHex(bootstrapScript),
|
|
1179
|
-
bootstrapBindArgs(artifacts, afterState, tokenState),
|
|
1180
|
-
);
|
|
1181
|
-
transaction.inputs[0].signatureScript = encodeP2sh(
|
|
1182
|
-
signatureScriptArgsBytes(recipes.placeholder.args),
|
|
1183
|
-
bootstrapScript,
|
|
1184
|
-
);
|
|
1185
|
-
transaction.inputs[1].signatureScript = encodeP2sh(
|
|
1186
|
-
buildKcc20TransferSigScript(kaspaWasm, artifacts.kcc20, {
|
|
1187
|
-
nextStates: [tokenState],
|
|
1188
|
-
witness: Uint8Array.of(0),
|
|
1189
|
-
}),
|
|
1190
|
-
tokenScript,
|
|
1191
|
-
);
|
|
1192
|
-
const adjusted = transactionWithCalculatedFeeChange(
|
|
1193
|
-
kaspaWasm,
|
|
1194
|
-
transaction,
|
|
1195
|
-
fundingChangeOutputIndex,
|
|
1196
|
-
network,
|
|
1197
|
-
"snapshot bootstrap bind",
|
|
1198
|
-
[recipes.approval],
|
|
1199
|
-
);
|
|
1200
|
-
return outputEnvelope(adjusted, [{ index: 2 }], [recipes.approval], {
|
|
1201
|
-
migrationId: requireHex32(params.migrationId, "migration ID"),
|
|
1202
|
-
stage: "bootstrap-bind",
|
|
1203
|
-
tokenId,
|
|
1204
|
-
bootstrapId,
|
|
1205
|
-
bootstrapState: {
|
|
1206
|
-
...bootstrapStateReceipt(afterState),
|
|
1207
|
-
phase: "TOKEN_BOUND",
|
|
1208
|
-
},
|
|
1209
|
-
maximumFeeSompi: feeOf(adjusted).toString(),
|
|
1210
|
-
intentInputs: [
|
|
1211
|
-
{
|
|
1212
|
-
role: "snapshot-bootstrap",
|
|
1213
|
-
index: 0,
|
|
1214
|
-
covenantId: bootstrapId,
|
|
1215
|
-
revealScriptHex: bytesToHex(bootstrapScript),
|
|
1216
|
-
},
|
|
1217
|
-
{
|
|
1218
|
-
role: "fixed-supply-token",
|
|
1219
|
-
index: 1,
|
|
1220
|
-
covenantId: tokenId,
|
|
1221
|
-
revealScriptHex: bytesToHex(tokenScript),
|
|
1222
|
-
staticSignatureScriptHex: signatureScriptHex(adjusted, 1),
|
|
1223
|
-
},
|
|
1224
|
-
{ role: "creator-funding", index: 2 },
|
|
1225
|
-
],
|
|
1226
|
-
intentOutputs: [
|
|
1227
|
-
{
|
|
1228
|
-
role: "fixed-supply-token",
|
|
1229
|
-
index: 0,
|
|
1230
|
-
covenantId: tokenId,
|
|
1231
|
-
revealScriptHex: bytesToHex(tokenScript),
|
|
1232
|
-
owner: bootstrapId,
|
|
1233
|
-
ownerScheme: 4,
|
|
1234
|
-
borrowScheme: 0,
|
|
1235
|
-
borrowGuard: bytesToHex(tokenState.borrowGuard),
|
|
1236
|
-
extensionCommitment: bytesToHex(tokenState.extensionCommitment),
|
|
1237
|
-
tokenAmount: tokenState.amount.toString(),
|
|
1238
|
-
},
|
|
1239
|
-
{
|
|
1240
|
-
role: "snapshot-bootstrap",
|
|
1241
|
-
index: 1,
|
|
1242
|
-
covenantId: bootstrapId,
|
|
1243
|
-
revealScriptHex: bytesToHex(nextBootstrapScript),
|
|
1244
|
-
},
|
|
1245
|
-
],
|
|
1246
|
-
});
|
|
1247
|
-
} finally {
|
|
1248
|
-
await rpc.disconnect();
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
async function buildBootstrapSeal(input) {
|
|
1253
|
-
const request = input.request || {};
|
|
1254
|
-
const params = request.params || {};
|
|
1255
|
-
const network = networkOf(request);
|
|
1256
|
-
const walletAddress = walletAddressOf(request);
|
|
1257
|
-
const artifacts = await readPinnedArtifacts(
|
|
1258
|
-
params.artifactPins,
|
|
1259
|
-
false,
|
|
1260
|
-
true,
|
|
1261
|
-
);
|
|
1262
|
-
const template = templateParts(artifacts.kcc20);
|
|
1263
|
-
const base = bootstrapBaseState(params, template);
|
|
1264
|
-
const migrationId = requireHex32(params.migrationId, "migration ID");
|
|
1265
|
-
const bootstrapId = requireHex32(params.bootstrapId, "bootstrap ID");
|
|
1266
|
-
const tokenId = requireHex32(params.tokenId, "token ID");
|
|
1267
|
-
const shards = reserveShards(params.shards, base.totalSupply);
|
|
1268
|
-
const shardPlanCommitment = snapshotShardPlanCommitment({
|
|
1269
|
-
migrationCommitment: migrationId,
|
|
1270
|
-
manifestCommitment: bytesToHex(base.manifestCommitment),
|
|
1271
|
-
tokenId,
|
|
1272
|
-
totalSupply: base.totalSupply,
|
|
1273
|
-
shards,
|
|
1274
|
-
});
|
|
1275
|
-
const beforeState = {
|
|
1276
|
-
...base,
|
|
1277
|
-
phase: 1n,
|
|
1278
|
-
tokenId: hexToBytes(tokenId),
|
|
1279
|
-
shardPlanCommitment: hexToBytes(ZERO_HASH),
|
|
1280
|
-
activationDigest: hexToBytes(ZERO_HASH),
|
|
1281
|
-
shardCount: 0n,
|
|
1282
|
-
};
|
|
1283
|
-
const afterState = {
|
|
1284
|
-
...base,
|
|
1285
|
-
phase: 2n,
|
|
1286
|
-
tokenId: hexToBytes(tokenId),
|
|
1287
|
-
shardPlanCommitment: hexToBytes(shardPlanCommitment),
|
|
1288
|
-
activationDigest: hexToBytes(
|
|
1289
|
-
requireHex32(params.activationDigest, "activation digest"),
|
|
1290
|
-
),
|
|
1291
|
-
shardCount: BigInt(shards.length),
|
|
1292
|
-
};
|
|
1293
|
-
const bootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
1294
|
-
artifacts.bootstrap,
|
|
1295
|
-
beforeState,
|
|
1296
|
-
);
|
|
1297
|
-
const nextBootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
1298
|
-
artifacts.bootstrap,
|
|
1299
|
-
afterState,
|
|
1300
|
-
);
|
|
1301
|
-
const bootstrapSource = sourceDescriptor(
|
|
1302
|
-
params.bootstrapSource,
|
|
1303
|
-
"bootstrap source",
|
|
1304
|
-
scriptAddress(bootstrapScript, network),
|
|
1305
|
-
);
|
|
1306
|
-
const rpc = rpcFor(network);
|
|
1307
|
-
await rpc.connect();
|
|
1308
|
-
try {
|
|
1309
|
-
const [bootstrapUtxos, walletUtxos] = await Promise.all([
|
|
1310
|
-
rpc.getUtxosByAddresses({ addresses: [bootstrapSource.address] }),
|
|
1311
|
-
rpc.getUtxosByAddresses({ addresses: [walletAddress] }),
|
|
1312
|
-
]);
|
|
1313
|
-
const bootstrapEntry = findExactEntry(
|
|
1314
|
-
bootstrapUtxos.entries,
|
|
1315
|
-
bootstrapSource,
|
|
1316
|
-
);
|
|
1317
|
-
assertCovenantSource(bootstrapEntry, {
|
|
1318
|
-
covenantId: bootstrapId,
|
|
1319
|
-
script: bootstrapScript,
|
|
1320
|
-
amount: base.bootstrapCellKas,
|
|
1321
|
-
label: "bootstrap seal source",
|
|
1322
|
-
});
|
|
1323
|
-
const fundingEntry = selectFundingEntry(
|
|
1324
|
-
withoutOutpoints(walletUtxos.entries, [bootstrapEntry.outpoint]),
|
|
1325
|
-
priorityFee() + 10_000n,
|
|
1326
|
-
[],
|
|
1327
|
-
params.fundingOutpoint,
|
|
1328
|
-
);
|
|
1329
|
-
const outputs = [
|
|
1330
|
-
new kaspaWasm.TransactionOutput(
|
|
1331
|
-
base.bootstrapCellKas,
|
|
1332
|
-
kaspaWasm.payToScriptHashScript(nextBootstrapScript),
|
|
1333
|
-
new kaspaWasm.CovenantBinding(0, new kaspaWasm.Hash(bootstrapId)),
|
|
1334
|
-
),
|
|
1335
|
-
];
|
|
1336
|
-
const fundingChangeOutputIndex = appendFundingChange(
|
|
1337
|
-
outputs,
|
|
1338
|
-
[bootstrapEntry, fundingEntry],
|
|
1339
|
-
walletAddress,
|
|
1340
|
-
base.bootstrapCellKas,
|
|
1341
|
-
priorityFee(),
|
|
1342
|
-
);
|
|
1343
|
-
const transaction = new kaspaWasm.Transaction({
|
|
1344
|
-
version: 1,
|
|
1345
|
-
lockTime: 0n,
|
|
1346
|
-
inputs: [
|
|
1347
|
-
covenantInput(bootstrapEntry, BOOTSTRAP_COMPUTE_BUDGET),
|
|
1348
|
-
publicInput(fundingEntry, PUBLIC_INPUT_COMPUTE_BUDGET),
|
|
1349
|
-
],
|
|
1350
|
-
outputs,
|
|
1351
|
-
subnetworkId: NATIVE_SUBNETWORK_ID,
|
|
1352
|
-
gas: 0n,
|
|
1353
|
-
payload: "",
|
|
1354
|
-
});
|
|
1355
|
-
setVersionOneInputMassFields(transaction, PUBLIC_INPUT_COMPUTE_BUDGET);
|
|
1356
|
-
transaction.inputs[0].computeBudget = BOOTSTRAP_COMPUTE_BUDGET;
|
|
1357
|
-
const recipes = bootstrapApprovalRecipes(
|
|
1358
|
-
bytesToHex(bootstrapScript),
|
|
1359
|
-
bootstrapSealArgs(artifacts, afterState),
|
|
1360
|
-
);
|
|
1361
|
-
transaction.inputs[0].signatureScript = encodeP2sh(
|
|
1362
|
-
signatureScriptArgsBytes(recipes.placeholder.args),
|
|
1363
|
-
bootstrapScript,
|
|
1364
|
-
);
|
|
1365
|
-
const adjusted = transactionWithCalculatedFeeChange(
|
|
1366
|
-
kaspaWasm,
|
|
1367
|
-
transaction,
|
|
1368
|
-
fundingChangeOutputIndex,
|
|
1369
|
-
network,
|
|
1370
|
-
"snapshot bootstrap seal",
|
|
1371
|
-
[recipes.approval],
|
|
1372
|
-
);
|
|
1373
|
-
return outputEnvelope(adjusted, [{ index: 1 }], [recipes.approval], {
|
|
1374
|
-
migrationId,
|
|
1375
|
-
stage: "bootstrap-seal",
|
|
1376
|
-
tokenId,
|
|
1377
|
-
bootstrapId,
|
|
1378
|
-
bootstrapState: {
|
|
1379
|
-
...bootstrapStateReceipt(afterState),
|
|
1380
|
-
phase: "SEALED",
|
|
1381
|
-
},
|
|
1382
|
-
shardPlanCommitment,
|
|
1383
|
-
activationDigest: bytesToHex(afterState.activationDigest),
|
|
1384
|
-
shards: shards.map((shard) => shard.shardIndex),
|
|
1385
|
-
maximumFeeSompi: feeOf(adjusted).toString(),
|
|
1386
|
-
intentInputs: [
|
|
1387
|
-
{
|
|
1388
|
-
role: "snapshot-bootstrap",
|
|
1389
|
-
index: 0,
|
|
1390
|
-
covenantId: bootstrapId,
|
|
1391
|
-
revealScriptHex: bytesToHex(bootstrapScript),
|
|
1392
|
-
},
|
|
1393
|
-
{ role: "creator-funding", index: 1 },
|
|
1394
|
-
],
|
|
1395
|
-
intentOutputs: [
|
|
1396
|
-
{
|
|
1397
|
-
role: "snapshot-bootstrap",
|
|
1398
|
-
index: 0,
|
|
1399
|
-
covenantId: bootstrapId,
|
|
1400
|
-
revealScriptHex: bytesToHex(nextBootstrapScript),
|
|
1401
|
-
},
|
|
1402
|
-
],
|
|
1403
|
-
});
|
|
1404
|
-
} finally {
|
|
1405
|
-
await rpc.disconnect();
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
async function buildBootstrapGenesis(input) {
|
|
1410
|
-
const request = input.request || {};
|
|
1411
|
-
const params = request.params || {};
|
|
1412
|
-
const network = networkOf(request);
|
|
1413
|
-
const walletAddress = walletAddressOf(request);
|
|
1414
|
-
const [kcc20, bootstrap] = await Promise.all([
|
|
1415
|
-
readPinnedArtifact(
|
|
1416
|
-
KCC20_ARTIFACT_PATH,
|
|
1417
|
-
params.artifactPins?.kcc20ArtifactSha256,
|
|
1418
|
-
"KCC20",
|
|
1419
|
-
),
|
|
1420
|
-
readPinnedArtifact(
|
|
1421
|
-
BOOTSTRAP_ARTIFACT_PATH,
|
|
1422
|
-
params.artifactPins?.bootstrapArtifactSha256,
|
|
1423
|
-
"KCC20SnapshotBootstrap",
|
|
1424
|
-
),
|
|
1425
|
-
]);
|
|
1426
|
-
const template = templateParts(kcc20);
|
|
1427
|
-
const expectedTemplateHash = requireHex32(
|
|
1428
|
-
params.expectedKcc20TemplateHash,
|
|
1429
|
-
"KCC20 template hash",
|
|
1430
|
-
);
|
|
1431
|
-
if (bytesToHex(template.hash) !== expectedTemplateHash) {
|
|
1432
|
-
throw new Error("KCC20 template hash does not match the migration pin");
|
|
1433
|
-
}
|
|
1434
|
-
const approvalKey = requireHex32(
|
|
1435
|
-
params.approvalKey ?? params.approvalKeyA,
|
|
1436
|
-
"approval key",
|
|
1437
|
-
);
|
|
1438
|
-
if (approvalKey === ZERO_HASH) {
|
|
1439
|
-
throw new Error("bootstrap approval key must be nonzero");
|
|
1440
|
-
}
|
|
1441
|
-
const state = {
|
|
1442
|
-
migrationCommitment: hexToBytes(
|
|
1443
|
-
requireHex32(params.migrationCommitment, "migration commitment"),
|
|
1444
|
-
),
|
|
1445
|
-
manifestCommitment: hexToBytes(
|
|
1446
|
-
requireHex32(params.manifestCommitment, "manifest commitment"),
|
|
1447
|
-
),
|
|
1448
|
-
holderExtensionCommitment: hexToBytes(
|
|
1449
|
-
requireHex32(
|
|
1450
|
-
params.holderExtensionCommitment,
|
|
1451
|
-
"holder extension commitment",
|
|
1452
|
-
),
|
|
1453
|
-
),
|
|
1454
|
-
totalSupply: positiveI64(params.totalSupply, "total supply"),
|
|
1455
|
-
claimNotBeforeDaaScore: positiveI64(
|
|
1456
|
-
params.claimNotBeforeDaaScore,
|
|
1457
|
-
"claim-not-before DAA score",
|
|
1458
|
-
),
|
|
1459
|
-
kcc20TemplateHash: hexToBytes(expectedTemplateHash),
|
|
1460
|
-
kcc20TemplatePrefixLen: BigInt(template.prefix.length),
|
|
1461
|
-
kcc20TemplateSuffixLen: BigInt(template.suffix.length),
|
|
1462
|
-
tokenCellKas: minimumCell(params.tokenCellKas, "token cell KAS"),
|
|
1463
|
-
reserveCellKas: minimumCell(params.reserveCellKas, "reserve cell KAS"),
|
|
1464
|
-
bootstrapCellKas: minimumCell(
|
|
1465
|
-
params.bootstrapCellKas,
|
|
1466
|
-
"bootstrap cell KAS",
|
|
1467
|
-
),
|
|
1468
|
-
approvalKey: hexToBytes(approvalKey),
|
|
1469
|
-
phase: 0n,
|
|
1470
|
-
tokenId: hexToBytes(ZERO_HASH),
|
|
1471
|
-
shardPlanCommitment: hexToBytes(ZERO_HASH),
|
|
1472
|
-
activationDigest: hexToBytes(ZERO_HASH),
|
|
1473
|
-
shardCount: 0n,
|
|
1474
|
-
};
|
|
1475
|
-
if (state.claimNotBeforeDaaScore >= 500_000_000_000n) {
|
|
1476
|
-
throw new Error("claim-not-before must use DAA-score locktime mode");
|
|
1477
|
-
}
|
|
1478
|
-
const bootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
1479
|
-
bootstrap,
|
|
1480
|
-
state,
|
|
1481
|
-
);
|
|
1482
|
-
const rpc = rpcFor(network);
|
|
1483
|
-
await rpc.connect();
|
|
1484
|
-
try {
|
|
1485
|
-
const walletUtxos = await rpc.getUtxosByAddresses({
|
|
1486
|
-
addresses: [walletAddress],
|
|
1487
|
-
});
|
|
1488
|
-
const fundingEntry = selectFundingEntry(
|
|
1489
|
-
walletUtxos.entries,
|
|
1490
|
-
state.bootstrapCellKas + priorityFee() + 10_000n,
|
|
1491
|
-
[],
|
|
1492
|
-
params.fundingOutpoint,
|
|
1493
|
-
);
|
|
1494
|
-
const outputs = [
|
|
1495
|
-
new kaspaWasm.TransactionOutput(
|
|
1496
|
-
state.bootstrapCellKas,
|
|
1497
|
-
kaspaWasm.payToScriptHashScript(bootstrapScript),
|
|
1498
|
-
),
|
|
1499
|
-
];
|
|
1500
|
-
const changeIndex = appendFundingChange(
|
|
1501
|
-
outputs,
|
|
1502
|
-
[fundingEntry],
|
|
1503
|
-
walletAddress,
|
|
1504
|
-
state.bootstrapCellKas,
|
|
1505
|
-
priorityFee(),
|
|
1506
|
-
);
|
|
1507
|
-
const stateReceipt = bootstrapStateReceipt(state);
|
|
1508
|
-
const transaction = new kaspaWasm.Transaction({
|
|
1509
|
-
version: 1,
|
|
1510
|
-
lockTime: 0n,
|
|
1511
|
-
inputs: [publicInput(fundingEntry, PUBLIC_INPUT_COMPUTE_BUDGET)],
|
|
1512
|
-
outputs,
|
|
1513
|
-
subnetworkId: NATIVE_SUBNETWORK_ID,
|
|
1514
|
-
gas: 0n,
|
|
1515
|
-
payload: bytesToHex(
|
|
1516
|
-
new TextEncoder().encode(
|
|
1517
|
-
JSON.stringify({
|
|
1518
|
-
tn10: {
|
|
1519
|
-
v: 1,
|
|
1520
|
-
tmpl: "KCC20SnapshotBootstrap",
|
|
1521
|
-
args: [
|
|
1522
|
-
{
|
|
1523
|
-
name: "bootstrapState",
|
|
1524
|
-
type: "object",
|
|
1525
|
-
value: stateReceipt,
|
|
1526
|
-
},
|
|
1527
|
-
],
|
|
1528
|
-
},
|
|
1529
|
-
}),
|
|
1530
|
-
),
|
|
1531
|
-
),
|
|
1532
|
-
});
|
|
1533
|
-
setVersionOneInputMassFields(transaction, PUBLIC_INPUT_COMPUTE_BUDGET);
|
|
1534
|
-
transaction.populateGenesisCovenants([
|
|
1535
|
-
new kaspaWasm.GenesisCovenantGroup(0, [0]),
|
|
1536
|
-
]);
|
|
1537
|
-
transaction.finalize();
|
|
1538
|
-
const covenantId = requireHex32(
|
|
1539
|
-
transaction.outputs[0]?.covenant?.covenantId?.toString(),
|
|
1540
|
-
"bootstrap covenant id",
|
|
1541
|
-
);
|
|
1542
|
-
const adjusted = transactionWithCalculatedFeeChange(
|
|
1543
|
-
kaspaWasm,
|
|
1544
|
-
transaction,
|
|
1545
|
-
changeIndex,
|
|
1546
|
-
network,
|
|
1547
|
-
"snapshot bootstrap genesis",
|
|
1548
|
-
);
|
|
1549
|
-
return outputEnvelope(adjusted, [{ index: 0 }], [], {
|
|
1550
|
-
migrationId: requireHex32(params.migrationId, "migration id"),
|
|
1551
|
-
stage: "bootstrap-deploy",
|
|
1552
|
-
covenantId,
|
|
1553
|
-
bootstrapState: { ...stateReceipt, phase: "UNBOUND" },
|
|
1554
|
-
maximumFeeSompi: feeOf(adjusted).toString(),
|
|
1555
|
-
intentInputs: [{ role: "creator-funding", index: 0 }],
|
|
1556
|
-
intentOutputs: [
|
|
1557
|
-
{
|
|
1558
|
-
role: "snapshot-bootstrap",
|
|
1559
|
-
index: 0,
|
|
1560
|
-
covenantId,
|
|
1561
|
-
revealScriptHex: bytesToHex(bootstrapScript),
|
|
1562
|
-
},
|
|
1563
|
-
],
|
|
1564
|
-
});
|
|
1565
|
-
} finally {
|
|
1566
|
-
await rpc.disconnect();
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
async function buildControllerGenesis(input) {
|
|
1571
|
-
const request = input.request || {};
|
|
1572
|
-
const params = request.params || {};
|
|
1573
|
-
const network = networkOf(request);
|
|
1574
|
-
const walletAddress = walletAddressOf(request);
|
|
1575
|
-
const artifacts = await readPinnedArtifacts(params.artifactPins, true);
|
|
1576
|
-
const definition = snapshotDefinition(params, artifacts);
|
|
1577
|
-
const controllerState = initialControllerState(definition);
|
|
1578
|
-
const controllerScript = buildSnapshotControllerScriptForState(
|
|
1579
|
-
artifacts.controller,
|
|
1580
|
-
controllerState,
|
|
1581
|
-
);
|
|
1582
|
-
const outputSompi = definition.controllerCellKas;
|
|
1583
|
-
const rpc = rpcFor(network);
|
|
1584
|
-
await rpc.connect();
|
|
1585
|
-
try {
|
|
1586
|
-
const walletUtxos = await rpc.getUtxosByAddresses({
|
|
1587
|
-
addresses: [walletAddress],
|
|
1588
|
-
});
|
|
1589
|
-
const fundingEntry = selectFundingEntry(
|
|
1590
|
-
walletUtxos.entries,
|
|
1591
|
-
outputSompi + priorityFee() + 10_000n,
|
|
1592
|
-
[],
|
|
1593
|
-
params.fundingOutpoint,
|
|
1594
|
-
);
|
|
1595
|
-
const outputs = [
|
|
1596
|
-
new kaspaWasm.TransactionOutput(
|
|
1597
|
-
outputSompi,
|
|
1598
|
-
kaspaWasm.payToScriptHashScript(controllerScript),
|
|
1599
|
-
),
|
|
1600
|
-
];
|
|
1601
|
-
const fundingChangeOutputIndex = appendFundingChange(
|
|
1602
|
-
outputs,
|
|
1603
|
-
[fundingEntry],
|
|
1604
|
-
walletAddress,
|
|
1605
|
-
outputSompi,
|
|
1606
|
-
priorityFee(),
|
|
1607
|
-
);
|
|
1608
|
-
const transaction = new kaspaWasm.Transaction({
|
|
1609
|
-
version: 1,
|
|
1610
|
-
lockTime: 0n,
|
|
1611
|
-
inputs: [publicInput(fundingEntry, PUBLIC_INPUT_COMPUTE_BUDGET)],
|
|
1612
|
-
outputs,
|
|
1613
|
-
subnetworkId: NATIVE_SUBNETWORK_ID,
|
|
1614
|
-
gas: 0n,
|
|
1615
|
-
payload: controllerDeploymentPayload(definition, controllerState),
|
|
1616
|
-
});
|
|
1617
|
-
setVersionOneInputMassFields(transaction, PUBLIC_INPUT_COMPUTE_BUDGET);
|
|
1618
|
-
transaction.populateGenesisCovenants([
|
|
1619
|
-
new kaspaWasm.GenesisCovenantGroup(0, [0]),
|
|
1620
|
-
]);
|
|
1621
|
-
transaction.finalize();
|
|
1622
|
-
const controllerId = requireHex32(
|
|
1623
|
-
transaction.outputs[0]?.covenant?.covenantId?.toString(),
|
|
1624
|
-
"controller covenant id",
|
|
1625
|
-
);
|
|
1626
|
-
const adjusted = transactionWithCalculatedFeeChange(
|
|
1627
|
-
kaspaWasm,
|
|
1628
|
-
transaction,
|
|
1629
|
-
fundingChangeOutputIndex,
|
|
1630
|
-
network,
|
|
1631
|
-
"snapshot controller genesis",
|
|
1632
|
-
);
|
|
1633
|
-
return outputEnvelope(adjusted, [{ index: 0 }], [], {
|
|
1634
|
-
migrationId: definition.migrationId,
|
|
1635
|
-
stage: "controller-deploy",
|
|
1636
|
-
controllerId,
|
|
1637
|
-
shard: definition.shard.shardIndex,
|
|
1638
|
-
controllerStateCommitment:
|
|
1639
|
-
snapshotControllerStateCommitment(controllerState),
|
|
1640
|
-
holderExtensionCommitment: definition.holderExtensionCommitment,
|
|
1641
|
-
maximumFeeSompi: feeOf(adjusted).toString(),
|
|
1642
|
-
intentInputs: [{ role: "creator-funding", index: 0 }],
|
|
1643
|
-
intentOutputs: [
|
|
1644
|
-
{
|
|
1645
|
-
role: "snapshot-controller",
|
|
1646
|
-
index: 0,
|
|
1647
|
-
shard: definition.shard.shardIndex,
|
|
1648
|
-
covenantId: controllerId,
|
|
1649
|
-
revealScriptHex: bytesToHex(controllerScript),
|
|
1650
|
-
},
|
|
1651
|
-
],
|
|
1652
|
-
});
|
|
1653
|
-
} finally {
|
|
1654
|
-
await rpc.disconnect();
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
async function buildReserveHandoff(input) {
|
|
1659
|
-
const request = input.request || {};
|
|
1660
|
-
const params = request.params || {};
|
|
1661
|
-
const network = networkOf(request);
|
|
1662
|
-
const walletAddress = walletAddressOf(request);
|
|
1663
|
-
const migrationId = requireHex32(params.migrationId, "migration id");
|
|
1664
|
-
const artifacts = await readPinnedArtifacts(
|
|
1665
|
-
params.artifactPins,
|
|
1666
|
-
false,
|
|
1667
|
-
true,
|
|
1668
|
-
);
|
|
1669
|
-
const bootstrapId = requireHex32(
|
|
1670
|
-
params.bootstrapId,
|
|
1671
|
-
"bootstrap covenant id",
|
|
1672
|
-
);
|
|
1673
|
-
const tokenId = requireHex32(params.tokenId, "token covenant id");
|
|
1674
|
-
const extensionCommitment = requireHex32(
|
|
1675
|
-
params.holderExtensionCommitment,
|
|
1676
|
-
"holder extension commitment",
|
|
1677
|
-
);
|
|
1678
|
-
const totalSupply = positiveI64(params.totalSupply, "total supply");
|
|
1679
|
-
const tokenState = kcc20State(params.tokenSource?.state);
|
|
1680
|
-
if (
|
|
1681
|
-
tokenState.amount !== totalSupply ||
|
|
1682
|
-
tokenState.ownerScheme !== 4 ||
|
|
1683
|
-
bytesToHex(tokenState.owner) !== bootstrapId ||
|
|
1684
|
-
bytesToHex(tokenState.extensionCommitment) !== extensionCommitment
|
|
1685
|
-
) {
|
|
1686
|
-
throw new Error(
|
|
1687
|
-
"reserve handoff token source does not match the migration",
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
const shards = reserveShards(params.shards, totalSupply);
|
|
1691
|
-
const template = templateParts(artifacts.kcc20);
|
|
1692
|
-
const expectedTemplateHash = requireHex32(
|
|
1693
|
-
params.kcc20TemplateHash,
|
|
1694
|
-
"KCC20 template hash",
|
|
1695
|
-
);
|
|
1696
|
-
if (bytesToHex(template.hash) !== expectedTemplateHash) {
|
|
1697
|
-
throw new Error("KCC20 template hash does not match the migration pin");
|
|
1698
|
-
}
|
|
1699
|
-
const shardPlanCommitment = snapshotShardPlanCommitment({
|
|
1700
|
-
migrationCommitment: migrationId,
|
|
1701
|
-
manifestCommitment: requireHex32(
|
|
1702
|
-
params.manifestCommitment,
|
|
1703
|
-
"manifest commitment",
|
|
1704
|
-
),
|
|
1705
|
-
tokenId,
|
|
1706
|
-
totalSupply,
|
|
1707
|
-
shards,
|
|
1708
|
-
});
|
|
1709
|
-
if (
|
|
1710
|
-
shardPlanCommitment !==
|
|
1711
|
-
requireHex32(
|
|
1712
|
-
params.bootstrapShardPlanCommitment,
|
|
1713
|
-
"bootstrap shard plan commitment",
|
|
1714
|
-
)
|
|
1715
|
-
) {
|
|
1716
|
-
throw new Error("activation shards do not match the sealed bootstrap");
|
|
1717
|
-
}
|
|
1718
|
-
const bootstrapState = {
|
|
1719
|
-
migrationCommitment: hexToBytes(migrationId),
|
|
1720
|
-
manifestCommitment: hexToBytes(
|
|
1721
|
-
requireHex32(params.manifestCommitment, "manifest commitment"),
|
|
1722
|
-
),
|
|
1723
|
-
holderExtensionCommitment: hexToBytes(extensionCommitment),
|
|
1724
|
-
totalSupply,
|
|
1725
|
-
claimNotBeforeDaaScore: positiveI64(
|
|
1726
|
-
params.claimNotBeforeDaaScore,
|
|
1727
|
-
"claim-not-before DAA score",
|
|
1728
|
-
),
|
|
1729
|
-
kcc20TemplateHash: hexToBytes(expectedTemplateHash),
|
|
1730
|
-
kcc20TemplatePrefixLen: BigInt(template.prefix.length),
|
|
1731
|
-
kcc20TemplateSuffixLen: BigInt(template.suffix.length),
|
|
1732
|
-
tokenCellKas: minimumCell(params.tokenCellKas, "token cell KAS"),
|
|
1733
|
-
reserveCellKas: minimumCell(params.reserveCellKas, "reserve cell KAS"),
|
|
1734
|
-
bootstrapCellKas: minimumCell(
|
|
1735
|
-
params.bootstrapCellKas,
|
|
1736
|
-
"bootstrap cell KAS",
|
|
1737
|
-
),
|
|
1738
|
-
approvalKey: hexToBytes(
|
|
1739
|
-
requireHex32(params.approvalKey ?? params.approvalKeyA, "approval key"),
|
|
1740
|
-
),
|
|
1741
|
-
phase: 2n,
|
|
1742
|
-
tokenId: hexToBytes(tokenId),
|
|
1743
|
-
shardPlanCommitment: hexToBytes(shardPlanCommitment),
|
|
1744
|
-
activationDigest: hexToBytes(
|
|
1745
|
-
requireHex32(
|
|
1746
|
-
params.bootstrapActivationDigest,
|
|
1747
|
-
"bootstrap activation digest",
|
|
1748
|
-
),
|
|
1749
|
-
),
|
|
1750
|
-
shardCount: BigInt(shards.length),
|
|
1751
|
-
};
|
|
1752
|
-
const bootstrapScript = buildSnapshotBootstrapScriptForState(
|
|
1753
|
-
artifacts.bootstrap,
|
|
1754
|
-
bootstrapState,
|
|
1755
|
-
);
|
|
1756
|
-
const bootstrapAddress = scriptAddress(bootstrapScript, network);
|
|
1757
|
-
const bootstrapSource = sourceDescriptor(
|
|
1758
|
-
params.bootstrapSource,
|
|
1759
|
-
"bootstrap source",
|
|
1760
|
-
bootstrapAddress,
|
|
1761
|
-
);
|
|
1762
|
-
const tokenScript = buildKcc20ScriptForState(artifacts.kcc20, tokenState);
|
|
1763
|
-
const tokenAddress = scriptAddress(tokenScript, network);
|
|
1764
|
-
const tokenSource = sourceDescriptor(
|
|
1765
|
-
params.tokenSource,
|
|
1766
|
-
"token source",
|
|
1767
|
-
tokenAddress,
|
|
1768
|
-
);
|
|
1769
|
-
const nextStates = shards.map((shard) => ({
|
|
1770
|
-
amount: shard.allocationTotal,
|
|
1771
|
-
owner: hexToBytes(shard.controllerId),
|
|
1772
|
-
ownerScheme: 4,
|
|
1773
|
-
borrowScheme: 0,
|
|
1774
|
-
borrowGuard: hexToBytes(ZERO_HASH),
|
|
1775
|
-
extensionCommitment: hexToBytes(extensionCommitment),
|
|
1776
|
-
}));
|
|
1777
|
-
const rpc = rpcFor(network);
|
|
1778
|
-
await rpc.connect();
|
|
1779
|
-
try {
|
|
1780
|
-
const [bootstrapUtxos, tokenUtxos, walletUtxos] = await Promise.all([
|
|
1781
|
-
rpc.getUtxosByAddresses({ addresses: [bootstrapAddress] }),
|
|
1782
|
-
rpc.getUtxosByAddresses({ addresses: [tokenAddress] }),
|
|
1783
|
-
rpc.getUtxosByAddresses({ addresses: [walletAddress] }),
|
|
1784
|
-
]);
|
|
1785
|
-
const bootstrapEntry = findExactEntry(
|
|
1786
|
-
bootstrapUtxos.entries,
|
|
1787
|
-
bootstrapSource,
|
|
1788
|
-
);
|
|
1789
|
-
assertCovenantSource(bootstrapEntry, {
|
|
1790
|
-
covenantId: bootstrapId,
|
|
1791
|
-
script: bootstrapScript,
|
|
1792
|
-
amount: bootstrapState.bootstrapCellKas,
|
|
1793
|
-
label: "reserve activation bootstrap source",
|
|
1794
|
-
});
|
|
1795
|
-
const tokenEntry = findExactEntry(tokenUtxos.entries, tokenSource);
|
|
1796
|
-
assertCovenantSource(tokenEntry, {
|
|
1797
|
-
covenantId: tokenId,
|
|
1798
|
-
script: tokenScript,
|
|
1799
|
-
amount: bootstrapState.tokenCellKas,
|
|
1800
|
-
label: "reserve handoff token source",
|
|
1801
|
-
});
|
|
1802
|
-
const fundingEntry = selectFundingEntry(
|
|
1803
|
-
withoutOutpoints(walletUtxos.entries, [
|
|
1804
|
-
bootstrapEntry.outpoint,
|
|
1805
|
-
tokenEntry.outpoint,
|
|
1806
|
-
]),
|
|
1807
|
-
priorityFee() + DEFAULT_CELL_SOMPI * BigInt(shards.length) + 10_000n,
|
|
1808
|
-
[],
|
|
1809
|
-
params.fundingOutpoint,
|
|
1810
|
-
);
|
|
1811
|
-
const outputs = nextStates.map(
|
|
1812
|
-
(state) =>
|
|
1813
|
-
new kaspaWasm.TransactionOutput(
|
|
1814
|
-
positiveI64(
|
|
1815
|
-
params.reserveCellKas ?? DEFAULT_CELL_SOMPI,
|
|
1816
|
-
"reserve cell KAS",
|
|
1817
|
-
),
|
|
1818
|
-
kaspaWasm.payToScriptHashScript(
|
|
1819
|
-
buildKcc20ScriptForState(artifacts.kcc20, state),
|
|
1820
|
-
),
|
|
1821
|
-
new kaspaWasm.CovenantBinding(1, new kaspaWasm.Hash(tokenId)),
|
|
1822
|
-
),
|
|
1823
|
-
);
|
|
1824
|
-
const outputSompi = outputs.reduce(
|
|
1825
|
-
(sum, output) => sum + BigInt(output.value),
|
|
1826
|
-
0n,
|
|
1827
|
-
);
|
|
1828
|
-
const fundingChangeOutputIndex = appendFundingChange(
|
|
1829
|
-
outputs,
|
|
1830
|
-
[bootstrapEntry, tokenEntry, fundingEntry],
|
|
1831
|
-
walletAddress,
|
|
1832
|
-
outputSompi,
|
|
1833
|
-
priorityFee(),
|
|
1834
|
-
);
|
|
1835
|
-
const transaction = new kaspaWasm.Transaction({
|
|
1836
|
-
version: 1,
|
|
1837
|
-
lockTime: 0n,
|
|
1838
|
-
inputs: [
|
|
1839
|
-
covenantInput(bootstrapEntry, BOOTSTRAP_COMPUTE_BUDGET),
|
|
1840
|
-
covenantInput(tokenEntry, KCC20_COMPUTE_BUDGET),
|
|
1841
|
-
publicInput(fundingEntry, PUBLIC_INPUT_COMPUTE_BUDGET),
|
|
1842
|
-
],
|
|
1843
|
-
outputs,
|
|
1844
|
-
subnetworkId: NATIVE_SUBNETWORK_ID,
|
|
1845
|
-
gas: 0n,
|
|
1846
|
-
payload: "",
|
|
686
|
+
});
|
|
687
|
+
const peerPlans = allShards.slice(1).map((peer) => {
|
|
688
|
+
if (!peer.controllerId)
|
|
689
|
+
throw new Error(
|
|
690
|
+
"deploy shard zero only after every peer controller is indexed",
|
|
691
|
+
);
|
|
692
|
+
const plan = createSignerlessControllerPlan({
|
|
693
|
+
...common,
|
|
694
|
+
shard: peer,
|
|
695
|
+
deploymentGuardCommitment: ZERO_HASH,
|
|
1847
696
|
});
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
697
|
+
return { controllerId: peer.controllerId, state: plan.controllerState };
|
|
698
|
+
});
|
|
699
|
+
const guard = snapshotPeerPlanCommitment({
|
|
700
|
+
migrationCommitment: common.migrationCommitment,
|
|
701
|
+
manifestCommitment: common.manifestCommitment,
|
|
702
|
+
shards: [
|
|
703
|
+
{ controllerId: "01".repeat(32), state: provisional.controllerState },
|
|
704
|
+
...peerPlans,
|
|
705
|
+
],
|
|
706
|
+
});
|
|
707
|
+
return createSignerlessControllerPlan({
|
|
708
|
+
...common,
|
|
709
|
+
shard,
|
|
710
|
+
deploymentGuardCommitment: guard,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function normalizedSignerlessShards(values) {
|
|
715
|
+
if (!Array.isArray(values) || values.length < 1 || values.length > 8) {
|
|
716
|
+
throw new Error("signerless snapshot requires 1..8 ordered shards");
|
|
717
|
+
}
|
|
718
|
+
const shards = values.map((value) => ({
|
|
719
|
+
...snapshotShard(value),
|
|
720
|
+
controllerId: value?.controllerId
|
|
721
|
+
? requireHex32(value.controllerId, "controller id")
|
|
722
|
+
: undefined,
|
|
723
|
+
controllerSource: value?.controllerSource,
|
|
724
|
+
}));
|
|
725
|
+
shards.sort((left, right) => left.shardIndex - right.shardIndex);
|
|
726
|
+
if (shards.some((shard, index) => shard.shardIndex !== index)) {
|
|
727
|
+
throw new Error(
|
|
728
|
+
"signerless snapshot shards must be contiguous from zero",
|
|
1877
729
|
);
|
|
1878
|
-
return outputEnvelope(adjusted, [{ index: 2 }], scripts, {
|
|
1879
|
-
migrationId,
|
|
1880
|
-
stage: "reserve-handoff",
|
|
1881
|
-
tokenId,
|
|
1882
|
-
bootstrapId,
|
|
1883
|
-
bootstrapState: {
|
|
1884
|
-
...bootstrapStateReceipt(bootstrapState),
|
|
1885
|
-
phase: "ACTIVATED",
|
|
1886
|
-
},
|
|
1887
|
-
shardPlanCommitment,
|
|
1888
|
-
activationDigest: bytesToHex(bootstrapState.activationDigest),
|
|
1889
|
-
shards: shards.map((shard) => shard.shardIndex),
|
|
1890
|
-
maximumFeeSompi: feeOf(adjusted).toString(),
|
|
1891
|
-
intentInputs: [
|
|
1892
|
-
{
|
|
1893
|
-
role: "snapshot-bootstrap",
|
|
1894
|
-
index: 0,
|
|
1895
|
-
covenantId: bootstrapId,
|
|
1896
|
-
revealScriptHex: bytesToHex(bootstrapScript),
|
|
1897
|
-
staticSignatureScriptHex: signatureScriptHex(adjusted, 0),
|
|
1898
|
-
},
|
|
1899
|
-
{
|
|
1900
|
-
role: "fixed-supply-token",
|
|
1901
|
-
index: 1,
|
|
1902
|
-
covenantId: tokenId,
|
|
1903
|
-
revealScriptHex: bytesToHex(tokenScript),
|
|
1904
|
-
staticSignatureScriptHex: signatureScriptHex(adjusted, 1),
|
|
1905
|
-
},
|
|
1906
|
-
{ role: "creator-funding", index: 2 },
|
|
1907
|
-
],
|
|
1908
|
-
intentOutputs: shards.map((shard, index) => ({
|
|
1909
|
-
role: "shard-reserve",
|
|
1910
|
-
index,
|
|
1911
|
-
shard: shard.shardIndex,
|
|
1912
|
-
covenantId: tokenId,
|
|
1913
|
-
revealScriptHex: bytesToHex(
|
|
1914
|
-
buildKcc20ScriptForState(artifacts.kcc20, nextStates[index]),
|
|
1915
|
-
),
|
|
1916
|
-
owner: shard.controllerId,
|
|
1917
|
-
ownerScheme: 4,
|
|
1918
|
-
borrowScheme: 0,
|
|
1919
|
-
borrowGuard: ZERO_HASH,
|
|
1920
|
-
extensionCommitment: bytesToHex(
|
|
1921
|
-
bootstrapState.holderExtensionCommitment,
|
|
1922
|
-
),
|
|
1923
|
-
tokenAmount: shard.allocationTotal.toString(),
|
|
1924
|
-
})),
|
|
1925
|
-
});
|
|
1926
|
-
} finally {
|
|
1927
|
-
await rpc.disconnect();
|
|
1928
730
|
}
|
|
731
|
+
return shards;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function portableInput(entry, network, script) {
|
|
735
|
+
return {
|
|
736
|
+
previousOutpoint: entry.outpoint,
|
|
737
|
+
utxo: entry,
|
|
738
|
+
networkId: network,
|
|
739
|
+
...(script ? { script } : {}),
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function outpointOfSource(source, label) {
|
|
744
|
+
return {
|
|
745
|
+
transactionId: requireHex32(source?.txidHex, `${label} txid`),
|
|
746
|
+
index: unsignedInteger(source?.vout, `${label} vout`),
|
|
747
|
+
};
|
|
1929
748
|
}
|
|
1930
749
|
|
|
1931
750
|
async function buildClaim(input) {
|
|
@@ -2413,278 +1232,6 @@ export function createKcc20SnapshotPsktBuilder(
|
|
|
2413
1232
|
};
|
|
2414
1233
|
}
|
|
2415
1234
|
|
|
2416
|
-
function initialControllerState(definition) {
|
|
2417
|
-
return {
|
|
2418
|
-
tokenId: hexToBytes(definition.tokenId),
|
|
2419
|
-
holderExtensionCommitment: hexToBytes(
|
|
2420
|
-
definition.holderExtensionCommitment,
|
|
2421
|
-
),
|
|
2422
|
-
manifestCommitment: hexToBytes(definition.manifestCommitment),
|
|
2423
|
-
snapshotContext: hexToBytes(definition.snapshotContext),
|
|
2424
|
-
kcc20TemplateHash: hexToBytes(definition.kcc20TemplateHash),
|
|
2425
|
-
claimNotBeforeDaaScore: definition.claimNotBeforeDaaScore,
|
|
2426
|
-
shardIndex: BigInt(definition.shard.shardIndex),
|
|
2427
|
-
globalIndexStart: BigInt(definition.shard.globalIndexStart),
|
|
2428
|
-
holderCount: BigInt(definition.shard.holderCount),
|
|
2429
|
-
treeDepth: BigInt(definition.shard.depth),
|
|
2430
|
-
kcc20TemplatePrefixLen: BigInt(definition.kcc20TemplatePrefixLen),
|
|
2431
|
-
kcc20TemplateSuffixLen: BigInt(definition.kcc20TemplateSuffixLen),
|
|
2432
|
-
controllerCellKas: definition.controllerCellKas,
|
|
2433
|
-
reserveCellKas: definition.reserveCellKas,
|
|
2434
|
-
recipientMinCellKas: definition.recipientMinCellKas,
|
|
2435
|
-
remainingRoot: hexToBytes(definition.shard.initialRoot),
|
|
2436
|
-
remainingAmount: definition.shard.allocationTotal,
|
|
2437
|
-
claimsProcessed: 0n,
|
|
2438
|
-
};
|
|
2439
|
-
}
|
|
2440
|
-
|
|
2441
|
-
function buildSnapshotControllerScriptForState(artifact, state) {
|
|
2442
|
-
const normalized = normalizeSilverScriptArtifact(artifact);
|
|
2443
|
-
const encoded = encodeControllerState(state);
|
|
2444
|
-
const start = normalized.state_layout?.start;
|
|
2445
|
-
const length = normalized.state_layout?.len;
|
|
2446
|
-
if (
|
|
2447
|
-
!Number.isInteger(start) ||
|
|
2448
|
-
!Number.isInteger(length) ||
|
|
2449
|
-
encoded.length !== length
|
|
2450
|
-
) {
|
|
2451
|
-
throw new Error("snapshot controller artifact state layout is invalid");
|
|
2452
|
-
}
|
|
2453
|
-
return concatBytes(
|
|
2454
|
-
Uint8Array.from(normalized.script).slice(0, start),
|
|
2455
|
-
encoded,
|
|
2456
|
-
Uint8Array.from(normalized.script).slice(start + length),
|
|
2457
|
-
);
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
function buildSnapshotBootstrapScriptForState(artifact, state) {
|
|
2461
|
-
const normalized = normalizeSilverScriptArtifact(artifact);
|
|
2462
|
-
const encoded = encodeBootstrapState(state);
|
|
2463
|
-
const start = normalized.state_layout?.start;
|
|
2464
|
-
const length = normalized.state_layout?.len;
|
|
2465
|
-
if (
|
|
2466
|
-
normalized.contract_name !== "KCC20SnapshotBootstrap" ||
|
|
2467
|
-
!Number.isInteger(start) ||
|
|
2468
|
-
!Number.isInteger(length) ||
|
|
2469
|
-
encoded.length !== length
|
|
2470
|
-
) {
|
|
2471
|
-
throw new Error("snapshot bootstrap artifact state layout is invalid");
|
|
2472
|
-
}
|
|
2473
|
-
return concatBytes(
|
|
2474
|
-
Uint8Array.from(normalized.script).slice(0, start),
|
|
2475
|
-
encoded,
|
|
2476
|
-
Uint8Array.from(normalized.script).slice(start + length),
|
|
2477
|
-
);
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
|
-
function encodeBootstrapState(state) {
|
|
2481
|
-
return concatBytes(
|
|
2482
|
-
pushData(state.migrationCommitment),
|
|
2483
|
-
pushData(state.manifestCommitment),
|
|
2484
|
-
pushData(state.holderExtensionCommitment),
|
|
2485
|
-
pushData(i64(state.totalSupply)),
|
|
2486
|
-
pushData(i64(state.claimNotBeforeDaaScore)),
|
|
2487
|
-
pushData(state.kcc20TemplateHash),
|
|
2488
|
-
pushData(i64(state.kcc20TemplatePrefixLen)),
|
|
2489
|
-
pushData(i64(state.kcc20TemplateSuffixLen)),
|
|
2490
|
-
pushData(i64(state.tokenCellKas)),
|
|
2491
|
-
pushData(i64(state.reserveCellKas)),
|
|
2492
|
-
pushData(i64(state.bootstrapCellKas)),
|
|
2493
|
-
pushData(state.approvalKey),
|
|
2494
|
-
pushData(i64(state.phase)),
|
|
2495
|
-
pushData(state.tokenId),
|
|
2496
|
-
pushData(state.shardPlanCommitment),
|
|
2497
|
-
pushData(state.activationDigest),
|
|
2498
|
-
pushData(i64(state.shardCount)),
|
|
2499
|
-
);
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
function bootstrapStateReceipt(state) {
|
|
2503
|
-
return {
|
|
2504
|
-
migrationCommitment: bytesToHex(state.migrationCommitment),
|
|
2505
|
-
manifestCommitment: bytesToHex(state.manifestCommitment),
|
|
2506
|
-
holderExtensionCommitment: bytesToHex(state.holderExtensionCommitment),
|
|
2507
|
-
totalSupply: state.totalSupply.toString(),
|
|
2508
|
-
claimNotBeforeDaaScore: state.claimNotBeforeDaaScore.toString(),
|
|
2509
|
-
kcc20TemplateHash: bytesToHex(state.kcc20TemplateHash),
|
|
2510
|
-
kcc20TemplatePrefixLen: state.kcc20TemplatePrefixLen.toString(),
|
|
2511
|
-
kcc20TemplateSuffixLen: state.kcc20TemplateSuffixLen.toString(),
|
|
2512
|
-
tokenCellKas: state.tokenCellKas.toString(),
|
|
2513
|
-
reserveCellKas: state.reserveCellKas.toString(),
|
|
2514
|
-
bootstrapCellKas: state.bootstrapCellKas.toString(),
|
|
2515
|
-
approvalKey: bytesToHex(state.approvalKey),
|
|
2516
|
-
phase: state.phase.toString(),
|
|
2517
|
-
tokenId: bytesToHex(state.tokenId),
|
|
2518
|
-
shardPlanCommitment: bytesToHex(state.shardPlanCommitment),
|
|
2519
|
-
activationDigest: bytesToHex(state.activationDigest),
|
|
2520
|
-
shardCount: state.shardCount.toString(),
|
|
2521
|
-
};
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
function bootstrapBaseState(params, template) {
|
|
2525
|
-
const approvalKey = requireHex32(
|
|
2526
|
-
params.approvalKey ?? params.approvalKeyA,
|
|
2527
|
-
"approval key",
|
|
2528
|
-
);
|
|
2529
|
-
if (approvalKey === ZERO_HASH) {
|
|
2530
|
-
throw new Error("bootstrap approval key must be nonzero");
|
|
2531
|
-
}
|
|
2532
|
-
const expectedTemplateHash = requireHex32(
|
|
2533
|
-
params.kcc20TemplateHash,
|
|
2534
|
-
"KCC20 template hash",
|
|
2535
|
-
);
|
|
2536
|
-
if (bytesToHex(template.hash) !== expectedTemplateHash) {
|
|
2537
|
-
throw new Error("KCC20 template hash does not match the migration pin");
|
|
2538
|
-
}
|
|
2539
|
-
const claimNotBeforeDaaScore = positiveI64(
|
|
2540
|
-
params.claimNotBeforeDaaScore,
|
|
2541
|
-
"claim-not-before DAA score",
|
|
2542
|
-
);
|
|
2543
|
-
if (claimNotBeforeDaaScore >= 500_000_000_000n) {
|
|
2544
|
-
throw new Error("claim-not-before must use DAA-score locktime mode");
|
|
2545
|
-
}
|
|
2546
|
-
return {
|
|
2547
|
-
migrationCommitment: hexToBytes(
|
|
2548
|
-
requireHex32(params.migrationId, "migration ID"),
|
|
2549
|
-
),
|
|
2550
|
-
manifestCommitment: hexToBytes(
|
|
2551
|
-
requireHex32(params.manifestCommitment, "manifest commitment"),
|
|
2552
|
-
),
|
|
2553
|
-
holderExtensionCommitment: hexToBytes(
|
|
2554
|
-
requireHex32(
|
|
2555
|
-
params.holderExtensionCommitment,
|
|
2556
|
-
"holder extension commitment",
|
|
2557
|
-
),
|
|
2558
|
-
),
|
|
2559
|
-
totalSupply: positiveI64(params.totalSupply, "total supply"),
|
|
2560
|
-
claimNotBeforeDaaScore,
|
|
2561
|
-
kcc20TemplateHash: hexToBytes(expectedTemplateHash),
|
|
2562
|
-
kcc20TemplatePrefixLen: BigInt(template.prefix.length),
|
|
2563
|
-
kcc20TemplateSuffixLen: BigInt(template.suffix.length),
|
|
2564
|
-
tokenCellKas: minimumCell(params.tokenCellKas, "token cell KAS"),
|
|
2565
|
-
reserveCellKas: minimumCell(params.reserveCellKas, "reserve cell KAS"),
|
|
2566
|
-
bootstrapCellKas: minimumCell(
|
|
2567
|
-
params.bootstrapCellKas,
|
|
2568
|
-
"bootstrap cell KAS",
|
|
2569
|
-
),
|
|
2570
|
-
approvalKey: hexToBytes(approvalKey),
|
|
2571
|
-
};
|
|
2572
|
-
}
|
|
2573
|
-
|
|
2574
|
-
function bootstrapStateArgs(state) {
|
|
2575
|
-
return [
|
|
2576
|
-
dataArg(state.migrationCommitment),
|
|
2577
|
-
dataArg(state.manifestCommitment),
|
|
2578
|
-
dataArg(state.holderExtensionCommitment),
|
|
2579
|
-
i64Arg(state.totalSupply),
|
|
2580
|
-
i64Arg(state.claimNotBeforeDaaScore),
|
|
2581
|
-
dataArg(state.kcc20TemplateHash),
|
|
2582
|
-
i64Arg(state.kcc20TemplatePrefixLen),
|
|
2583
|
-
i64Arg(state.kcc20TemplateSuffixLen),
|
|
2584
|
-
i64Arg(state.tokenCellKas),
|
|
2585
|
-
i64Arg(state.reserveCellKas),
|
|
2586
|
-
i64Arg(state.bootstrapCellKas),
|
|
2587
|
-
dataArg(state.approvalKey),
|
|
2588
|
-
i64Arg(state.phase),
|
|
2589
|
-
dataArg(state.tokenId),
|
|
2590
|
-
dataArg(state.shardPlanCommitment),
|
|
2591
|
-
dataArg(state.activationDigest),
|
|
2592
|
-
i64Arg(state.shardCount),
|
|
2593
|
-
];
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
function bootstrapBindArgs(artifacts, nextBootstrapState, tokenState) {
|
|
2597
|
-
const template = templateParts(artifacts.kcc20);
|
|
2598
|
-
return [
|
|
2599
|
-
...bootstrapStateArgs(nextBootstrapState),
|
|
2600
|
-
i64Arg(1),
|
|
2601
|
-
i64Arg(0),
|
|
2602
|
-
i64Arg(1),
|
|
2603
|
-
{ type: "approval" },
|
|
2604
|
-
dataArg(template.prefix),
|
|
2605
|
-
dataArg(template.suffix),
|
|
2606
|
-
...kcc20StateArgs(tokenState),
|
|
2607
|
-
dataArg(dispatchTagFor(artifacts.bootstrap, "bindToken")),
|
|
2608
|
-
];
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
function bootstrapSealArgs(artifacts, nextBootstrapState) {
|
|
2612
|
-
return [
|
|
2613
|
-
...bootstrapStateArgs(nextBootstrapState),
|
|
2614
|
-
i64Arg(0),
|
|
2615
|
-
{ type: "approval" },
|
|
2616
|
-
dataArg(dispatchTagFor(artifacts.bootstrap, "seal")),
|
|
2617
|
-
];
|
|
2618
|
-
}
|
|
2619
|
-
|
|
2620
|
-
function bootstrapCancellationArgs(
|
|
2621
|
-
artifacts,
|
|
2622
|
-
bound,
|
|
2623
|
-
sinkOutputIndex,
|
|
2624
|
-
sinkTokenState,
|
|
2625
|
-
) {
|
|
2626
|
-
return [
|
|
2627
|
-
{ type: "approval" },
|
|
2628
|
-
i64Arg(sinkOutputIndex),
|
|
2629
|
-
...(bound
|
|
2630
|
-
? [
|
|
2631
|
-
i64Arg(0),
|
|
2632
|
-
dataArg(templateParts(artifacts.kcc20).prefix),
|
|
2633
|
-
dataArg(templateParts(artifacts.kcc20).suffix),
|
|
2634
|
-
...kcc20StateArgs(sinkTokenState),
|
|
2635
|
-
]
|
|
2636
|
-
: []),
|
|
2637
|
-
dataArg(
|
|
2638
|
-
dispatchTagFor(
|
|
2639
|
-
artifacts.bootstrap,
|
|
2640
|
-
bound ? "cancelBound" : "cancelUnbound",
|
|
2641
|
-
),
|
|
2642
|
-
),
|
|
2643
|
-
];
|
|
2644
|
-
}
|
|
2645
|
-
|
|
2646
|
-
function bootstrapApprovalRecipes(scriptHex, args) {
|
|
2647
|
-
const zeroSignature = "00".repeat(65);
|
|
2648
|
-
const replace = (dynamicSlot) =>
|
|
2649
|
-
args.map((arg) => {
|
|
2650
|
-
if (arg.type === dynamicSlot) return { type: "signature" };
|
|
2651
|
-
if (arg.type === "approval") {
|
|
2652
|
-
return { type: "data", hex: zeroSignature };
|
|
2653
|
-
}
|
|
2654
|
-
return arg;
|
|
2655
|
-
});
|
|
2656
|
-
const script = (normalizedArgs) => ({
|
|
2657
|
-
inputIndex: 0,
|
|
2658
|
-
scriptHex,
|
|
2659
|
-
signatureScript: { mode: "ordered-args", args: normalizedArgs },
|
|
2660
|
-
});
|
|
2661
|
-
return {
|
|
2662
|
-
placeholder: { args: replace("none") },
|
|
2663
|
-
approval: script(replace("approval")),
|
|
2664
|
-
};
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
function signatureScriptArgsBytes(args, signature = new Uint8Array(65)) {
|
|
2668
|
-
const builder = new kaspaWasm.ScriptBuilder({
|
|
2669
|
-
flags: { covenantsEnabled: true },
|
|
2670
|
-
});
|
|
2671
|
-
for (const arg of args || []) {
|
|
2672
|
-
if (arg.type === "i64") {
|
|
2673
|
-
builder.addI64(BigInt(arg.value));
|
|
2674
|
-
} else if (arg.type === "byte") {
|
|
2675
|
-
builder.addData(Uint8Array.of(arg.value));
|
|
2676
|
-
} else if (arg.type === "data") {
|
|
2677
|
-
builder.addData(hexToBytes(arg.hex));
|
|
2678
|
-
} else if (arg.type === "signature") {
|
|
2679
|
-
const prefix = hexToBytes(arg.prefixHex || "");
|
|
2680
|
-
builder.addData(concatBytes(prefix, signature));
|
|
2681
|
-
} else {
|
|
2682
|
-
throw new Error(`unsupported snapshot signature argument ${arg.type}`);
|
|
2683
|
-
}
|
|
2684
|
-
}
|
|
2685
|
-
return builder.drain();
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
1235
|
function encodeControllerState(state) {
|
|
2689
1236
|
return concatBytes(
|
|
2690
1237
|
pushData(state.tokenId),
|
|
@@ -2892,62 +1439,6 @@ export function createKcc20SnapshotPsktBuilder(
|
|
|
2892
1439
|
return normalized;
|
|
2893
1440
|
}
|
|
2894
1441
|
|
|
2895
|
-
function reserveShards(values, totalSupply) {
|
|
2896
|
-
if (!Array.isArray(values) || values.length < 1 || values.length > 8) {
|
|
2897
|
-
throw new Error("atomic reserve handoff requires 1..8 shards");
|
|
2898
|
-
}
|
|
2899
|
-
const indexes = new Set<number>();
|
|
2900
|
-
const controllerIds = new Set<string>();
|
|
2901
|
-
const shards = values.map((value) => {
|
|
2902
|
-
const shardIndex = unsignedInteger(value?.shardIndex, "shard index");
|
|
2903
|
-
const controllerId = requireHex32(value?.controllerId, "controller id");
|
|
2904
|
-
const allocationTotal = positiveI64(
|
|
2905
|
-
value?.allocationTotal,
|
|
2906
|
-
"shard allocation total",
|
|
2907
|
-
);
|
|
2908
|
-
if (indexes.has(shardIndex) || controllerIds.has(controllerId)) {
|
|
2909
|
-
throw new Error("reserve handoff shard identities must be unique");
|
|
2910
|
-
}
|
|
2911
|
-
indexes.add(shardIndex);
|
|
2912
|
-
controllerIds.add(controllerId);
|
|
2913
|
-
return { shardIndex, controllerId, allocationTotal };
|
|
2914
|
-
});
|
|
2915
|
-
if (
|
|
2916
|
-
shards.reduce((sum, shard) => sum + shard.allocationTotal, 0n) !==
|
|
2917
|
-
totalSupply
|
|
2918
|
-
) {
|
|
2919
|
-
throw new Error("reserve handoff allocations do not equal total supply");
|
|
2920
|
-
}
|
|
2921
|
-
return shards.sort((left, right) => left.shardIndex - right.shardIndex);
|
|
2922
|
-
}
|
|
2923
|
-
|
|
2924
|
-
function snapshotShardPlanCommitment({
|
|
2925
|
-
migrationCommitment,
|
|
2926
|
-
manifestCommitment,
|
|
2927
|
-
tokenId,
|
|
2928
|
-
totalSupply,
|
|
2929
|
-
shards,
|
|
2930
|
-
}) {
|
|
2931
|
-
return bytesToHex(
|
|
2932
|
-
blake2b(
|
|
2933
|
-
concatBytes(
|
|
2934
|
-
Uint8Array.of(0x20),
|
|
2935
|
-
hexToBytes(migrationCommitment),
|
|
2936
|
-
hexToBytes(manifestCommitment),
|
|
2937
|
-
hexToBytes(tokenId),
|
|
2938
|
-
i64(totalSupply),
|
|
2939
|
-
i64(BigInt(shards.length)),
|
|
2940
|
-
...shards.flatMap((shard, index) => [
|
|
2941
|
-
i64(BigInt(index)),
|
|
2942
|
-
hexToBytes(shard.controllerId),
|
|
2943
|
-
i64(shard.allocationTotal),
|
|
2944
|
-
]),
|
|
2945
|
-
),
|
|
2946
|
-
{ dkLen: 32 },
|
|
2947
|
-
),
|
|
2948
|
-
);
|
|
2949
|
-
}
|
|
2950
|
-
|
|
2951
1442
|
function claimProof(value, shard) {
|
|
2952
1443
|
const proof = value || {};
|
|
2953
1444
|
const normalized = {
|
|
@@ -3270,52 +1761,6 @@ export function createKcc20SnapshotPsktBuilder(
|
|
|
3270
1761
|
return value.toLowerCase();
|
|
3271
1762
|
}
|
|
3272
1763
|
|
|
3273
|
-
function controllerDeploymentPayload(definition, state) {
|
|
3274
|
-
return bytesToHex(
|
|
3275
|
-
new TextEncoder().encode(
|
|
3276
|
-
JSON.stringify({
|
|
3277
|
-
tn10: {
|
|
3278
|
-
v: 1,
|
|
3279
|
-
tmpl: "KCC20SnapshotController",
|
|
3280
|
-
migrationId: definition.migrationId,
|
|
3281
|
-
manifestCommitment: definition.manifestCommitment,
|
|
3282
|
-
shardIndex: definition.shard.shardIndex,
|
|
3283
|
-
args: [
|
|
3284
|
-
{
|
|
3285
|
-
name: "controllerState",
|
|
3286
|
-
type: "object",
|
|
3287
|
-
value: jsonControllerState(state),
|
|
3288
|
-
},
|
|
3289
|
-
],
|
|
3290
|
-
},
|
|
3291
|
-
}),
|
|
3292
|
-
),
|
|
3293
|
-
);
|
|
3294
|
-
}
|
|
3295
|
-
|
|
3296
|
-
function jsonControllerState(state) {
|
|
3297
|
-
return Object.fromEntries(
|
|
3298
|
-
Object.entries(state).map(([key, value]) => [
|
|
3299
|
-
key,
|
|
3300
|
-
value instanceof Uint8Array
|
|
3301
|
-
? bytesToHex(value)
|
|
3302
|
-
: controllerInteger(value, key),
|
|
3303
|
-
]),
|
|
3304
|
-
);
|
|
3305
|
-
}
|
|
3306
|
-
|
|
3307
|
-
function controllerInteger(value: unknown, key: string): string {
|
|
3308
|
-
if (
|
|
3309
|
-
typeof value !== "string" &&
|
|
3310
|
-
typeof value !== "number" &&
|
|
3311
|
-
typeof value !== "bigint" &&
|
|
3312
|
-
typeof value !== "boolean"
|
|
3313
|
-
) {
|
|
3314
|
-
throw new Error(`controller state ${key} is not an integer`);
|
|
3315
|
-
}
|
|
3316
|
-
return BigInt(value).toString();
|
|
3317
|
-
}
|
|
3318
|
-
|
|
3319
1764
|
function scriptAddress(script, network) {
|
|
3320
1765
|
return kaspaWasm
|
|
3321
1766
|
.addressFromScriptPublicKey(
|
|
@@ -3473,120 +1918,3 @@ export function createKcc20SnapshotPsktBuilder(
|
|
|
3473
1918
|
|
|
3474
1919
|
return buildSnapshotMigrationPskt;
|
|
3475
1920
|
}
|
|
3476
|
-
|
|
3477
|
-
/** Pure script constructors shared by browser verification and server projections. */
|
|
3478
|
-
function buildSnapshotControllerScriptForState(artifact, state) {
|
|
3479
|
-
const normalized = normalizeSilverScriptArtifact(artifact);
|
|
3480
|
-
const encoded = encodePortableControllerState(state);
|
|
3481
|
-
const start = normalized.state_layout?.start;
|
|
3482
|
-
const length = normalized.state_layout?.len;
|
|
3483
|
-
if (
|
|
3484
|
-
!Number.isInteger(start) ||
|
|
3485
|
-
!Number.isInteger(length) ||
|
|
3486
|
-
encoded.length !== length
|
|
3487
|
-
) {
|
|
3488
|
-
throw new Error("snapshot controller artifact state layout is invalid");
|
|
3489
|
-
}
|
|
3490
|
-
return portableConcatBytes(
|
|
3491
|
-
Uint8Array.from(normalized.script).slice(0, start),
|
|
3492
|
-
encoded,
|
|
3493
|
-
Uint8Array.from(normalized.script).slice(start + length),
|
|
3494
|
-
);
|
|
3495
|
-
}
|
|
3496
|
-
|
|
3497
|
-
function buildSnapshotBootstrapScriptForState(artifact, state) {
|
|
3498
|
-
const normalized = normalizeSilverScriptArtifact(artifact);
|
|
3499
|
-
const encoded = encodePortableBootstrapState(state);
|
|
3500
|
-
const start = normalized.state_layout?.start;
|
|
3501
|
-
const length = normalized.state_layout?.len;
|
|
3502
|
-
if (
|
|
3503
|
-
normalized.contract_name !== "KCC20SnapshotBootstrap" ||
|
|
3504
|
-
!Number.isInteger(start) ||
|
|
3505
|
-
!Number.isInteger(length) ||
|
|
3506
|
-
encoded.length !== length
|
|
3507
|
-
) {
|
|
3508
|
-
throw new Error("snapshot bootstrap artifact state layout is invalid");
|
|
3509
|
-
}
|
|
3510
|
-
return portableConcatBytes(
|
|
3511
|
-
Uint8Array.from(normalized.script).slice(0, start),
|
|
3512
|
-
encoded,
|
|
3513
|
-
Uint8Array.from(normalized.script).slice(start + length),
|
|
3514
|
-
);
|
|
3515
|
-
}
|
|
3516
|
-
|
|
3517
|
-
function encodePortableControllerState(state) {
|
|
3518
|
-
return portableConcatBytes(
|
|
3519
|
-
portablePushData(state.tokenId),
|
|
3520
|
-
portablePushData(state.holderExtensionCommitment),
|
|
3521
|
-
portablePushData(state.manifestCommitment),
|
|
3522
|
-
portablePushData(state.snapshotContext),
|
|
3523
|
-
portablePushData(state.kcc20TemplateHash),
|
|
3524
|
-
portablePushData(portableI64(state.claimNotBeforeDaaScore)),
|
|
3525
|
-
portablePushData(portableI64(state.shardIndex)),
|
|
3526
|
-
portablePushData(portableI64(state.globalIndexStart)),
|
|
3527
|
-
portablePushData(portableI64(state.holderCount)),
|
|
3528
|
-
portablePushData(portableI64(state.treeDepth)),
|
|
3529
|
-
portablePushData(portableI64(state.kcc20TemplatePrefixLen)),
|
|
3530
|
-
portablePushData(portableI64(state.kcc20TemplateSuffixLen)),
|
|
3531
|
-
portablePushData(portableI64(state.controllerCellKas)),
|
|
3532
|
-
portablePushData(portableI64(state.reserveCellKas)),
|
|
3533
|
-
portablePushData(portableI64(state.recipientMinCellKas)),
|
|
3534
|
-
portablePushData(state.remainingRoot),
|
|
3535
|
-
portablePushData(portableI64(state.remainingAmount)),
|
|
3536
|
-
portablePushData(portableI64(state.claimsProcessed)),
|
|
3537
|
-
);
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
function encodePortableBootstrapState(state) {
|
|
3541
|
-
return portableConcatBytes(
|
|
3542
|
-
portablePushData(state.migrationCommitment),
|
|
3543
|
-
portablePushData(state.manifestCommitment),
|
|
3544
|
-
portablePushData(state.holderExtensionCommitment),
|
|
3545
|
-
portablePushData(portableI64(state.totalSupply)),
|
|
3546
|
-
portablePushData(portableI64(state.claimNotBeforeDaaScore)),
|
|
3547
|
-
portablePushData(state.kcc20TemplateHash),
|
|
3548
|
-
portablePushData(portableI64(state.kcc20TemplatePrefixLen)),
|
|
3549
|
-
portablePushData(portableI64(state.kcc20TemplateSuffixLen)),
|
|
3550
|
-
portablePushData(portableI64(state.tokenCellKas)),
|
|
3551
|
-
portablePushData(portableI64(state.reserveCellKas)),
|
|
3552
|
-
portablePushData(portableI64(state.bootstrapCellKas)),
|
|
3553
|
-
portablePushData(state.approvalKey ?? state.approvalKeyA),
|
|
3554
|
-
portablePushData(portableI64(state.phase)),
|
|
3555
|
-
portablePushData(state.tokenId),
|
|
3556
|
-
portablePushData(state.shardPlanCommitment),
|
|
3557
|
-
portablePushData(state.activationDigest),
|
|
3558
|
-
portablePushData(portableI64(state.shardCount)),
|
|
3559
|
-
);
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
function portablePushData(value) {
|
|
3563
|
-
const bytes = Uint8Array.from(value);
|
|
3564
|
-
if (bytes.length > 75) throw new Error("snapshot state push is too large");
|
|
3565
|
-
return portableConcatBytes(Uint8Array.of(bytes.length), bytes);
|
|
3566
|
-
}
|
|
3567
|
-
|
|
3568
|
-
function portableI64(value) {
|
|
3569
|
-
let cursor = BigInt(value);
|
|
3570
|
-
if (cursor < 0n || cursor > 9_223_372_036_854_775_807n) {
|
|
3571
|
-
throw new Error("snapshot state integer is outside signed i64 range");
|
|
3572
|
-
}
|
|
3573
|
-
const bytes = new Uint8Array(8);
|
|
3574
|
-
for (let index = 0; index < bytes.length; index += 1) {
|
|
3575
|
-
bytes[index] = Number(cursor & 0xffn);
|
|
3576
|
-
cursor >>= 8n;
|
|
3577
|
-
}
|
|
3578
|
-
return bytes;
|
|
3579
|
-
}
|
|
3580
|
-
|
|
3581
|
-
function portableConcatBytes(...values) {
|
|
3582
|
-
const arrays = values.map((value) => Uint8Array.from(value));
|
|
3583
|
-
const output = new Uint8Array(
|
|
3584
|
-
arrays.reduce((sum, value) => sum + value.length, 0),
|
|
3585
|
-
);
|
|
3586
|
-
let offset = 0;
|
|
3587
|
-
for (const value of arrays) {
|
|
3588
|
-
output.set(value, offset);
|
|
3589
|
-
offset += value.length;
|
|
3590
|
-
}
|
|
3591
|
-
return output;
|
|
3592
|
-
}
|