Migrate grateWizard from React/Next.js to native Nuxt integration
- Port all React components to Vue 3 (GwTabs, GwMN, GwCRA, GwCRS, GwMap, GwRelations, GwPerimeterList) - Port hooks to Vue composables (useCesiumProfiles, useSavedPerimeters) - Copy pure TS services and utils (duniter/, ss58, gratewizard utils) - Add Leaflet + Geoman + MarkerCluster dependencies - Serve grateWizard as popup via /gratewizard?popup (layout: false) and info page on /gratewizard (with Librodrome layout) - Remove public/gratewizard-app/ static Next.js export - Refine UI: compact tabs, buttons, inputs, cards, perimeter list - Use Ğ1 breve everywhere, French locale for all dates and amounts - Rename roles: vendeur→offre / acheteur→reçoit le produit ou service - Rename prix→évaluation in all visible text - Add calculated result column in CRA and CRS relation tables - DU/Ğ1 selector uses toggle switch (same as role toggle) - Auto-scroll to monetary data card on polygon selection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
109
app/utils/ss58.ts
Normal file
109
app/utils/ss58.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
|
||||
const ALPHABET_MAP = new Map<string, number>();
|
||||
for (let i = 0; i < BASE58_ALPHABET.length; i++) {
|
||||
ALPHABET_MAP.set(BASE58_ALPHABET[i], i);
|
||||
}
|
||||
|
||||
export function base58Decode(str: string): Uint8Array {
|
||||
if (str.length === 0) return new Uint8Array(0);
|
||||
|
||||
// Count leading '1's (zero bytes)
|
||||
let leadingZeros = 0;
|
||||
while (leadingZeros < str.length && str[leadingZeros] === '1') {
|
||||
leadingZeros++;
|
||||
}
|
||||
|
||||
// Decode base58 to big integer (stored as byte array)
|
||||
const size = Math.ceil(str.length * 733 / 1000) + 1; // log(58) / log(256)
|
||||
const bytes = new Uint8Array(size);
|
||||
|
||||
for (let i = leadingZeros; i < str.length; i++) {
|
||||
const val = ALPHABET_MAP.get(str[i]);
|
||||
if (val === undefined) throw new Error(`Invalid base58 character: ${str[i]}`);
|
||||
|
||||
let carry = val;
|
||||
for (let j = size - 1; j >= 0; j--) {
|
||||
carry += 256 * bytes[j];
|
||||
bytes[j] = carry % 256;
|
||||
carry = Math.floor(carry / 256);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip leading zeros in the decoded bytes
|
||||
let start = 0;
|
||||
while (start < size && bytes[start] === 0) {
|
||||
start++;
|
||||
}
|
||||
|
||||
const result = new Uint8Array(leadingZeros + (size - start));
|
||||
// Leading zeros from '1' characters
|
||||
for (let i = 0; i < leadingZeros; i++) {
|
||||
result[i] = 0;
|
||||
}
|
||||
// Decoded bytes
|
||||
for (let i = start; i < size; i++) {
|
||||
result[leadingZeros + (i - start)] = bytes[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function base58Encode(bytes: Uint8Array): string {
|
||||
if (bytes.length === 0) return '';
|
||||
|
||||
// Count leading zero bytes
|
||||
let leadingZeros = 0;
|
||||
while (leadingZeros < bytes.length && bytes[leadingZeros] === 0) {
|
||||
leadingZeros++;
|
||||
}
|
||||
|
||||
// Encode to base58
|
||||
const size = Math.ceil(bytes.length * 138 / 100) + 1; // log(256) / log(58)
|
||||
const digits = new Uint8Array(size);
|
||||
|
||||
for (let i = leadingZeros; i < bytes.length; i++) {
|
||||
let carry = bytes[i];
|
||||
for (let j = size - 1; j >= 0; j--) {
|
||||
carry += 256 * digits[j];
|
||||
digits[j] = carry % 58;
|
||||
carry = Math.floor(carry / 58);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip leading zeros in base58 output
|
||||
let start = 0;
|
||||
while (start < size && digits[start] === 0) {
|
||||
start++;
|
||||
}
|
||||
|
||||
let result = '1'.repeat(leadingZeros);
|
||||
for (let i = start; i < size; i++) {
|
||||
result += BASE58_ALPHABET[digits[i]];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an SS58 address to a base58-encoded raw pubkey (Cesium+ v1 format).
|
||||
*
|
||||
* SS58 layout: [prefix (1 or 2 bytes)] [32 bytes pubkey] [2 bytes checksum]
|
||||
* - If first byte has bit 6 set (& 0x40), prefix is 2 bytes
|
||||
* - Otherwise prefix is 1 byte
|
||||
*/
|
||||
export function ss58ToV1Pubkey(ss58: string): string {
|
||||
const raw = base58Decode(ss58);
|
||||
|
||||
// Determine prefix length
|
||||
const prefixLen = (raw[0] & 0x40) ? 2 : 1;
|
||||
|
||||
// Extract 32-byte pubkey (skip prefix, drop 2-byte checksum)
|
||||
const pubkey = raw.slice(prefixLen, prefixLen + 32);
|
||||
|
||||
if (pubkey.length !== 32) {
|
||||
throw new Error(`Invalid SS58 address: expected 32-byte pubkey, got ${pubkey.length}`);
|
||||
}
|
||||
|
||||
return base58Encode(pubkey);
|
||||
}
|
||||
Reference in New Issue
Block a user