@gorbchain-xyz/chaindecode
Version:
GorbchainSDK V1.3+ - Complete Solana development toolkit with advanced cryptography, messaging, and collaboration features. Build secure applications with blockchain, DeFi, and end-to-end encryption.
525 lines (524 loc) • 19.5 kB
JavaScript
// SPL Token Instruction Types (discriminators)
export var SPLTokenInstruction;
(function (SPLTokenInstruction) {
SPLTokenInstruction[SPLTokenInstruction["InitializeMint"] = 0] = "InitializeMint";
SPLTokenInstruction[SPLTokenInstruction["InitializeAccount"] = 1] = "InitializeAccount";
SPLTokenInstruction[SPLTokenInstruction["InitializeMultisig"] = 2] = "InitializeMultisig";
SPLTokenInstruction[SPLTokenInstruction["Transfer"] = 3] = "Transfer";
SPLTokenInstruction[SPLTokenInstruction["Approve"] = 4] = "Approve";
SPLTokenInstruction[SPLTokenInstruction["Revoke"] = 5] = "Revoke";
SPLTokenInstruction[SPLTokenInstruction["SetAuthority"] = 6] = "SetAuthority";
SPLTokenInstruction[SPLTokenInstruction["MintTo"] = 7] = "MintTo";
SPLTokenInstruction[SPLTokenInstruction["Burn"] = 8] = "Burn";
SPLTokenInstruction[SPLTokenInstruction["CloseAccount"] = 9] = "CloseAccount";
SPLTokenInstruction[SPLTokenInstruction["FreezeAccount"] = 10] = "FreezeAccount";
SPLTokenInstruction[SPLTokenInstruction["ThawAccount"] = 11] = "ThawAccount";
SPLTokenInstruction[SPLTokenInstruction["TransferChecked"] = 12] = "TransferChecked";
SPLTokenInstruction[SPLTokenInstruction["ApproveChecked"] = 13] = "ApproveChecked";
SPLTokenInstruction[SPLTokenInstruction["MintToChecked"] = 14] = "MintToChecked";
SPLTokenInstruction[SPLTokenInstruction["BurnChecked"] = 15] = "BurnChecked";
SPLTokenInstruction[SPLTokenInstruction["InitializeAccount2"] = 16] = "InitializeAccount2";
SPLTokenInstruction[SPLTokenInstruction["SyncNative"] = 17] = "SyncNative";
SPLTokenInstruction[SPLTokenInstruction["InitializeAccount3"] = 18] = "InitializeAccount3";
SPLTokenInstruction[SPLTokenInstruction["InitializeMultisig2"] = 19] = "InitializeMultisig2";
SPLTokenInstruction[SPLTokenInstruction["InitializeMint2"] = 20] = "InitializeMint2";
})(SPLTokenInstruction || (SPLTokenInstruction = {}));
// Authority Types
export var AuthorityType;
(function (AuthorityType) {
AuthorityType[AuthorityType["MintTokens"] = 0] = "MintTokens";
AuthorityType[AuthorityType["FreezeAccount"] = 1] = "FreezeAccount";
AuthorityType[AuthorityType["AccountOwner"] = 2] = "AccountOwner";
AuthorityType[AuthorityType["CloseAccount"] = 3] = "CloseAccount";
})(AuthorityType || (AuthorityType = {}));
/**
* Main SPL Token decoder function
*/
export function decodeSPLTokenInstruction(instruction) {
const data = instruction.data;
if (!data || data.length === 0) {
throw new Error('Invalid SPL Token instruction: no data');
}
const instructionType = data[0];
const programId = instruction.programId;
switch (instructionType) {
case SPLTokenInstruction.Transfer:
return decodeTransfer(instruction, programId);
case SPLTokenInstruction.MintTo:
return decodeMintTo(instruction, programId);
case SPLTokenInstruction.Burn:
return decodeBurn(instruction, programId);
case SPLTokenInstruction.InitializeMint:
return decodeInitializeMint(instruction, programId);
case SPLTokenInstruction.InitializeAccount:
return decodeInitializeAccount(instruction, programId);
case SPLTokenInstruction.SetAuthority:
return decodeSetAuthority(instruction, programId);
case SPLTokenInstruction.Approve:
return decodeApprove(instruction, programId);
case SPLTokenInstruction.Revoke:
return decodeRevoke(instruction, programId);
case SPLTokenInstruction.CloseAccount:
return decodeCloseAccount(instruction, programId);
case SPLTokenInstruction.FreezeAccount:
return decodeFreezeAccount(instruction, programId);
case SPLTokenInstruction.ThawAccount:
return decodeThawAccount(instruction, programId);
case SPLTokenInstruction.TransferChecked:
return decodeTransferChecked(instruction, programId);
case SPLTokenInstruction.ApproveChecked:
return decodeApproveChecked(instruction, programId);
case SPLTokenInstruction.MintToChecked:
return decodeMintToChecked(instruction, programId);
case SPLTokenInstruction.BurnChecked:
return decodeBurnChecked(instruction, programId);
case SPLTokenInstruction.SyncNative:
return decodeSyncNative(instruction, programId);
default:
return {
type: 'spl-token-unknown',
programId,
data: {
instructionType,
error: `Unknown SPL Token instruction type: ${instructionType}`
},
accounts: instruction.accounts,
raw: instruction
};
}
}
/**
* Enhanced SPL Token instruction decoder with amount extraction
*/
export function decodeSPLTokenInstructionWithDetails(data) {
if (data.length === 0) {
return {
type: 'spl-token-unknown',
instruction: 'Unknown SPL Token instruction',
accounts: []
};
}
const instructionType = data[0];
switch (instructionType) {
case 3: // Transfer
if (data.length >= 9) {
const amount = new DataView(data.buffer, data.byteOffset + 1, 8).getBigUint64(0, true);
return {
type: 'spl-token-transfer',
instruction: 'Transfer tokens',
amount,
accounts: []
};
}
break;
case 7: // MintTo
if (data.length >= 9) {
const amount = new DataView(data.buffer, data.byteOffset + 1, 8).getBigUint64(0, true);
return {
type: 'spl-token-mint-to',
instruction: 'Mint tokens',
amount,
accounts: []
};
}
break;
case 8: // Burn
if (data.length >= 9) {
const amount = new DataView(data.buffer, data.byteOffset + 1, 8).getBigUint64(0, true);
return {
type: 'spl-token-burn',
instruction: 'Burn tokens',
amount,
accounts: []
};
}
break;
case 4: // Approve
if (data.length >= 9) {
const amount = new DataView(data.buffer, data.byteOffset + 1, 8).getBigUint64(0, true);
return {
type: 'spl-token-approve',
instruction: 'Approve token spending',
amount,
accounts: []
};
}
break;
case 5: // Revoke
return {
type: 'spl-token-revoke',
instruction: 'Revoke token approval',
accounts: []
};
case 9: // CloseAccount
return {
type: 'spl-token-close-account',
instruction: 'Close token account',
accounts: []
};
case 10: // FreezeAccount
return {
type: 'spl-token-freeze-account',
instruction: 'Freeze token account',
accounts: []
};
case 11: // ThawAccount
return {
type: 'spl-token-thaw-account',
instruction: 'Thaw token account',
accounts: []
};
case 0: // InitializeMint
if (data.length >= 51) {
const decimals = data[1];
return {
type: 'spl-token-initialize-mint',
instruction: `Initialize mint with ${decimals} decimals`,
decimals,
accounts: []
};
}
break;
case 1: // InitializeAccount
return {
type: 'spl-token-initialize-account',
instruction: 'Initialize token account',
accounts: []
};
default:
return {
type: 'spl-token-unknown',
instruction: `Unknown SPL Token instruction (type: ${instructionType})`,
accounts: []
};
}
return {
type: 'spl-token-unknown',
instruction: 'Unknown SPL Token instruction',
accounts: []
};
}
/**
* Decode base58 instruction data to Uint8Array
*/
export function decodeInstructionData(base58Data) {
try {
// Simple base58 decode - in production use proper base58 library
const bytes = new Uint8Array(Buffer.from(base58Data, 'base64'));
return bytes;
}
catch (error) {
// Failed to decode instruction data - return empty array
return new Uint8Array(0);
}
}
/**
* Decode Transfer instruction (3)
* Layout: [u8 instruction, u64 amount]
*/
function decodeTransfer(instruction, programId) {
var _a, _b, _c;
const data = instruction.data;
if (data.length !== 9) {
throw new Error('Invalid Transfer instruction data length');
}
const amount = readU64LE(data, 1);
const accounts = instruction.accounts;
return {
type: 'spl-token-transfer',
programId,
data: {
amount: amount.toString(),
source: (_a = accounts[0]) !== null && _a !== void 0 ? _a : null,
destination: (_b = accounts[1]) !== null && _b !== void 0 ? _b : null,
authority: (_c = accounts[2]) !== null && _c !== void 0 ? _c : null,
signers: accounts.slice(3)
},
accounts,
raw: instruction
};
}
/**
* Decode MintTo instruction (7)
* Layout: [u8 instruction, u64 amount]
*/
function decodeMintTo(instruction, programId) {
var _a, _b, _c;
const data = instruction.data;
if (data.length !== 9) {
throw new Error('Invalid MintTo instruction data length');
}
const amount = readU64LE(data, 1);
const accounts = instruction.accounts;
return {
type: 'spl-token-mint-to',
programId,
data: {
amount: amount.toString(),
mint: (_a = accounts[0]) !== null && _a !== void 0 ? _a : null,
account: (_b = accounts[1]) !== null && _b !== void 0 ? _b : null,
authority: (_c = accounts[2]) !== null && _c !== void 0 ? _c : null,
signers: accounts.slice(3)
},
accounts,
raw: instruction
};
}
/**
* Decode Burn instruction (8)
* Layout: [u8 instruction, u64 amount]
*/
function decodeBurn(instruction, programId) {
var _a, _b, _c, _d;
const data = instruction.data;
if (data.length !== 9) {
throw new Error('Invalid Burn instruction data length');
}
const amount = readU64LE(data, 1);
const accounts = (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [];
return {
type: 'spl-token-burn',
programId,
data: {
amount: amount.toString(),
account: (_b = accounts[0]) !== null && _b !== void 0 ? _b : null,
mint: (_c = accounts[1]) !== null && _c !== void 0 ? _c : null,
authority: (_d = accounts[2]) !== null && _d !== void 0 ? _d : null,
signers: accounts.slice(3)
},
accounts,
raw: instruction
};
}
/**
* Decode InitializeMint instruction (0)
* Layout: [u8 instruction, u8 decimals, [u8; 32] mint_authority, Option<[u8; 32]> freeze_authority]
*/
function decodeInitializeMint(instruction, programId) {
var _a;
const data = instruction.data;
if (data.length !== 67) {
throw new Error('Invalid InitializeMint instruction data length');
}
const decimals = data[1];
const mintAuthority = data.slice(2, 34);
const freezeAuthorityOption = data[34];
const freezeAuthority = freezeAuthorityOption ? data.slice(35, 67) : null;
return {
type: 'spl-token-initialize-mint',
programId,
data: {
decimals,
mintAuthority: bufferToBase58(mintAuthority),
freezeAuthority: freezeAuthority ? bufferToBase58(freezeAuthority) : null
},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
/**
* Decode InitializeAccount instruction (1)
*/
function decodeInitializeAccount(instruction, programId) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const accounts = (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [];
return {
type: 'spl-token-initialize-account',
programId,
data: {
account: (_c = (_b = accounts[0]) === null || _b === void 0 ? void 0 : _b.address) !== null && _c !== void 0 ? _c : accounts[0],
mint: (_e = (_d = accounts[1]) === null || _d === void 0 ? void 0 : _d.address) !== null && _e !== void 0 ? _e : accounts[1],
owner: (_g = (_f = accounts[2]) === null || _f === void 0 ? void 0 : _f.address) !== null && _g !== void 0 ? _g : accounts[2],
rentSysvar: (_j = (_h = accounts[3]) === null || _h === void 0 ? void 0 : _h.address) !== null && _j !== void 0 ? _j : accounts[3]
},
accounts,
raw: instruction
};
}
/**
* Decode SetAuthority instruction (6)
* Layout: [u8 instruction, u8 authority_type, Option<[u8; 32]> new_authority]
*/
function decodeSetAuthority(instruction, programId) {
var _a;
const data = instruction.data;
if (data.length !== 35) {
throw new Error('Invalid SetAuthority instruction data length');
}
const authorityType = data[1];
const newAuthorityOption = data[2];
const newAuthority = newAuthorityOption ? data.slice(3, 35) : null;
return {
type: 'spl-token-set-authority',
programId,
data: {
authorityType: getAuthorityTypeName(authorityType),
newAuthority: newAuthority ? bufferToBase58(newAuthority) : null
},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
/**
* Decode other instructions with simple layouts
*/
function decodeApprove(instruction, programId) {
var _a, _b, _c;
const data = instruction.data;
const amount = readU64LE(data, 1);
const accounts = instruction.accounts;
return {
type: 'spl-token-approve',
programId,
data: {
amount: amount.toString(),
source: (_a = accounts[0]) !== null && _a !== void 0 ? _a : null, // Token account
delegate: (_b = accounts[1]) !== null && _b !== void 0 ? _b : null, // Delegate account
authority: (_c = accounts[2]) !== null && _c !== void 0 ? _c : null, // Authority (owner)
signers: accounts.slice(3)
},
accounts,
raw: instruction
};
}
function decodeRevoke(instruction, programId) {
var _a;
return {
type: 'spl-token-revoke',
programId,
data: {},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeCloseAccount(instruction, programId) {
var _a;
return {
type: 'spl-token-close-account',
programId,
data: {},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeFreezeAccount(instruction, programId) {
var _a;
return {
type: 'spl-token-freeze-account',
programId,
data: {},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeThawAccount(instruction, programId) {
var _a;
return {
type: 'spl-token-thaw-account',
programId,
data: {},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeTransferChecked(instruction, programId) {
var _a;
const data = instruction.data;
const amount = readU64LE(data, 1);
const decimals = data[9];
return {
type: 'spl-token-transfer-checked',
programId,
data: { amount: amount.toString(), decimals },
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeApproveChecked(instruction, programId) {
var _a;
const data = instruction.data;
const amount = readU64LE(data, 1);
const decimals = data[9];
return {
type: 'spl-token-approve-checked',
programId,
data: { amount: amount.toString(), decimals },
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeMintToChecked(instruction, programId) {
var _a;
const data = instruction.data;
const amount = readU64LE(data, 1);
const decimals = data[9];
return {
type: 'spl-token-mint-to-checked',
programId,
data: { amount: amount.toString(), decimals },
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeBurnChecked(instruction, programId) {
var _a;
const data = instruction.data;
const amount = readU64LE(data, 1);
const decimals = data[9];
return {
type: 'spl-token-burn-checked',
programId,
data: { amount: amount.toString(), decimals },
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
function decodeSyncNative(instruction, programId) {
var _a;
return {
type: 'spl-token-sync-native',
programId,
data: {},
accounts: (_a = instruction.accounts) !== null && _a !== void 0 ? _a : [],
raw: instruction
};
}
// Utility functions
function readU64LE(buffer, offset) {
// Read 64-bit little-endian integer as string to avoid BigInt requirement
let result = 0;
let multiplier = 1;
// Read the lower 32 bits
for (let i = 0; i < 4; i++) {
result += buffer[offset + i] * multiplier;
multiplier *= 256;
}
// For simplicity, ignore the upper 32 bits if they would cause overflow
// In production, you'd want proper 64-bit integer handling
let upper = 0;
multiplier = 1;
for (let i = 4; i < 8; i++) {
upper += buffer[offset + i] * multiplier;
multiplier *= 256;
}
// Combine lower and upper parts, handling overflow by returning as string
const total = result + (upper * Math.pow(2, 32));
return total.toString();
}
function bufferToBase58(buffer) {
// Simplified base58 encoding - in production, use a proper base58 library
// For now, return hex representation
return Array.from(buffer).map((b) => {
const hex = b.toString(16);
return hex.length === 1 ? `0${hex}` : hex;
}).join('');
}
function getAuthorityTypeName(type) {
switch (type) {
case AuthorityType.MintTokens: return 'MintTokens';
case AuthorityType.FreezeAccount: return 'FreezeAccount';
case AuthorityType.AccountOwner: return 'AccountOwner';
case AuthorityType.CloseAccount: return 'CloseAccount';
default: return `Unknown(${type})`;
}
}