@solana/addresses
Version:
Helpers for generating account addresses
298 lines (293 loc) • 10.2 kB
JavaScript
import { transformEncoder, fixEncoderSize, fixDecoderSize, combineCodec } from '@solana/codecs-core';
import { getBase58Encoder, getBase58Decoder } from '@solana/codecs-strings';
import { SolanaError, SOLANA_ERROR__ADDRESSES__STRING_LENGTH_OUT_OF_RANGE, SOLANA_ERROR__ADDRESSES__INVALID_BYTE_LENGTH, SOLANA_ERROR__ADDRESSES__MALFORMED_PDA, SOLANA_ERROR__ADDRESSES__PDA_BUMP_SEED_OUT_OF_RANGE, isSolanaError, SOLANA_ERROR__ADDRESSES__INVALID_SEEDS_POINT_ON_CURVE, SOLANA_ERROR__ADDRESSES__FAILED_TO_FIND_VIABLE_PDA_BUMP_SEED, SOLANA_ERROR__ADDRESSES__MAX_PDA_SEED_LENGTH_EXCEEDED, SOLANA_ERROR__ADDRESSES__PDA_ENDS_WITH_PDA_MARKER, SOLANA_ERROR__ADDRESSES__INVALID_ED25519_PUBLIC_KEY, SOLANA_ERROR__ADDRESSES__MAX_NUMBER_OF_PDA_SEEDS_EXCEEDED } from '@solana/errors';
import { assertKeyExporterIsAvailable, assertDigestCapabilityIsAvailable } from '@solana/assertions';
// src/address.ts
var memoizedBase58Encoder;
var memoizedBase58Decoder;
function getMemoizedBase58Encoder() {
if (!memoizedBase58Encoder) memoizedBase58Encoder = getBase58Encoder();
return memoizedBase58Encoder;
}
function getMemoizedBase58Decoder() {
if (!memoizedBase58Decoder) memoizedBase58Decoder = getBase58Decoder();
return memoizedBase58Decoder;
}
function isAddress(putativeAddress) {
if (
// Lowest address (32 bytes of zeroes)
putativeAddress.length < 32 || // Highest address (32 bytes of 255)
putativeAddress.length > 44
) {
return false;
}
const base58Encoder = getMemoizedBase58Encoder();
try {
return base58Encoder.encode(putativeAddress).byteLength === 32;
} catch {
return false;
}
}
function assertIsAddress(putativeAddress) {
if (
// Lowest address (32 bytes of zeroes)
putativeAddress.length < 32 || // Highest address (32 bytes of 255)
putativeAddress.length > 44
) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__STRING_LENGTH_OUT_OF_RANGE, {
actualLength: putativeAddress.length
});
}
const base58Encoder = getMemoizedBase58Encoder();
const bytes = base58Encoder.encode(putativeAddress);
const numBytes = bytes.byteLength;
if (numBytes !== 32) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__INVALID_BYTE_LENGTH, {
actualLength: numBytes
});
}
}
function address(putativeAddress) {
assertIsAddress(putativeAddress);
return putativeAddress;
}
function getAddressEncoder() {
return transformEncoder(
fixEncoderSize(getMemoizedBase58Encoder(), 32),
(putativeAddress) => address(putativeAddress)
);
}
function getAddressDecoder() {
return fixDecoderSize(getMemoizedBase58Decoder(), 32);
}
function getAddressCodec() {
return combineCodec(getAddressEncoder(), getAddressDecoder());
}
function getAddressComparator() {
return new Intl.Collator("en", {
caseFirst: "lower",
ignorePunctuation: false,
localeMatcher: "best fit",
numeric: false,
sensitivity: "variant",
usage: "sort"
}).compare;
}
// src/vendor/noble/ed25519.ts
var D = 37095705934669439343138083508754565189542113879843219016388785533085940283555n;
var P = 57896044618658097711785492504343953926634992332820282019728792003956564819949n;
var RM1 = 19681161376707505956807079304988542015446066515923890162744021073123829784752n;
function mod(a) {
const r = a % P;
return r >= 0n ? r : P + r;
}
function pow2(x, power) {
let r = x;
while (power-- > 0n) {
r *= r;
r %= P;
}
return r;
}
function pow_2_252_3(x) {
const x2 = x * x % P;
const b2 = x2 * x % P;
const b4 = pow2(b2, 2n) * b2 % P;
const b5 = pow2(b4, 1n) * x % P;
const b10 = pow2(b5, 5n) * b5 % P;
const b20 = pow2(b10, 10n) * b10 % P;
const b40 = pow2(b20, 20n) * b20 % P;
const b80 = pow2(b40, 40n) * b40 % P;
const b160 = pow2(b80, 80n) * b80 % P;
const b240 = pow2(b160, 80n) * b80 % P;
const b250 = pow2(b240, 10n) * b10 % P;
const pow_p_5_8 = pow2(b250, 2n) * x % P;
return pow_p_5_8;
}
function uvRatio(u, v) {
const v3 = mod(v * v * v);
const v7 = mod(v3 * v3 * v);
const pow = pow_2_252_3(u * v7);
let x = mod(u * v3 * pow);
const vx2 = mod(v * x * x);
const root1 = x;
const root2 = mod(x * RM1);
const useRoot1 = vx2 === u;
const useRoot2 = vx2 === mod(-u);
const noRoot = vx2 === mod(-u * RM1);
if (useRoot1) x = root1;
if (useRoot2 || noRoot) x = root2;
if ((mod(x) & 1n) === 1n) x = mod(-x);
if (!useRoot1 && !useRoot2) {
return null;
}
return x;
}
function pointIsOnCurve(y, lastByte) {
const y2 = mod(y * y);
const u = mod(y2 - 1n);
const v = mod(D * y2 + 1n);
const x = uvRatio(u, v);
if (x === null) {
return false;
}
const isLastByteOdd = (lastByte & 128) !== 0;
if (x === 0n && isLastByteOdd) {
return false;
}
return true;
}
// src/curve.ts
function byteToHex(byte) {
const hexString = byte.toString(16);
if (hexString.length === 1) {
return `0${hexString}`;
} else {
return hexString;
}
}
function decompressPointBytes(bytes) {
const hexString = bytes.reduce((acc, byte, ii) => `${byteToHex(ii === 31 ? byte & -129 : byte)}${acc}`, "");
const integerLiteralString = `0x${hexString}`;
return BigInt(integerLiteralString);
}
function compressedPointBytesAreOnCurve(bytes) {
if (bytes.byteLength !== 32) {
return false;
}
const y = decompressPointBytes(bytes);
return pointIsOnCurve(y, bytes[31]);
}
// src/program-derived-address.ts
function isProgramDerivedAddress(value) {
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && typeof value[1] === "number" && value[1] >= 0 && value[1] <= 255 && isAddress(value[0]);
}
function assertIsProgramDerivedAddress(value) {
const validFormat = Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && typeof value[1] === "number";
if (!validFormat) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__MALFORMED_PDA);
}
if (value[1] < 0 || value[1] > 255) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__PDA_BUMP_SEED_OUT_OF_RANGE, {
bump: value[1]
});
}
assertIsAddress(value[0]);
}
var MAX_SEED_LENGTH = 32;
var MAX_SEEDS = 16;
var PDA_MARKER_BYTES = [
// The string 'ProgramDerivedAddress'
80,
114,
111,
103,
114,
97,
109,
68,
101,
114,
105,
118,
101,
100,
65,
100,
100,
114,
101,
115,
115
];
async function createProgramDerivedAddress({ programAddress, seeds }) {
assertDigestCapabilityIsAvailable();
if (seeds.length > MAX_SEEDS) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__MAX_NUMBER_OF_PDA_SEEDS_EXCEEDED, {
actual: seeds.length,
maxSeeds: MAX_SEEDS
});
}
let textEncoder;
const seedBytes = seeds.reduce((acc, seed, ii) => {
const bytes = typeof seed === "string" ? (textEncoder ||= new TextEncoder()).encode(seed) : seed;
if (bytes.byteLength > MAX_SEED_LENGTH) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__MAX_PDA_SEED_LENGTH_EXCEEDED, {
actual: bytes.byteLength,
index: ii,
maxSeedLength: MAX_SEED_LENGTH
});
}
acc.push(...bytes);
return acc;
}, []);
const base58EncodedAddressCodec = getAddressCodec();
const programAddressBytes = base58EncodedAddressCodec.encode(programAddress);
const addressBytesBuffer = await crypto.subtle.digest(
"SHA-256",
new Uint8Array([...seedBytes, ...programAddressBytes, ...PDA_MARKER_BYTES])
);
const addressBytes = new Uint8Array(addressBytesBuffer);
if (compressedPointBytesAreOnCurve(addressBytes)) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__INVALID_SEEDS_POINT_ON_CURVE);
}
return base58EncodedAddressCodec.decode(addressBytes);
}
async function getProgramDerivedAddress({
programAddress,
seeds
}) {
let bumpSeed = 255;
while (bumpSeed > 0) {
try {
const address2 = await createProgramDerivedAddress({
programAddress,
seeds: [...seeds, new Uint8Array([bumpSeed])]
});
return [address2, bumpSeed];
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__ADDRESSES__INVALID_SEEDS_POINT_ON_CURVE)) {
bumpSeed--;
} else {
throw e;
}
}
}
throw new SolanaError(SOLANA_ERROR__ADDRESSES__FAILED_TO_FIND_VIABLE_PDA_BUMP_SEED);
}
async function createAddressWithSeed({ baseAddress, programAddress, seed }) {
const { encode, decode } = getAddressCodec();
const seedBytes = typeof seed === "string" ? new TextEncoder().encode(seed) : seed;
if (seedBytes.byteLength > MAX_SEED_LENGTH) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__MAX_PDA_SEED_LENGTH_EXCEEDED, {
actual: seedBytes.byteLength,
index: 0,
maxSeedLength: MAX_SEED_LENGTH
});
}
const programAddressBytes = encode(programAddress);
if (programAddressBytes.length >= PDA_MARKER_BYTES.length && programAddressBytes.slice(-PDA_MARKER_BYTES.length).every((byte, index) => byte === PDA_MARKER_BYTES[index])) {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__PDA_ENDS_WITH_PDA_MARKER);
}
const addressBytesBuffer = await crypto.subtle.digest(
"SHA-256",
new Uint8Array([...encode(baseAddress), ...seedBytes, ...programAddressBytes])
);
const addressBytes = new Uint8Array(addressBytesBuffer);
return decode(addressBytes);
}
async function getAddressFromPublicKey(publicKey) {
assertKeyExporterIsAvailable();
if (publicKey.type !== "public" || publicKey.algorithm.name !== "Ed25519") {
throw new SolanaError(SOLANA_ERROR__ADDRESSES__INVALID_ED25519_PUBLIC_KEY);
}
const publicKeyBytes = await crypto.subtle.exportKey("raw", publicKey);
return getAddressDecoder().decode(new Uint8Array(publicKeyBytes));
}
async function getPublicKeyFromAddress(address2) {
const addressBytes = getAddressEncoder().encode(address2);
return await crypto.subtle.importKey("raw", addressBytes, { name: "Ed25519" }, true, ["verify"]);
}
export { address, assertIsAddress, assertIsProgramDerivedAddress, createAddressWithSeed, getAddressCodec, getAddressComparator, getAddressDecoder, getAddressEncoder, getAddressFromPublicKey, getProgramDerivedAddress, getPublicKeyFromAddress, isAddress, isProgramDerivedAddress };
//# sourceMappingURL=index.browser.mjs.map
//# sourceMappingURL=index.browser.mjs.map