UNPKG

@wormhole-foundation/sdk-algorand-core

Version:

SDK for Algorand, used in conjunction with @wormhole-foundation/sdk

237 lines 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StorageLogicSig = exports.varint = exports.MAX_BITS = exports.MAX_BYTES = exports.BITS_PER_KEY = exports.BITS_PER_BYTE = exports.MAX_BYTES_PER_KEY = exports.MAX_KEYS = exports.SEED_AMT = void 0; const sdk_connect_1 = require("@wormhole-foundation/sdk-connect"); const algosdk_1 = require("algosdk"); const sdk_algorand_1 = require("@wormhole-foundation/sdk-algorand"); exports.SEED_AMT = 1002000; exports.MAX_KEYS = 15; exports.MAX_BYTES_PER_KEY = 127; exports.BITS_PER_BYTE = 8; exports.BITS_PER_KEY = exports.MAX_BYTES_PER_KEY * exports.BITS_PER_BYTE; exports.MAX_BYTES = exports.MAX_BYTES_PER_KEY * exports.MAX_KEYS; exports.MAX_BITS = exports.BITS_PER_BYTE * exports.MAX_BYTES; // Useful for encoding numbers as varints to patch TEAL binary exports.varint = { // Forever grateful to https://github.com/joeltg/big-varint/blob/main/src/unsigned.ts _limit: 0x7f, encodingLength: (value) => { let i = 0; for (; value >= 0x80; i++) value >>= 7; return i + 1; }, encode: (i, buffer, byteOffset) => { if (typeof i === "bigint") i = (0, sdk_algorand_1.safeBigIntToNumber)(i); if (i < 0) throw new RangeError("value must be unsigned"); const byteLength = exports.varint.encodingLength(i); buffer = buffer || new ArrayBuffer(byteLength); byteOffset = byteOffset || 0; if (buffer.byteLength < byteOffset + byteLength) throw new RangeError("the buffer is too small to encode the number at the offset"); const array = new Uint8Array(buffer, byteOffset); let offset = 0; while (exports.varint._limit < i) { array[offset++] = (i & exports.varint._limit) | 0x80; i >>= 7; } array[offset] = Number(i); return array; }, decode: (data, offset = 0) => { let i = 0; let n = 0; let b; do { b = data[offset + n]; if (b === undefined) throw new RangeError("offset out of range"); i += (b & exports.varint._limit) << (n * 7); n++; } while (0x80 <= b); return i; }, }; exports.StorageLogicSig = { // Get the storage lsig for a Wormhole message ID forMessageId: (appId, whm) => { const appAddress = (0, algosdk_1.decodeAddress)((0, algosdk_1.getApplicationAddress)(appId)).publicKey; const emitterAddr = whm.emitter.toUniversalAddress().toUint8Array(); const chainIdBytes = sdk_connect_1.encoding.bignum.toBytes(BigInt((0, sdk_connect_1.toChainId)(whm.chain)), 2); const address = sdk_connect_1.encoding.bytes.concat(chainIdBytes, emitterAddr); return exports.StorageLogicSig.fromData({ appId, appAddress, idx: whm.sequence / BigInt(exports.MAX_BITS), address, }); }, // Get the storage lsig for a wrapped asset forWrappedAsset: (appId, token) => { if ((0, sdk_connect_1.isNative)(token.address)) throw new Error("native asset cannot be a wrapped asset"); const appAddress = (0, algosdk_1.decodeAddress)((0, algosdk_1.getApplicationAddress)(appId)).publicKey; return exports.StorageLogicSig.fromData({ appId, appAddress, idx: BigInt((0, sdk_connect_1.toChainId)(token.chain)), address: token.address.toUniversalAddress().toUint8Array(), }); }, // Get the storage lsig for a native asset forNativeAsset: (appId, tokenId) => { const appAddress = (0, algosdk_1.decodeAddress)((0, algosdk_1.getApplicationAddress)(appId)).publicKey; return exports.StorageLogicSig.fromData({ appId, appAddress, idx: tokenId, address: sdk_connect_1.encoding.bytes.encode("native"), }); }, // Get the storage lsig for the guardian set forGuardianSet: (appId, idx) => { const appAddress = (0, algosdk_1.decodeAddress)((0, algosdk_1.getApplicationAddress)(appId)).publicKey; return exports.StorageLogicSig.fromData({ appId, appAddress, idx: BigInt(idx), address: sdk_connect_1.encoding.bytes.encode("guardian"), }); }, forEmitter: (appId, emitter) => { const appAddress = (0, algosdk_1.decodeAddress)((0, algosdk_1.getApplicationAddress)(appId)).publicKey; return exports.StorageLogicSig.fromData({ appId, appAddress, idx: 0n, address: emitter, }); }, _encode: (data) => { if (typeof data === "bigint") return [sdk_connect_1.encoding.hex.encode(exports.varint.encode(data))]; return [sdk_connect_1.encoding.hex.encode(exports.varint.encode(data.length)), sdk_connect_1.encoding.hex.encode(data)]; }, fromData: (data) => { // This patches the binary of the TEAL program used to store data // to produce a logic sig that can be used to sign transactions // to store data in the its account local state for a given app const byteStrings = [ "0620010181", ...exports.StorageLogicSig._encode(data.idx), "4880", ...exports.StorageLogicSig._encode(data.address), "483110810612443119221244311881", ...exports.StorageLogicSig._encode(data.appId), "1244312080", ...exports.StorageLogicSig._encode(data.appAddress), "124431018100124431093203124431153203124422", ]; const bytecode = sdk_connect_1.encoding.hex.decode(byteStrings.join("")); return new algosdk_1.LogicSigAccount(bytecode); }, /** * Returns the local data for an application ID * @param client Algodv2 client * @param appId Application ID of interest * @param address Address of the account * @returns Promise with Uint8Array of data squirreled away */ decodeLocalState: async (client, appId, address) => { let appState; try { const ai = await client .accountApplicationInformation(address, (0, sdk_algorand_1.safeBigIntToNumber)(appId)) .do(); const acctAppInfo = algosdk_1.modelsv2.AccountApplicationResponse.from_obj_for_encoding(ai); appState = acctAppInfo.appLocalState; } catch (e) { return new Uint8Array(); } const metaKey = sdk_connect_1.encoding.b64.encode("meta"); // We don't want the data in the `meta` key // and we want to make sure the sequences come back in order // so first put them in a map by numeric key // then iterate over keys to concat them in the right order let vals = new Map(); for (const kv of appState.keyValue) { if (kv.key === metaKey) continue; // Take the first byte off the key to be the // numeric index const key = sdk_connect_1.encoding.b64.decode(kv.key)[0]; const value = sdk_connect_1.encoding.b64.decode(kv.value.bytes); vals.set(key, value); } const byteArrays = []; for (let i = 0; i < exports.MAX_KEYS; i++) { if (vals.has(i)) byteArrays.push(vals.get(i)); } return sdk_connect_1.encoding.bytes.concat(...byteArrays); }, /** * This function is used to check if a VAA has been redeemed by looking at a specific bit * @param client AlgodV2 client * @param appId Application Id * @param addr Wallet address. Someone has to pay for this * @param seq The sequence number of the redemption * @returns True, if the bit was set and VAA was redeemed, False otherwise */ checkBitsSet: async (client, appId, addr, seq) => { let retval = false; let appState; const acctInfoResp = await client.accountInformation(addr).do(); const acctInfo = algosdk_1.modelsv2.Account.from_obj_for_encoding(acctInfoResp); const als = acctInfo.appsLocalState; als && als.forEach((app) => { if (BigInt(app.id) === appId) { appState = app.keyValue; } }); if (appState?.length === 0) { return retval; } const BIG_MAX_BITS = BigInt(exports.MAX_BITS); const BIG_EIGHT = BigInt(8); // Start on a MAX_BITS boundary const start = (seq / BIG_MAX_BITS) * BIG_MAX_BITS; // beg should be in the range [0..MAX_BITS] const beg = (0, sdk_algorand_1.safeBigIntToNumber)(seq - start); // s should be in the range [0..15] const s = Math.floor(beg / exports.BITS_PER_KEY); const b = Math.floor((beg - s * exports.BITS_PER_KEY) / 8); const key = sdk_connect_1.encoding.b64.encode(sdk_connect_1.encoding.bignum.toBytes(s, 1)); appState?.forEach((kv) => { if (kv.key === key) { const v = Buffer.from(kv.value.bytes, "base64"); const bt = 1 << (0, sdk_algorand_1.safeBigIntToNumber)(seq % BIG_EIGHT); retval = (v[b] & bt) != 0; // Added non-null assertion return; } }); return retval; }, /** * Checks to see if the account exists for the application * @param client An Algodv2 client * @param appId Application ID * @param acctAddr Account address to check * @returns True, if account exists for application, False otherwise */ storageAccountExists: async (client, address, appId) => { try { const acctAppInfo = await client .accountApplicationInformation(address, (0, sdk_algorand_1.safeBigIntToNumber)(appId)) .do(); return Object.keys(acctAppInfo).length > 0; } catch { } return false; }, }; //# sourceMappingURL=storage.js.map