@kaspacom/kcc20-tx-builder 0.1.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/LICENSE +201 -0
- package/README.md +104 -0
- package/artifacts/KCC20.placeholder.json +77479 -0
- package/artifacts/KCC20FeeTicket.placeholder.json +16846 -0
- package/artifacts/KCC20Orderbook.placeholder.json +78001 -0
- package/artifacts/KCC20Vesting.placeholder.json +4298 -0
- package/artifacts/KCC20Wrapper.placeholder.json +12246 -0
- package/dist/abi.d.ts +44 -0
- package/dist/abi.d.ts.map +1 -0
- package/dist/abi.js +336 -0
- package/dist/abi.js.map +1 -0
- package/dist/action-source-resolver.d.ts +80 -0
- package/dist/action-source-resolver.d.ts.map +1 -0
- package/dist/action-source-resolver.js +412 -0
- package/dist/action-source-resolver.js.map +1 -0
- package/dist/artifacts.d.ts +14 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +44 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/builder.d.ts +12 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +18 -0
- package/dist/builder.js.map +1 -0
- package/dist/cjs/abi.d.ts +43 -0
- package/dist/cjs/abi.js +361 -0
- package/dist/cjs/abi.js.map +1 -0
- package/dist/cjs/action-source-resolver.d.ts +79 -0
- package/dist/cjs/action-source-resolver.js +435 -0
- package/dist/cjs/action-source-resolver.js.map +1 -0
- package/dist/cjs/artifacts.d.ts +13 -0
- package/dist/cjs/artifacts.js +51 -0
- package/dist/cjs/artifacts.js.map +1 -0
- package/dist/cjs/builder.d.ts +11 -0
- package/dist/cjs/builder.js +22 -0
- package/dist/cjs/builder.js.map +1 -0
- package/dist/cjs/deploy-operation.d.ts +103 -0
- package/dist/cjs/deploy-operation.js +335 -0
- package/dist/cjs/deploy-operation.js.map +1 -0
- package/dist/cjs/encoding.d.ts +5 -0
- package/dist/cjs/encoding.js +51 -0
- package/dist/cjs/encoding.js.map +1 -0
- package/dist/cjs/engine.d.ts +40 -0
- package/dist/cjs/engine.js +8972 -0
- package/dist/cjs/engine.js.map +1 -0
- package/dist/cjs/errors.d.ts +7 -0
- package/dist/cjs/errors.js +23 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/fee-ticket-operation.d.ts +204 -0
- package/dist/cjs/fee-ticket-operation.js +327 -0
- package/dist/cjs/fee-ticket-operation.js.map +1 -0
- package/dist/cjs/index.d.ts +27 -0
- package/dist/cjs/index.js +45 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/ladder-limit-orders.d.ts +41 -0
- package/dist/cjs/ladder-limit-orders.js +129 -0
- package/dist/cjs/ladder-limit-orders.js.map +1 -0
- package/dist/cjs/mint-availability-operation.d.ts +56 -0
- package/dist/cjs/mint-availability-operation.js +95 -0
- package/dist/cjs/mint-availability-operation.js.map +1 -0
- package/dist/cjs/models.d.ts +90 -0
- package/dist/cjs/models.js +6 -0
- package/dist/cjs/models.js.map +1 -0
- package/dist/cjs/operations.d.ts +6 -0
- package/dist/cjs/operations.js +41 -0
- package/dist/cjs/operations.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/paid-mint-amount.d.ts +69 -0
- package/dist/cjs/paid-mint-amount.js +281 -0
- package/dist/cjs/paid-mint-amount.js.map +1 -0
- package/dist/cjs/protocol.d.ts +21 -0
- package/dist/cjs/protocol.js +25 -0
- package/dist/cjs/protocol.js.map +1 -0
- package/dist/cjs/runtime.d.ts +27 -0
- package/dist/cjs/runtime.js +3 -0
- package/dist/cjs/runtime.js.map +1 -0
- package/dist/cjs/sale-common.d.ts +287 -0
- package/dist/cjs/sale-common.js +928 -0
- package/dist/cjs/sale-common.js.map +1 -0
- package/dist/cjs/source-provider.d.ts +11 -0
- package/dist/cjs/source-provider.js +3 -0
- package/dist/cjs/source-provider.js.map +1 -0
- package/dist/cjs/source-validation.d.ts +9 -0
- package/dist/cjs/source-validation.js +33 -0
- package/dist/cjs/source-validation.js.map +1 -0
- package/dist/cjs/token-amount.d.ts +31 -0
- package/dist/cjs/token-amount.js +252 -0
- package/dist/cjs/token-amount.js.map +1 -0
- package/dist/cjs/token-operation.d.ts +135 -0
- package/dist/cjs/token-operation.js +236 -0
- package/dist/cjs/token-operation.js.map +1 -0
- package/dist/cjs/trade-fee.d.ts +60 -0
- package/dist/cjs/trade-fee.js +185 -0
- package/dist/cjs/trade-fee.js.map +1 -0
- package/dist/cjs/trading-operation.d.ts +212 -0
- package/dist/cjs/trading-operation.js +587 -0
- package/dist/cjs/trading-operation.js.map +1 -0
- package/dist/cjs/verify-operation.d.ts +79 -0
- package/dist/cjs/verify-operation.js +246 -0
- package/dist/cjs/verify-operation.js.map +1 -0
- package/dist/cjs/wallet-operation.d.ts +52 -0
- package/dist/cjs/wallet-operation.js +58 -0
- package/dist/cjs/wallet-operation.js.map +1 -0
- package/dist/cjs/wasm-provider.d.ts +15 -0
- package/dist/cjs/wasm-provider.js +20 -0
- package/dist/cjs/wasm-provider.js.map +1 -0
- package/dist/cjs/wrapper-operation.d.ts +196 -0
- package/dist/cjs/wrapper-operation.js +317 -0
- package/dist/cjs/wrapper-operation.js.map +1 -0
- package/dist/deploy-operation.d.ts +104 -0
- package/dist/deploy-operation.d.ts.map +1 -0
- package/dist/deploy-operation.js +327 -0
- package/dist/deploy-operation.js.map +1 -0
- package/dist/encoding.d.ts +6 -0
- package/dist/encoding.d.ts.map +1 -0
- package/dist/encoding.js +44 -0
- package/dist/encoding.js.map +1 -0
- package/dist/engine.d.ts +41 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +8969 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/fee-ticket-operation.d.ts +205 -0
- package/dist/fee-ticket-operation.d.ts.map +1 -0
- package/dist/fee-ticket-operation.js +320 -0
- package/dist/fee-ticket-operation.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/ladder-limit-orders.d.ts +42 -0
- package/dist/ladder-limit-orders.d.ts.map +1 -0
- package/dist/ladder-limit-orders.js +121 -0
- package/dist/ladder-limit-orders.js.map +1 -0
- package/dist/mint-availability-operation.d.ts +57 -0
- package/dist/mint-availability-operation.d.ts.map +1 -0
- package/dist/mint-availability-operation.js +91 -0
- package/dist/mint-availability-operation.js.map +1 -0
- package/dist/models.d.ts +91 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +3 -0
- package/dist/models.js.map +1 -0
- package/dist/operations.d.ts +7 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +36 -0
- package/dist/operations.js.map +1 -0
- package/dist/paid-mint-amount.d.ts +70 -0
- package/dist/paid-mint-amount.d.ts.map +1 -0
- package/dist/paid-mint-amount.js +268 -0
- package/dist/paid-mint-amount.js.map +1 -0
- package/dist/protocol.d.ts +22 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +22 -0
- package/dist/protocol.js.map +1 -0
- package/dist/runtime.d.ts +28 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sale-common.d.ts +288 -0
- package/dist/sale-common.d.ts.map +1 -0
- package/dist/sale-common.js +844 -0
- package/dist/sale-common.js.map +1 -0
- package/dist/source-provider.d.ts +12 -0
- package/dist/source-provider.d.ts.map +1 -0
- package/dist/source-provider.js +2 -0
- package/dist/source-provider.js.map +1 -0
- package/dist/source-validation.d.ts +10 -0
- package/dist/source-validation.d.ts.map +1 -0
- package/dist/source-validation.js +28 -0
- package/dist/source-validation.js.map +1 -0
- package/dist/token-amount.d.ts +32 -0
- package/dist/token-amount.d.ts.map +1 -0
- package/dist/token-amount.js +237 -0
- package/dist/token-amount.js.map +1 -0
- package/dist/token-operation.d.ts +136 -0
- package/dist/token-operation.d.ts.map +1 -0
- package/dist/token-operation.js +231 -0
- package/dist/token-operation.js.map +1 -0
- package/dist/trade-fee.d.ts +61 -0
- package/dist/trade-fee.d.ts.map +1 -0
- package/dist/trade-fee.js +173 -0
- package/dist/trade-fee.js.map +1 -0
- package/dist/trading-operation.d.ts +213 -0
- package/dist/trading-operation.d.ts.map +1 -0
- package/dist/trading-operation.js +580 -0
- package/dist/trading-operation.js.map +1 -0
- package/dist/verify-operation.d.ts +80 -0
- package/dist/verify-operation.d.ts.map +1 -0
- package/dist/verify-operation.js +243 -0
- package/dist/verify-operation.js.map +1 -0
- package/dist/wallet-operation.d.ts +53 -0
- package/dist/wallet-operation.d.ts.map +1 -0
- package/dist/wallet-operation.js +55 -0
- package/dist/wallet-operation.js.map +1 -0
- package/dist/wasm-provider.d.ts +16 -0
- package/dist/wasm-provider.d.ts.map +1 -0
- package/dist/wasm-provider.js +17 -0
- package/dist/wasm-provider.js.map +1 -0
- package/dist/wrapper-operation.d.ts +197 -0
- package/dist/wrapper-operation.d.ts.map +1 -0
- package/dist/wrapper-operation.js +311 -0
- package/dist/wrapper-operation.js.map +1 -0
- package/index.d.ts +1 -0
- package/package.json +84 -0
- package/src/abi.ts +493 -0
- package/src/action-source-resolver.ts +660 -0
- package/src/artifacts.ts +87 -0
- package/src/builder.ts +37 -0
- package/src/deploy-operation.ts +458 -0
- package/src/encoding.ts +58 -0
- package/src/engine.ts +13226 -0
- package/src/errors.ts +35 -0
- package/src/fee-ticket-operation.ts +518 -0
- package/src/index.ts +27 -0
- package/src/ladder-limit-orders.ts +184 -0
- package/src/mint-availability-operation.ts +146 -0
- package/src/models.ts +103 -0
- package/src/operations.ts +46 -0
- package/src/paid-mint-amount.ts +402 -0
- package/src/protocol.ts +28 -0
- package/src/runtime.ts +29 -0
- package/src/sale-common.ts +1238 -0
- package/src/source-provider.ts +12 -0
- package/src/source-validation.ts +47 -0
- package/src/token-amount.ts +302 -0
- package/src/token-operation.ts +384 -0
- package/src/trade-fee.ts +264 -0
- package/src/trading-operation.ts +842 -0
- package/src/verify-operation.ts +400 -0
- package/src/wallet-operation.ts +86 -0
- package/src/wasm-provider.ts +32 -0
- package/src/wrapper-operation.ts +531 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
export const KCC20_PAID_MINT_MIN_GROSS_SOMPI = 50_000_000n;
|
|
2
|
+
export const KCC20_VERIFY_PAID_MINT_MIN_GROSS_SOMPI = 100_000_000n;
|
|
3
|
+
export const KCC20_RECOMMENDED_PAID_MINT_MIN_GROSS_SOMPI = 55_000_000n;
|
|
4
|
+
export const KCC20_MIN_PAID_MINT_TREASURY_OUTPUT_SOMPI = 2_003_085n;
|
|
5
|
+
|
|
6
|
+
const KCC20_FEE_DENOMINATOR_BPS = 10_000n;
|
|
7
|
+
const KCC20_PAID_MINT_GROSS_SEARCH_MAX_DOUBLINGS = 64;
|
|
8
|
+
|
|
9
|
+
export interface Kcc20PaidMintAmountQuote {
|
|
10
|
+
tokenAmount: bigint;
|
|
11
|
+
adjustedTokenAmount: bigint;
|
|
12
|
+
grossSompi: bigint;
|
|
13
|
+
adjusted: boolean;
|
|
14
|
+
belowMinimumGross: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether all remaining paid-mint supply can produce at least the
|
|
19
|
+
* required gross amount. The comparison is cross-multiplied so fractional
|
|
20
|
+
* display scales are handled without rounding or floating-point arithmetic.
|
|
21
|
+
*/
|
|
22
|
+
export function kcc20PaidMintCapacityMeetsMinimumGross(input: {
|
|
23
|
+
remainingTokenAmount: bigint;
|
|
24
|
+
unitPriceSompi: bigint;
|
|
25
|
+
priceScale: bigint;
|
|
26
|
+
minimumGrossSompi?: bigint;
|
|
27
|
+
}): boolean {
|
|
28
|
+
assertPositivePriceScale(input.priceScale);
|
|
29
|
+
if (input.unitPriceSompi <= 0n) return true;
|
|
30
|
+
if (input.remainingTokenAmount <= 0n) return false;
|
|
31
|
+
const minimumGrossSompi =
|
|
32
|
+
input.minimumGrossSompi ?? KCC20_PAID_MINT_MIN_GROSS_SOMPI;
|
|
33
|
+
if (minimumGrossSompi < 0n) {
|
|
34
|
+
throw new Error("minimumGrossSompi must not be negative");
|
|
35
|
+
}
|
|
36
|
+
return (
|
|
37
|
+
input.remainingTokenAmount * input.unitPriceSompi >=
|
|
38
|
+
minimumGrossSompi * input.priceScale
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function kcc20PaidMintExactGrossSompi(input: {
|
|
43
|
+
tokenAmount: bigint;
|
|
44
|
+
unitPriceSompi: bigint;
|
|
45
|
+
priceScale: bigint;
|
|
46
|
+
}): bigint {
|
|
47
|
+
if (input.unitPriceSompi <= 0n) {
|
|
48
|
+
return 0n;
|
|
49
|
+
}
|
|
50
|
+
assertPositivePriceScale(input.priceScale);
|
|
51
|
+
const numerator = input.tokenAmount * input.unitPriceSompi;
|
|
52
|
+
const remainder = numerator % input.priceScale;
|
|
53
|
+
if (remainder !== 0n) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`paid mint amount is not valid for token decimals: tokenAmount=${input.tokenAmount} unitPriceSompi=${input.unitPriceSompi} priceScale=${input.priceScale}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return numerator / input.priceScale;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function kcc20PaidMintAmountStep(
|
|
62
|
+
unitPriceSompi: bigint,
|
|
63
|
+
priceScale: bigint,
|
|
64
|
+
): bigint {
|
|
65
|
+
if (unitPriceSompi <= 0n) {
|
|
66
|
+
return 1n;
|
|
67
|
+
}
|
|
68
|
+
assertPositivePriceScale(priceScale);
|
|
69
|
+
return priceScale / gcd(priceScale, unitPriceSompi);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function kcc20SnapPaidMintAmountUp(input: {
|
|
73
|
+
tokenAmount: bigint;
|
|
74
|
+
unitPriceSompi: bigint;
|
|
75
|
+
priceScale: bigint;
|
|
76
|
+
}): bigint {
|
|
77
|
+
const step = kcc20PaidMintAmountStep(input.unitPriceSompi, input.priceScale);
|
|
78
|
+
const remainder = input.tokenAmount % step;
|
|
79
|
+
return remainder === 0n
|
|
80
|
+
? input.tokenAmount
|
|
81
|
+
: input.tokenAmount + step - remainder;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function kcc20MinimumPaidMintAmount(input: {
|
|
85
|
+
unitPriceSompi: bigint;
|
|
86
|
+
priceScale: bigint;
|
|
87
|
+
minGrossSompi?: bigint;
|
|
88
|
+
protocolFeeBps?: number | null;
|
|
89
|
+
}): bigint {
|
|
90
|
+
if (input.unitPriceSompi <= 0n) {
|
|
91
|
+
return input.priceScale;
|
|
92
|
+
}
|
|
93
|
+
const minGrossSompi = input.minGrossSompi ?? KCC20_PAID_MINT_MIN_GROSS_SOMPI;
|
|
94
|
+
const minimumAmountAtProtocolFloor = kcc20SnapPaidMintAmountUp({
|
|
95
|
+
tokenAmount: ceilDiv(
|
|
96
|
+
minGrossSompi * input.priceScale,
|
|
97
|
+
input.unitPriceSompi,
|
|
98
|
+
),
|
|
99
|
+
unitPriceSompi: input.unitPriceSompi,
|
|
100
|
+
priceScale: input.priceScale,
|
|
101
|
+
});
|
|
102
|
+
if (
|
|
103
|
+
normalizeFeeBps(input.protocolFeeBps) >= Number(KCC20_FEE_DENOMINATOR_BPS)
|
|
104
|
+
) {
|
|
105
|
+
return minimumAmountAtProtocolFloor;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const wholeDisplayTokens = ceilDiv(
|
|
109
|
+
minimumAmountAtProtocolFloor,
|
|
110
|
+
input.priceScale,
|
|
111
|
+
);
|
|
112
|
+
return kcc20SnapPaidMintAmountUp({
|
|
113
|
+
tokenAmount: wholeDisplayTokens * input.priceScale,
|
|
114
|
+
unitPriceSompi: input.unitPriceSompi,
|
|
115
|
+
priceScale: input.priceScale,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function kcc20ValidatePaidMintAmount(input: {
|
|
120
|
+
tokenAmount: bigint;
|
|
121
|
+
unitPriceSompi: bigint;
|
|
122
|
+
priceScale: bigint;
|
|
123
|
+
minGrossSompi?: bigint;
|
|
124
|
+
protocolFeeBps?: number | null;
|
|
125
|
+
minimumGrossLabel?: string;
|
|
126
|
+
}): void {
|
|
127
|
+
const grossSompi = kcc20PaidMintExactGrossSompi(input);
|
|
128
|
+
const minGrossSompi = input.minGrossSompi ?? KCC20_PAID_MINT_MIN_GROSS_SOMPI;
|
|
129
|
+
if (grossSompi > 0n && grossSompi < minGrossSompi) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`paid mint gross ${formatSompiAsKas(
|
|
132
|
+
grossSompi,
|
|
133
|
+
)} KAS is below ${input.minimumGrossLabel ?? "minimum protocol fee"} ${formatSompiAsKas(
|
|
134
|
+
minGrossSompi,
|
|
135
|
+
)} KAS`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function kcc20PaidMintFeeSplit(
|
|
141
|
+
grossSompi: bigint,
|
|
142
|
+
protocolFeeBps: number | null | undefined,
|
|
143
|
+
minGrossSompi = KCC20_PAID_MINT_MIN_GROSS_SOMPI,
|
|
144
|
+
): { fee: bigint; net: bigint } {
|
|
145
|
+
if (grossSompi <= 0n) {
|
|
146
|
+
return { fee: 0n, net: 0n };
|
|
147
|
+
}
|
|
148
|
+
const bps = normalizeFeeBps(protocolFeeBps);
|
|
149
|
+
const percentageFee = (grossSompi * BigInt(bps)) / KCC20_FEE_DENOMINATOR_BPS;
|
|
150
|
+
const fee = percentageFee < minGrossSompi ? minGrossSompi : percentageFee;
|
|
151
|
+
return { fee, net: grossSompi - fee };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Produces a transaction-safe paid-mint amount. It enforces exact sompi,
|
|
156
|
+
* protocol-fee, and non-zero treasury-output dust constraints in one place.
|
|
157
|
+
*/
|
|
158
|
+
export function buildKcc20PaidMintAmountQuote(input: {
|
|
159
|
+
tokenAmount: bigint;
|
|
160
|
+
unitPriceSompi: bigint;
|
|
161
|
+
priceScale: bigint;
|
|
162
|
+
protocolFeeBps?: number | null;
|
|
163
|
+
minimumGrossSompi?: bigint;
|
|
164
|
+
}): Kcc20PaidMintAmountQuote | undefined {
|
|
165
|
+
if (input.tokenAmount <= 0n || input.unitPriceSompi < 0n) return undefined;
|
|
166
|
+
assertPositivePriceScale(input.priceScale);
|
|
167
|
+
if (input.unitPriceSompi === 0n) {
|
|
168
|
+
return {
|
|
169
|
+
tokenAmount: input.tokenAmount,
|
|
170
|
+
adjustedTokenAmount: input.tokenAmount,
|
|
171
|
+
grossSompi: 0n,
|
|
172
|
+
adjusted: false,
|
|
173
|
+
belowMinimumGross: false,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const minimumGrossSompi =
|
|
178
|
+
input.minimumGrossSompi ?? KCC20_RECOMMENDED_PAID_MINT_MIN_GROSS_SOMPI;
|
|
179
|
+
const step = kcc20PaidMintAmountStep(input.unitPriceSompi, input.priceScale);
|
|
180
|
+
let adjustedTokenAmount = roundUpToStep(input.tokenAmount, step);
|
|
181
|
+
const typedGrossSompi = kcc20PaidMintExactGrossSompi({
|
|
182
|
+
...input,
|
|
183
|
+
tokenAmount: adjustedTokenAmount,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (typedGrossSompi < minimumGrossSompi) {
|
|
187
|
+
return {
|
|
188
|
+
tokenAmount: input.tokenAmount,
|
|
189
|
+
adjustedTokenAmount,
|
|
190
|
+
grossSompi: typedGrossSompi,
|
|
191
|
+
adjusted: adjustedTokenAmount !== input.tokenAmount,
|
|
192
|
+
belowMinimumGross: true,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const feeBps = normalizeUiFeeBps(input.protocolFeeBps);
|
|
197
|
+
if (!isKcc20SafePaidMintGross(typedGrossSompi, feeBps)) {
|
|
198
|
+
const safeTokenAmount =
|
|
199
|
+
feeBps < Number(KCC20_FEE_DENOMINATOR_BPS)
|
|
200
|
+
? snapPaidMintToSafeWholeTokens(
|
|
201
|
+
adjustedTokenAmount,
|
|
202
|
+
input.unitPriceSompi,
|
|
203
|
+
input.priceScale,
|
|
204
|
+
step,
|
|
205
|
+
feeBps,
|
|
206
|
+
)
|
|
207
|
+
: snapPaidMintToSafeExact(
|
|
208
|
+
adjustedTokenAmount,
|
|
209
|
+
input.unitPriceSompi,
|
|
210
|
+
input.priceScale,
|
|
211
|
+
step,
|
|
212
|
+
feeBps,
|
|
213
|
+
);
|
|
214
|
+
if (safeTokenAmount === undefined) return undefined;
|
|
215
|
+
adjustedTokenAmount = safeTokenAmount;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const grossSompi = kcc20PaidMintExactGrossSompi({
|
|
219
|
+
...input,
|
|
220
|
+
tokenAmount: adjustedTokenAmount,
|
|
221
|
+
});
|
|
222
|
+
if (!isKcc20SafePaidMintGross(grossSompi, feeBps)) return undefined;
|
|
223
|
+
return {
|
|
224
|
+
tokenAmount: input.tokenAmount,
|
|
225
|
+
adjustedTokenAmount,
|
|
226
|
+
grossSompi,
|
|
227
|
+
adjusted: adjustedTokenAmount !== input.tokenAmount,
|
|
228
|
+
belowMinimumGross: false,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function kcc20MinimumSafePaidMintAmount(input: {
|
|
233
|
+
unitPriceSompi: bigint;
|
|
234
|
+
priceScale: bigint;
|
|
235
|
+
protocolFeeBps?: number | null;
|
|
236
|
+
minimumGrossSompi?: bigint;
|
|
237
|
+
}): bigint | undefined {
|
|
238
|
+
if (input.unitPriceSompi <= 0n) return undefined;
|
|
239
|
+
const minimumGrossSompi =
|
|
240
|
+
input.minimumGrossSompi ?? KCC20_RECOMMENDED_PAID_MINT_MIN_GROSS_SOMPI;
|
|
241
|
+
const minimum = kcc20SnapPaidMintAmountUp({
|
|
242
|
+
tokenAmount: ceilDiv(
|
|
243
|
+
minimumGrossSompi * input.priceScale,
|
|
244
|
+
input.unitPriceSompi,
|
|
245
|
+
),
|
|
246
|
+
unitPriceSompi: input.unitPriceSompi,
|
|
247
|
+
priceScale: input.priceScale,
|
|
248
|
+
});
|
|
249
|
+
const quote = buildKcc20PaidMintAmountQuote({
|
|
250
|
+
...input,
|
|
251
|
+
tokenAmount: minimum,
|
|
252
|
+
minimumGrossSompi,
|
|
253
|
+
});
|
|
254
|
+
return quote?.adjustedTokenAmount ?? minimum;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function isKcc20SafePaidMintGross(
|
|
258
|
+
grossSompi: bigint,
|
|
259
|
+
protocolFeeBps?: number | null,
|
|
260
|
+
): boolean {
|
|
261
|
+
if (grossSompi < KCC20_PAID_MINT_MIN_GROSS_SOMPI) return false;
|
|
262
|
+
const split = kcc20PaidMintFeeSplit(
|
|
263
|
+
grossSompi,
|
|
264
|
+
normalizeUiFeeBps(protocolFeeBps),
|
|
265
|
+
);
|
|
266
|
+
return (
|
|
267
|
+
split.net >= 0n &&
|
|
268
|
+
(split.net === 0n || split.net >= KCC20_MIN_PAID_MINT_TREASURY_OUTPUT_SOMPI)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function snapPaidMintToSafeExact(
|
|
273
|
+
start: bigint,
|
|
274
|
+
price: bigint,
|
|
275
|
+
scale: bigint,
|
|
276
|
+
step: bigint,
|
|
277
|
+
feeBps: number,
|
|
278
|
+
): bigint | undefined {
|
|
279
|
+
let amount = start;
|
|
280
|
+
for (
|
|
281
|
+
let index = 0;
|
|
282
|
+
index < KCC20_PAID_MINT_GROSS_SEARCH_MAX_DOUBLINGS;
|
|
283
|
+
index += 1
|
|
284
|
+
) {
|
|
285
|
+
const gross = kcc20PaidMintExactGrossSompi({
|
|
286
|
+
tokenAmount: amount,
|
|
287
|
+
unitPriceSompi: price,
|
|
288
|
+
priceScale: scale,
|
|
289
|
+
});
|
|
290
|
+
if (isKcc20SafePaidMintGross(gross, feeBps)) return amount;
|
|
291
|
+
const nextGross = smallestSafePaidMintGrossSompi(gross + 1n, feeBps);
|
|
292
|
+
if (nextGross === undefined) return undefined;
|
|
293
|
+
const next = roundUpToStep(ceilDiv(nextGross * scale, price), step);
|
|
294
|
+
amount = next > amount ? next : amount + step;
|
|
295
|
+
}
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function snapPaidMintToSafeWholeTokens(
|
|
300
|
+
start: bigint,
|
|
301
|
+
price: bigint,
|
|
302
|
+
scale: bigint,
|
|
303
|
+
step: bigint,
|
|
304
|
+
feeBps: number,
|
|
305
|
+
): bigint | undefined {
|
|
306
|
+
let amount = roundUpToStep(ceilDiv(start, scale) * scale, step);
|
|
307
|
+
for (
|
|
308
|
+
let index = 0;
|
|
309
|
+
index < KCC20_PAID_MINT_GROSS_SEARCH_MAX_DOUBLINGS;
|
|
310
|
+
index += 1
|
|
311
|
+
) {
|
|
312
|
+
const gross = kcc20PaidMintExactGrossSompi({
|
|
313
|
+
tokenAmount: amount,
|
|
314
|
+
unitPriceSompi: price,
|
|
315
|
+
priceScale: scale,
|
|
316
|
+
});
|
|
317
|
+
if (isKcc20SafePaidMintGross(gross, feeBps)) return amount;
|
|
318
|
+
const nextGross = smallestSafePaidMintGrossSompi(gross + 1n, feeBps);
|
|
319
|
+
if (nextGross === undefined) return undefined;
|
|
320
|
+
let next = roundUpToStep(ceilDiv(nextGross * scale, price), step);
|
|
321
|
+
next = roundUpToStep(ceilDiv(next, scale) * scale, step);
|
|
322
|
+
amount = next > amount ? next : roundUpToStep(amount + scale, step);
|
|
323
|
+
}
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function smallestSafePaidMintGrossSompi(
|
|
328
|
+
minimum: bigint,
|
|
329
|
+
feeBps: number,
|
|
330
|
+
): bigint | undefined {
|
|
331
|
+
let low =
|
|
332
|
+
minimum < KCC20_PAID_MINT_MIN_GROSS_SOMPI
|
|
333
|
+
? KCC20_PAID_MINT_MIN_GROSS_SOMPI
|
|
334
|
+
: minimum;
|
|
335
|
+
if (isKcc20SafePaidMintGross(low, feeBps)) return low;
|
|
336
|
+
let high = low + 1n;
|
|
337
|
+
for (
|
|
338
|
+
let index = 0;
|
|
339
|
+
index < KCC20_PAID_MINT_GROSS_SEARCH_MAX_DOUBLINGS;
|
|
340
|
+
index += 1
|
|
341
|
+
) {
|
|
342
|
+
if (isKcc20SafePaidMintGross(high, feeBps)) {
|
|
343
|
+
while (low + 1n < high) {
|
|
344
|
+
const middle = low + (high - low) / 2n;
|
|
345
|
+
if (isKcc20SafePaidMintGross(middle, feeBps)) high = middle;
|
|
346
|
+
else low = middle;
|
|
347
|
+
}
|
|
348
|
+
return high;
|
|
349
|
+
}
|
|
350
|
+
high += high - low + 1n;
|
|
351
|
+
}
|
|
352
|
+
return undefined;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function normalizeUiFeeBps(value: number | null | undefined): number {
|
|
356
|
+
return Number.isInteger(value) && value !== null && value !== undefined
|
|
357
|
+
? Math.min(10_000, Math.max(0, value))
|
|
358
|
+
: 200;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function roundUpToStep(value: bigint, step: bigint): bigint {
|
|
362
|
+
const remainder = value % step;
|
|
363
|
+
return remainder === 0n ? value : value + step - remainder;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function normalizeFeeBps(value: number | null | undefined): number {
|
|
367
|
+
if (value === null || value === undefined) {
|
|
368
|
+
return 200;
|
|
369
|
+
}
|
|
370
|
+
if (!Number.isInteger(value) || value < 0 || value > 10_000) {
|
|
371
|
+
throw new Error("protocolFeeBps must be 0..10000");
|
|
372
|
+
}
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function ceilDiv(left: bigint, right: bigint): bigint {
|
|
377
|
+
return (left + right - 1n) / right;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function assertPositivePriceScale(priceScale: bigint): void {
|
|
381
|
+
if (priceScale <= 0n) {
|
|
382
|
+
throw new Error("paid mint priceScale must be greater than zero");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function formatSompiAsKas(value: bigint): string {
|
|
387
|
+
const whole = value / 100_000_000n;
|
|
388
|
+
const fractional = value % 100_000_000n;
|
|
389
|
+
if (fractional === 0n) return whole.toString();
|
|
390
|
+
return `${whole}.${fractional.toString().padStart(8, "0").replace(/0+$/, "")}`;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function gcd(left: bigint, right: bigint): bigint {
|
|
394
|
+
let a = left < 0n ? -left : left;
|
|
395
|
+
let b = right < 0n ? -right : right;
|
|
396
|
+
while (b !== 0n) {
|
|
397
|
+
const remainder = a % b;
|
|
398
|
+
a = b;
|
|
399
|
+
b = remainder;
|
|
400
|
+
}
|
|
401
|
+
return a;
|
|
402
|
+
}
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const SUBNETWORK_ID_NATIVE =
|
|
2
|
+
"0000000000000000000000000000000000000000" as const;
|
|
3
|
+
export const ZERO_HASH =
|
|
4
|
+
"0000000000000000000000000000000000000000000000000000000000000000" as const;
|
|
5
|
+
export const SOMPI_PER_TKAS = 100_000_000n;
|
|
6
|
+
export const DEFAULT_TOKEN_OUTPUT_SOMPI = 50_000_000n;
|
|
7
|
+
export const DEFAULT_COMPUTE_BUDGET = 200;
|
|
8
|
+
export const DEFAULT_KCC20_FEE_BPS = 200n;
|
|
9
|
+
export const DEFAULT_KCC20_MIN_PROTOCOL_FEE_SOMPI = 50_000_000n;
|
|
10
|
+
export const DEFAULT_KCC20_TOKEN_DECIMALS = 8;
|
|
11
|
+
export const KCC20_PUBLIC_MINT_MAX_DISPLAY_TOKENS = 100_000n;
|
|
12
|
+
export const KCC20_PUBLIC_MINT_MAX_DISPLAY_TOKENS_LABEL = "100,000";
|
|
13
|
+
export const KCC20_TICKER_MAX_LENGTH = 12;
|
|
14
|
+
export const KCC20_TOKEN_NAME_MAX_LENGTH = 32;
|
|
15
|
+
export const KCC20_ORDERBOOK_MAX_SWEEP_BID_LEGS = 3;
|
|
16
|
+
export const KCC20_ORDERBOOK_MAX_EXPANDED_SWEEP_BID_LEGS = 10;
|
|
17
|
+
export const KCC20_ORDERBOOK_EXPANDED_SWEEP_OUTPUT_SOMPI = 200_000_000n;
|
|
18
|
+
export const DEFAULT_KCC20_PRICE_SCALE =
|
|
19
|
+
10n ** BigInt(DEFAULT_KCC20_TOKEN_DECIMALS);
|
|
20
|
+
export const KCC20_MIN_PROTOCOL_FEE_SOMPI =
|
|
21
|
+
DEFAULT_KCC20_MIN_PROTOCOL_FEE_SOMPI;
|
|
22
|
+
export const KCC20_DEFAULT_PROTOCOL_FEE_BPS = DEFAULT_KCC20_FEE_BPS;
|
|
23
|
+
export const KCC20_DEFAULT_PRICE_SCALE = DEFAULT_KCC20_PRICE_SCALE;
|
|
24
|
+
|
|
25
|
+
export const KCC20_PLATFORM_PROTOCOL_FEE_RECIPIENT =
|
|
26
|
+
"ce2100e68197c8e695bd781d07e2bd5de7ed77f5a97668884190531bdc632c01" as const;
|
|
27
|
+
export const KCC20_ORDERBOOK_PROTOCOL_FEE_RECIPIENT =
|
|
28
|
+
KCC20_PLATFORM_PROTOCOL_FEE_RECIPIENT;
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared package receives a runtime from its host application.
|
|
3
|
+
*
|
|
4
|
+
* Do not replace this with an import from either Kaspa WASM build. The browser
|
|
5
|
+
* and Node applications initialize different binaries and use different asset
|
|
6
|
+
* loading mechanisms.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A loaded Kaspa module supplied by the host application. The shared package
|
|
10
|
+
* intentionally does not prescribe the exact generated-WASM TypeScript
|
|
11
|
+
* declarations. Operation modules should narrow the capabilities they use.
|
|
12
|
+
*/
|
|
13
|
+
export interface KaspaWasmRuntime {
|
|
14
|
+
readonly [capability: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CoreKaspaWasmCapabilities extends KaspaWasmRuntime {
|
|
18
|
+
readonly addressFromScriptPublicKey: (...args: any[]) => any;
|
|
19
|
+
readonly payToScriptHashScript: (...args: any[]) => any;
|
|
20
|
+
readonly payToAddressScript: (...args: any[]) => any;
|
|
21
|
+
readonly ScriptBuilder: any;
|
|
22
|
+
readonly Transaction: any;
|
|
23
|
+
readonly TransactionInput: any;
|
|
24
|
+
readonly TransactionOutput: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface KaspaWasmProvider {
|
|
28
|
+
getRuntime(): Promise<KaspaWasmRuntime>;
|
|
29
|
+
}
|