@hapi.one/solana-client
Version:
Client library for Solana smart contract for #HAPI
1,310 lines (1,290 loc) • 57.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var web3_js = require('@solana/web3.js');
var borsh = require('borsh');
var BN = require('bn.js');
var bs58 = require('bs58');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var BN__default = /*#__PURE__*/_interopDefaultLegacy(BN);
class u8 extends BN__default["default"] {
constructor(...args) {
super(...args);
Object.setPrototypeOf(this, u8.prototype);
}
/**
* Convert to Buffer representation
*/
toBuffer() {
return bnToBuffer(this, u8.size);
}
/**
* Construct a Numberu8 from Buffer representation
*/
static fromBuffer(buffer) {
if (buffer.length !== u8.size) {
throw new Error(`Invalid buffer length: ${buffer.length}`);
}
return new BN__default["default"]([...buffer]
.reverse()
.map((i) => `00${i.toString(4)}`.slice(-2))
.join(""), 16);
}
}
u8.size = 1;
class u32 extends BN__default["default"] {
constructor(...args) {
super(...args);
Object.setPrototypeOf(this, u32.prototype);
}
/**
* Convert to Buffer representation
*/
toBuffer() {
return bnToBuffer(this, u32.size);
}
/**
* Construct a Numberu32 from Buffer representation
*/
static fromBuffer(buffer) {
if (buffer.length !== u32.size) {
throw new Error(`Invalid buffer length: ${buffer.length}`);
}
return new BN__default["default"]([...buffer]
.reverse()
.map((i) => `00${i.toString(16)}`.slice(-2))
.join(""), 16);
}
}
u32.size = 4;
class u64 extends BN__default["default"] {
constructor(...args) {
super(...args);
Object.setPrototypeOf(this, u64.prototype);
}
/**
* Convert to Buffer representation
*/
toBuffer() {
return bnToBuffer(this, u64.size);
}
/**
* Construct a Numberu64 from Buffer representation
*/
static fromBuffer(buffer) {
if (buffer.length !== u64.size) {
throw new Error(`Invalid buffer length: ${buffer.length}`);
}
return new BN__default["default"]([...buffer]
.reverse()
.map((i) => `00${i.toString(16)}`.slice(-2))
.join(""), 16);
}
}
u64.size = 8;
function bnToBuffer(bn, size) {
const a = bn.toArray().reverse();
const b = Buffer.from(a);
if (b.length === size) {
return b;
}
if (b.length > size) {
throw new Error(`Buffer too large (size: ${size})`);
}
const zeroPad = Buffer.alloc(size);
b.copy(zeroPad);
return zeroPad;
}
function base58ToPublicKey(address) {
const buffer = bs58.decode(address);
return new web3_js.PublicKey(buffer);
}
// This enum must match the order of a smart contract enum
exports.HapiInstruction = void 0;
(function (HapiInstruction) {
HapiInstruction[HapiInstruction["CreateCommunity"] = 0] = "CreateCommunity";
HapiInstruction[HapiInstruction["UpdateCommunity"] = 1] = "UpdateCommunity";
HapiInstruction[HapiInstruction["CreateNetwork"] = 2] = "CreateNetwork";
HapiInstruction[HapiInstruction["UpdateNetwork"] = 3] = "UpdateNetwork";
HapiInstruction[HapiInstruction["CreateReporter"] = 4] = "CreateReporter";
HapiInstruction[HapiInstruction["UpdateReporter"] = 5] = "UpdateReporter";
HapiInstruction[HapiInstruction["CreateCase"] = 6] = "CreateCase";
HapiInstruction[HapiInstruction["UpdateCase"] = 7] = "UpdateCase";
HapiInstruction[HapiInstruction["CreateAddress"] = 8] = "CreateAddress";
HapiInstruction[HapiInstruction["UpdateAddress"] = 9] = "UpdateAddress";
})(exports.HapiInstruction || (exports.HapiInstruction = {}));
// This enum must match the order of a smart contract enum
exports.HapiAccountType = void 0;
(function (HapiAccountType) {
HapiAccountType[HapiAccountType["Uninitialized"] = 0] = "Uninitialized";
HapiAccountType[HapiAccountType["Community"] = 1] = "Community";
HapiAccountType[HapiAccountType["Network"] = 2] = "Network";
HapiAccountType[HapiAccountType["Reporter"] = 3] = "Reporter";
HapiAccountType[HapiAccountType["Case"] = 4] = "Case";
HapiAccountType[HapiAccountType["Address"] = 5] = "Address";
})(exports.HapiAccountType || (exports.HapiAccountType = {}));
// This enum must match the order of a smart contract enum
exports.ReporterType = void 0;
(function (ReporterType) {
ReporterType[ReporterType["Inactive"] = 0] = "Inactive";
ReporterType[ReporterType["Tracer"] = 1] = "Tracer";
ReporterType[ReporterType["Full"] = 2] = "Full";
ReporterType[ReporterType["Authority"] = 3] = "Authority";
})(exports.ReporterType || (exports.ReporterType = {}));
// This enum must match the order of a smart contract enum
exports.Category = void 0;
(function (Category) {
// Tier 0
/// Safe
Category[Category["Safe"] = 0] = "Safe";
// Tier 1 - Low risk
/// Wallet service - custodial or mixed wallets
Category[Category["WalletService"] = 1] = "WalletService";
/// Merchant service
Category[Category["MerchantService"] = 2] = "MerchantService";
/// Mining pool
Category[Category["MiningPool"] = 4] = "MiningPool";
/// Exchange (Low Risk) - Exchange with high KYC standards
Category[Category["LowRiskExchange"] = 8] = "LowRiskExchange";
// Tier 2 - Medium risk
/// Exchange (Medium Risk)
Category[Category["MediumRiskExchange"] = 16] = "MediumRiskExchange";
/// DeFi application
Category[Category["DeFi"] = 32] = "DeFi";
/// OTC Broker
Category[Category["OTCBroker"] = 64] = "OTCBroker";
/// Cryptocurrency ATM
Category[Category["ATM"] = 128] = "ATM";
/// Gambling
Category[Category["Gambling"] = 256] = "Gambling";
// Tier 3 - High risk
/// Illicit organization
Category[Category["IllicitOrganization"] = 512] = "IllicitOrganization";
/// Mixer
Category[Category["Mixer"] = 1024] = "Mixer";
/// Darknet market or service
Category[Category["DarknetService"] = 2048] = "DarknetService";
/// Scam
Category[Category["Scam"] = 4096] = "Scam";
/// Ransomware
Category[Category["Ransomware"] = 8192] = "Ransomware";
/// Theft - stolen funds
Category[Category["Theft"] = 16384] = "Theft";
/// Counterfeit - fake assets that try to appear as something else
Category[Category["Counterfeit"] = 32768] = "Counterfeit";
// Tier 4 - Severe risk
/// Terrorist financing
Category[Category["TerroristFinancing"] = 65536] = "TerroristFinancing";
/// Sanctions
Category[Category["Sanctions"] = 131072] = "Sanctions";
/// Child abuse and porn materials
Category[Category["ChildAbuse"] = 262144] = "ChildAbuse";
})(exports.Category || (exports.Category = {}));
const Categories = [
exports.Category.Safe,
exports.Category.WalletService,
exports.Category.MerchantService,
exports.Category.MiningPool,
exports.Category.LowRiskExchange,
exports.Category.MediumRiskExchange,
exports.Category.DeFi,
exports.Category.OTCBroker,
exports.Category.ATM,
exports.Category.Gambling,
exports.Category.IllicitOrganization,
exports.Category.Mixer,
exports.Category.DarknetService,
exports.Category.Scam,
exports.Category.Ransomware,
exports.Category.Theft,
exports.Category.Counterfeit,
exports.Category.TerroristFinancing,
exports.Category.Sanctions,
exports.Category.ChildAbuse,
];
exports.CaseStatus = void 0;
(function (CaseStatus) {
CaseStatus[CaseStatus["Open"] = 0] = "Open";
CaseStatus[CaseStatus["Closed"] = 1] = "Closed";
})(exports.CaseStatus || (exports.CaseStatus = {}));
class CommunityState {
constructor(object) {
Object.assign(this, object);
}
}
CommunityState.schema = new Map([
[
CommunityState,
{
kind: "struct",
fields: [
["account_type", "u8"],
["authority", [32]],
["next_case_id", "u64"],
["name", "string"],
],
},
],
]);
CommunityState.size = 73;
class Community {
constructor(data) {
/// HAPI account type
this.accountType = exports.HapiAccountType.Community;
if (data) {
this.authority = data.authority;
this.nextCaseId = data.nextCaseId;
this.name = data.name;
}
}
static async getAddress(programId, communityName) {
return web3_js.PublicKey.findProgramAddress(Community.getAddressSeeds(communityName), programId);
}
static getAddressSeeds(communityName) {
return [Buffer.from("community"), Buffer.from(communityName)];
}
static fromState(state) {
return new Community({
authority: new web3_js.PublicKey(state.authority),
nextCaseId: new u64(state.next_case_id),
name: state.name,
});
}
static deserialize(buffer) {
return Community.fromState(borsh.deserializeUnchecked(CommunityState.schema, CommunityState, buffer));
}
static async retrieve(programId, connection, communityName) {
const [communityAddress] = await Community.getAddress(programId, communityName);
const communityAccount = await connection.getAccountInfo(communityAddress);
if (!communityAccount) {
throw new Error(`Community not found: ${communityName} (${communityAddress})`);
}
return {
data: Community.deserialize(communityAccount.data),
account: communityAddress,
};
}
serialize() {
const buf = Buffer.alloc(CommunityState.size);
buf.set(borsh.serialize(CommunityState.schema, this.toState()));
return buf;
}
toState() {
return new CommunityState({
account_type: this.accountType,
authority: this.authority.toBytes(),
next_case_id: this.nextCaseId,
name: this.name,
});
}
}
Community.size = CommunityState.size;
class NetworkState {
constructor(object) {
Object.assign(this, object);
}
}
NetworkState.schema = new Map([
[
NetworkState,
{
kind: "struct",
fields: [
["account_type", "u8"],
["name", "string"],
],
},
],
]);
NetworkState.size = 33;
class Network {
constructor(object) {
/// HAPI account type
this.accountType = exports.HapiAccountType.Network;
if (object) {
Object.assign(this, object);
}
}
static async getAddress(programId, communityAddress, networkName) {
return web3_js.PublicKey.findProgramAddress([
Buffer.from("network"),
communityAddress.toBuffer(),
Buffer.from(networkName),
], programId);
}
static fromState(state) {
const network = new Network();
network.accountType = state.account_type;
network.name = state.name;
return network;
}
static deserialize(buffer) {
return Network.fromState(borsh.deserializeUnchecked(NetworkState.schema, NetworkState, buffer));
}
static async retrieve(programId, connection, communityName, networkName) {
const [communityAddress] = await Community.getAddress(programId, communityName);
const [networkAddress] = await Network.getAddress(programId, communityAddress, networkName);
const account = await connection.getAccountInfo(networkAddress);
if (!account) {
throw new Error(`Network not found: "${networkName}" (${networkAddress}) in community "${communityName}" (${communityAddress})`);
}
return { data: Network.deserialize(account.data), account: networkAddress };
}
serialize() {
const buf = Buffer.alloc(NetworkState.size);
buf.set(borsh.serialize(NetworkState.schema, this.toState()));
return buf;
}
toState() {
return new NetworkState({
account_type: this.accountType,
name: this.name,
});
}
}
Network.size = NetworkState.size;
class AddressState {
constructor(object) {
Object.assign(this, object);
}
}
AddressState.schema = new Map([
[
AddressState,
{
kind: "struct",
fields: [
["account_type", "u8"],
["risk", "u8"],
["case_id", "u64"],
["category", "u8"],
],
},
],
]);
AddressState.size = 11;
class Address {
constructor(data) {
/// HAPI account type
this.accountType = exports.HapiAccountType.Address;
if (data) {
Object.assign(this, data);
}
}
static async getAddress(programId, networkAddress, address) {
return web3_js.PublicKey.findProgramAddress([Buffer.from("address"), networkAddress.toBuffer(), address.toBuffer()], programId);
}
static fromState(state) {
return new Address({
accountType: state.account_type,
risk: state.risk,
caseId: state.case_id,
category: Categories[state.category],
});
}
static deserialize(buffer) {
return Address.fromState(borsh.deserializeUnchecked(AddressState.schema, AddressState, buffer));
}
static async retrieve(programId, connection, communityName, networkName, address) {
const [communityAddress] = await Community.getAddress(programId, communityName);
const [networkAddress] = await Network.getAddress(programId, communityAddress, networkName);
const [addressAddress] = await Address.getAddress(programId, networkAddress, address);
const account = await connection.getAccountInfo(addressAddress);
if (!account) {
throw new Error(`Address not found: "${address}" in network "${networkName}" (${networkAddress}) in community "${communityName}" (${communityAddress})`);
}
return { data: Address.deserialize(account.data), account: addressAddress };
}
serialize() {
const buf = Buffer.alloc(AddressState.size);
buf.set(borsh.serialize(AddressState.schema, this.toState()));
return buf;
}
toState() {
return new AddressState({
account_type: this.accountType,
risk: this.risk,
case_id: this.caseId,
category: Categories.indexOf(this.category),
});
}
}
class CaseState {
constructor(object) {
Object.assign(this, object);
}
}
CaseState.schema = new Map([
[
CaseState,
{
kind: "struct",
fields: [
["account_type", "u8"],
["reporter_key", [32]],
["categories", "u32"],
["status", "u8"],
["name", "string"],
],
},
],
]);
CaseState.size = 71;
class Case {
constructor(data) {
/// HAPI account type
this.accountType = exports.HapiAccountType.Case;
if (data) {
Object.assign(this, data);
}
}
static async getAddress(programId, communityAddress, caseId) {
return web3_js.PublicKey.findProgramAddress([Buffer.from("case"), communityAddress.toBuffer(), caseId.toBuffer()], programId);
}
static fromState(state) {
return new Case({
accountType: state.account_type,
name: state.name,
reporterKey: new web3_js.PublicKey(state.reporter_key),
status: state.status,
categories: Categories.filter((category) => state.categories & category).sort(),
});
}
static deserialize(buffer) {
return Case.fromState(borsh.deserializeUnchecked(CaseState.schema, CaseState, buffer));
}
static async retrieve(programId, connection, communityName, caseId) {
const [communityAddress] = await Community.getAddress(programId, communityName);
const [caseAddress] = await Case.getAddress(programId, communityAddress, caseId);
const account = await connection.getAccountInfo(caseAddress);
if (!account) {
throw new Error("Invalid case account provided");
}
return { data: Case.deserialize(account.data), account: communityAddress };
}
serialize() {
const buf = Buffer.alloc(CaseState.size);
buf.set(borsh.serialize(CaseState.schema, this.toState()));
return buf;
}
toState() {
return new CaseState({
account_type: this.accountType,
name: this.name,
reporter_key: this.reporterKey.toBytes(),
status: this.status,
categories: this.categories.reduce((acc, category) => {
return acc | category;
}, 0),
});
}
}
class ReporterState {
constructor(object) {
Object.assign(this, object);
}
}
ReporterState.schema = new Map([
[
ReporterState,
{
kind: "struct",
fields: [
["account_type", "u8"],
["reporter_type", "u8"],
["name", "string"],
],
},
],
]);
ReporterState.size = 34;
class Reporter {
constructor(data) {
/// HAPI account type
this.accountType = exports.HapiAccountType.Reporter;
if (data) {
Object.assign(this, data);
}
}
static async getAddress(programId, communityAddress, reporterPubkey) {
return web3_js.PublicKey.findProgramAddress([
Buffer.from("reporter"),
communityAddress.toBuffer(),
reporterPubkey.toBuffer(),
], programId);
}
static fromState(state) {
return new Reporter({
accountType: state.account_type,
reporterType: state.reporter_type,
name: state.name,
});
}
static deserialize(buffer) {
return Reporter.fromState(borsh.deserializeUnchecked(ReporterState.schema, ReporterState, buffer));
}
static async retrieve(programId, connection, communityName, reporterPubkey) {
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, reporterPubkey);
const account = await connection.getAccountInfo(reporterAddress);
if (!account) {
throw new Error(`Reporter not found: "${reporterPubkey}" in community "${communityName}" (${communityAddress})`);
}
return {
data: Reporter.deserialize(account.data),
account: reporterAddress,
};
}
serialize() {
const buf = Buffer.alloc(ReporterState.size);
buf.set(borsh.serialize(ReporterState.schema, this.toState()));
return buf;
}
toState() {
return new ReporterState({
account_type: this.accountType,
reporter_type: this.reporterType,
name: this.name,
});
}
}
Reporter.size = ReporterState.size;
const HAPI_PROGRAM_ID = new web3_js.PublicKey("hapiScWyxeZy36fqXD5CcRUYFCUdid26jXaakAtcdZ7");
/** HAPI client to read entity data from Solana */
class ReaderClient {
constructor(config) {
if (typeof config.endpoint === "object") {
this.connection = config.endpoint;
}
else {
this.connection = new web3_js.Connection(config.endpoint, config.commitment);
}
this.communityName = config.communityName;
this.programId = new web3_js.PublicKey(this.programId || HAPI_PROGRAM_ID);
}
ensureCommunityName(communityName) {
if (!communityName) {
communityName = this.communityName;
}
if (!communityName) {
throw new Error("Community name not set");
}
return communityName;
}
/**
* Sets community name for context
* @param communityName Community name
* @returns Self
*/
switchCommunity(communityName) {
this.communityName = communityName;
return this;
}
/**
* Fetch community info from blockchain
* @param communityName (Optional) The name of the community to fetch (defaults to context)
* @returns Community info
**/
async getCommunity(communityName) {
communityName = this.ensureCommunityName(communityName);
const state = await Community.retrieve(this.programId, this.connection, communityName || this.communityName);
return state;
}
/**
* Fetch network info from blockchain
* @param networkName The name of the network to fetch
* @param communityName (Optional) The name of the community to fetch network from (defaults to context)
* @returns Network info
**/
async getNetwork(networkName, communityName) {
communityName = this.ensureCommunityName(communityName);
if (!networkName) {
throw new Error("Network name not specified");
}
const state = await Network.retrieve(this.programId, this.connection, communityName || this.communityName, networkName);
return state;
}
/**
* Fetch reporter info from blockchain
* @param reporterPubkey Public key of the reporter to fetch
* @param communityName (Optional) The name of the community to fetch reporter from (defaults to context)
* @returns Reporter info
**/
async getReporter(reporterPubkey, communityName) {
communityName = this.ensureCommunityName(communityName);
const state = await Reporter.retrieve(this.programId, this.connection, communityName || this.communityName, new web3_js.PublicKey(reporterPubkey));
return state;
}
/**
* Fetch case info from blockchain
* @param caseId ID of the case to fetch
* @param communityName (Optional) The name of the community to fetch case from (defaults to context)
* @returns Case info
**/
async getCase(caseId, communityName) {
communityName = this.ensureCommunityName(communityName);
const state = await Case.retrieve(this.programId, this.connection, communityName || this.communityName, caseId);
return state;
}
/**
* Fetch address info from blockchain
* @param address The address to fetch info for (string for Solana addresses, Buffer for others)
* @param networkName The name of the network to which address belongs to
* @param communityName (Optional) The name of the community to fetch case from (defaults to context)
* @returns Address info
**/
async getAddress(address, networkName, communityName) {
communityName = this.ensureCommunityName(communityName);
if (!networkName) {
throw new Error("Network name not specified");
}
const convertedAddress = address instanceof Buffer
? new web3_js.PublicKey(address)
: base58ToPublicKey(address);
const state = await Address.retrieve(this.programId, this.connection, communityName || this.communityName, networkName, convertedAddress);
return state;
}
}
function categoriesToBitmask(categories) {
let bitmask = new u32(0);
for (const category of categories) {
bitmask = bitmask.or(new BN__default["default"](category));
}
return bitmask;
}
function categoryToBinary(category) {
return new u8(Categories.indexOf(category));
}
const SYSTEM_RENT_KEYS = [
{
pubkey: web3_js.SystemProgram.programId,
isSigner: false,
isWritable: false,
},
{ pubkey: web3_js.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
];
const PROGRAM_SCHEMA = web3_js.SOLANA_SCHEMA;
class CreateCommunityIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.CreateCommunity;
}
}
PROGRAM_SCHEMA.set(CreateCommunityIx, {
kind: "struct",
fields: [
["tag", "u8"],
["name", "string"],
],
});
class UpdateCommunityIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.UpdateCommunity;
}
}
PROGRAM_SCHEMA.set(UpdateCommunityIx, {
kind: "struct",
fields: [
["tag", "u8"],
["name", "string"],
],
});
class CreateNetworkIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.CreateNetwork;
}
}
PROGRAM_SCHEMA.set(CreateNetworkIx, {
kind: "struct",
fields: [
["tag", "u8"],
["name", "string"],
],
});
class CreateReporterIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.CreateReporter;
}
}
PROGRAM_SCHEMA.set(CreateReporterIx, {
kind: "struct",
fields: [
["tag", "u8"],
["reporterType", "u8"],
["name", "string"],
],
});
class UpdateReporterIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.UpdateReporter;
}
}
PROGRAM_SCHEMA.set(UpdateReporterIx, {
kind: "struct",
fields: [
["tag", "u8"],
["reporterType", "u8"],
["name", "string"],
],
});
class CreateCaseIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.CreateCase;
}
}
PROGRAM_SCHEMA.set(CreateCaseIx, {
kind: "struct",
fields: [
["tag", "u8"],
["caseId", "u64"],
["categories", "u32"],
["status", "u8"],
["name", "string"],
],
});
class UpdateCaseIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.UpdateCase;
}
}
PROGRAM_SCHEMA.set(UpdateCaseIx, {
kind: "struct",
fields: [
["tag", "u8"],
["categories", "u32"],
["status", "u8"],
],
});
class CreateAddressIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.CreateAddress;
}
}
PROGRAM_SCHEMA.set(CreateAddressIx, {
kind: "struct",
fields: [
["tag", "u8"],
["address", [32]],
["risk", "u8"],
["caseId", "u64"],
["category", "u8"],
],
});
class UpdateAddressIx extends web3_js.Struct {
constructor() {
super(...arguments);
this.tag = exports.HapiInstruction.UpdateAddress;
}
}
PROGRAM_SCHEMA.set(UpdateAddressIx, {
kind: "struct",
fields: [
["tag", "u8"],
["risk", "u8"],
["caseId", "u64"],
["category", "u8"],
],
});
async function createCaseInstruction({ programId, payer, communityName, caseId, caseName, status, categories, }) {
if (Buffer.from(caseName).length > 28) {
throw new Error("Case name length should not be over 28 bytes");
}
if (status !== exports.CaseStatus.Open && status !== exports.CaseStatus.Closed) {
throw new Error(`Unknown case status ${status}`);
}
categories.forEach((category) => {
if (Categories.indexOf(category) < 0) {
throw new Error(`Unknown category ${category}`);
}
});
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, payer);
const [caseAddress] = await Case.getAddress(programId, communityAddress, caseId);
const ix = new CreateCaseIx({
caseId,
status,
name: caseName,
categories: categoriesToBitmask(categories),
});
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: communityAddress, isSigner: false, isWritable: true },
{ pubkey: reporterAddress, isSigner: false, isWritable: false },
{ pubkey: caseAddress, isSigner: false, isWritable: true },
...SYSTEM_RENT_KEYS,
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
async function updateCaseInstruction({ programId, payer, communityName, caseId, status, categories, }) {
categories.forEach((category) => {
if (Categories.indexOf(category) < 0) {
throw new Error(`Unknown category ${category}`);
}
});
if (status !== exports.CaseStatus.Open && status !== exports.CaseStatus.Closed) {
throw new Error(`Unknown case status ${status}`);
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, payer);
const [caseAddress] = await Case.getAddress(programId, communityAddress, caseId);
const ix = new UpdateCaseIx({
status,
categories: categoriesToBitmask(categories),
});
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: communityAddress, isSigner: false, isWritable: false },
{ pubkey: reporterAddress, isSigner: false, isWritable: false },
{ pubkey: caseAddress, isSigner: false, isWritable: true },
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
async function createAddressInstruction({ programId, payer, communityName, networkName, address, caseId, risk, category, }) {
if (Categories.indexOf(category) < 0) {
throw new Error(`Unknown category: ${category}`);
}
if (risk < 0 || risk > 10) {
throw new RangeError(`Risk must have a value between 0 and 10`);
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, payer);
const [networkAddress] = await Network.getAddress(programId, communityAddress, networkName);
const [caseAddress] = await Case.getAddress(programId, communityAddress, caseId);
const [addressAddress] = await Address.getAddress(programId, networkAddress, address);
const ix = new CreateAddressIx({
address: address.toBytes(),
risk,
caseId,
category: categoryToBinary(category),
});
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: communityAddress, isSigner: false, isWritable: true },
{ pubkey: networkAddress, isSigner: false, isWritable: false },
{ pubkey: reporterAddress, isSigner: false, isWritable: false },
{ pubkey: caseAddress, isSigner: false, isWritable: false },
{ pubkey: addressAddress, isSigner: false, isWritable: true },
...SYSTEM_RENT_KEYS,
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
async function updateAddressInstruction({ programId, payer, communityName, networkName, address, caseId, risk, category, }) {
if (Categories.indexOf(category) < 0) {
throw new Error(`Unknown category: ${category}`);
}
if (risk < 0 || risk > 10) {
throw new RangeError(`Risk must have a value between 0 and 10`);
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, payer);
const [networkAddress] = await Network.getAddress(programId, communityAddress, networkName);
const [caseAddress] = await Case.getAddress(programId, communityAddress, caseId);
const [addressAddress] = await Address.getAddress(programId, networkAddress, address);
const ix = new UpdateAddressIx({
risk,
caseId,
category: categoryToBinary(category),
});
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: communityAddress, isSigner: false, isWritable: false },
{ pubkey: networkAddress, isSigner: false, isWritable: false },
{ pubkey: reporterAddress, isSigner: false, isWritable: false },
{ pubkey: caseAddress, isSigner: false, isWritable: false },
{ pubkey: addressAddress, isSigner: false, isWritable: true },
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
/** HAPI client to operate reporter program functions on Solana */
class ReporterClient extends ReaderClient {
constructor(config) {
super(config);
this.payer = config.payer;
}
get payerPublicKey() {
return this.payer instanceof web3_js.Keypair ? this.payer.publicKey : this.payer;
}
get payerKeypair() {
if (this.payer instanceof web3_js.Keypair) {
return this.payer;
}
throw new Error(`Client is not initialized with payer secret key`);
}
/**
* Create a case creation transaction that can be signed elsewhere
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param caseName The name of the case to create
* @param categories An array of categories to assign to the case
* @returns Transaction to sign
**/
async createCaseTransaction(caseName, status, categories, communityName) {
communityName = this.ensureCommunityName(communityName);
const community = await Community.retrieve(this.programId, this.connection, communityName);
const caseId = community.data.nextCaseId;
const transaction = new web3_js.Transaction();
transaction.add(await createCaseInstruction({
programId: this.programId,
payer: this.payerPublicKey,
caseId,
caseName,
status,
categories,
communityName,
}));
return { transaction, caseId };
}
/**
* Create and sign a case creation transaction
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param caseName The name of the case to create
* @param categories An array of categories to assign to the case
* @returns Transaction hash, account address and entity data
**/
async createCase(caseName, status, caseCategories, communityName) {
communityName = this.ensureCommunityName(communityName);
const { transaction, caseId } = await this.createCaseTransaction(caseName, status, caseCategories, communityName);
const txHash = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.payerKeypair]);
const { data, account } = await Case.retrieve(this.programId, this.connection, communityName, caseId);
return { account, data, txHash, meta: { caseId } };
}
/**
* Create a case updating transaction that can be signed elsewhere
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param caseId The ID of the case to update
* @param categories An array of categories to assign to the case
* @returns Transaction to sign
**/
async updateCaseTransaction(caseId, status, categories, communityName) {
communityName = this.ensureCommunityName(communityName);
const transaction = new web3_js.Transaction();
transaction.add(await updateCaseInstruction({
programId: this.programId,
payer: this.payerPublicKey,
communityName,
caseId,
status,
categories,
}));
return { transaction };
}
/**
* Create and sign a case updating transaction
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param caseID The ID of the case to update
* @param categories An array of categories to assign to the case
* @returns Transaction hash, account address and entity data
**/
async updateCase(caseId, status, caseCategories, communityName) {
communityName = this.ensureCommunityName(communityName);
// Make sure the community exists
await Community.retrieve(this.programId, this.connection, communityName);
// Make sure the case exists
await Case.retrieve(this.programId, this.connection, communityName, caseId);
const { transaction } = await this.updateCaseTransaction(caseId, status, caseCategories, communityName);
const txHash = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.payerKeypair]);
const { data, account } = await Case.retrieve(this.programId, this.connection, communityName, caseId);
return { account, data, txHash };
}
/**
* Create an address creation transaction that can be signed elsewhere
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param networkName The name of the network of the address
* @param address Public key of the address
* @param caseId The ID of the case to assign to the address
* @param category Category to assign to the address
* @param risk Risk score to assign to the address (0 to 10)
* @returns Transaction to sign
**/
async createAddressTransaction(networkName, address, caseId, category, risk, communityName) {
communityName = this.ensureCommunityName(communityName);
// Make sure the community exists
await Community.retrieve(this.programId, this.connection, communityName);
// Make sure the network exists
await Network.retrieve(this.programId, this.connection, communityName, networkName);
// Make sure the case exists
await Case.retrieve(this.programId, this.connection, communityName, caseId);
const transaction = new web3_js.Transaction();
risk = parseInt(risk.toString());
if (risk < 0 || risk > 10) {
throw new RangeError("risk should be an integer between 0 and 10");
}
transaction.add(await createAddressInstruction({
programId: this.programId,
payer: this.payerPublicKey,
communityName,
networkName,
address,
caseId,
category,
risk,
}));
return { transaction };
}
/**
* Create and sign a address creation transaction
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param networkName The name of the network of the address
* @param address Public key of the address
* @param caseId The ID of the case to assign to the address
* @param category Category to assign to the address
* @param risk Risk score to assign to the address (0 to 10)
* @returns Transaction hash, account address and entity data
**/
async createAddress(networkName, address, caseId, category, risk, communityName) {
communityName = this.ensureCommunityName(communityName);
const { transaction } = await this.createAddressTransaction(networkName, address, caseId, category, risk, communityName);
const txHash = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.payerKeypair]);
const { data, account } = await Address.retrieve(this.programId, this.connection, communityName, networkName, address);
return { account, data, txHash };
}
/**
* Create an address updating transaction that can be signed elsewhere
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param networkName The name of the network of the address
* @param address Public key of the address
* @param caseId The ID of the case to assign to the address
* @param category Category to assign to the address
* @param risk Risk score to assign to the address (0 to 10)
* @returns Transaction to sign
**/
async updateAddressTransaction(networkName, address, caseId, category, risk, communityName) {
communityName = this.ensureCommunityName(communityName);
const transaction = new web3_js.Transaction();
transaction.add(await updateAddressInstruction({
programId: this.programId,
payer: this.payerPublicKey,
communityName,
networkName,
address,
caseId,
category,
risk,
}));
return { transaction };
}
/**
* Create and sign a address updating transaction
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @param networkName The name of the network of the address
* @param address Public key of the address
* @param caseId The ID of the case to assign to the address
* @param category Category to assign to the address
* @param risk Risk score to assign to the address (0 to 10)
* @returns Transaction hash, account address and entity data
**/
async updateAddress(networkName, address, caseId, category, risk, communityName) {
communityName = this.ensureCommunityName(communityName);
const { transaction } = await this.updateAddressTransaction(networkName, address, caseId, category, risk, communityName);
const txHash = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.payerKeypair]);
const { data, account } = await Address.retrieve(this.programId, this.connection, communityName, networkName, address);
return { account, data, txHash };
}
}
async function createCommunityInstruction({ programId, payer, communityName, }) {
if (Buffer.from(communityName).length > 28) {
throw new Error("Community name length should not be over 28 bytes");
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const ix = new CreateCommunityIx({ name: communityName });
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: communityAddress, isSigner: false, isWritable: true },
...SYSTEM_RENT_KEYS,
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
async function createNetworkInstructions({ programId, payer, communityName, networkName, }) {
if (Buffer.from(networkName).length > 28) {
throw new Error("Network name length should not be over 28 bytes");
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const [networkAddress] = await Network.getAddress(programId, communityAddress, networkName);
const ix = new CreateNetworkIx({ name: networkName });
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: networkAddress, isSigner: false, isWritable: true },
{ pubkey: communityAddress, isSigner: false, isWritable: false },
...SYSTEM_RENT_KEYS,
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
async function createReporterInstructions({ programId, payer, communityName, reporterPubkey, reporterType, reporterName, }) {
if (Buffer.from(reporterName).length > 28) {
throw new Error("Reporter name length should not be over 28 bytes");
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, reporterPubkey);
const ix = new CreateReporterIx({
name: reporterName,
reporterType,
});
const keys = [
{ pubkey: payer, isSigner: true, isWritable: false },
{ pubkey: communityAddress, isSigner: false, isWritable: true },
{ pubkey: reporterPubkey, isSigner: false, isWritable: false },
{ pubkey: reporterAddress, isSigner: false, isWritable: true },
...SYSTEM_RENT_KEYS,
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
async function updateReporterInstructions({ programId, payer, communityName, reporterPubkey, reporterType, reporterName, }) {
if (Buffer.from(reporterName).length > 28) {
throw new Error("Reporter name length should not be over 28 bytes");
}
const [communityAddress] = await Community.getAddress(programId, communityName);
const [reporterAddress] = await Reporter.getAddress(programId, communityAddress, reporterPubkey);
const ix = new UpdateReporterIx({
name: reporterName,
reporterType,
});
const keys = [
{ pubkey: payer, isSigner: true, isWritable: false },
{ pubkey: communityAddress, isSigner: false, isWritable: true },
{ pubkey: reporterPubkey, isSigner: false, isWritable: false },
{ pubkey: reporterAddress, isSigner: false, isWritable: true },
];
const instruction = new web3_js.TransactionInstruction({
keys,
programId,
data: ix.encode(),
});
return instruction;
}
/** HAPI client to operate authority program functions on Solana */
class AuthorityClient extends ReaderClient {
constructor(config) {
super(config);
this.payer = config.payer;
}
get payerPublicKey() {
return this.payer instanceof web3_js.Keypair ? this.payer.publicKey : this.payer;
}
get payerKeypair() {
if (this.payer instanceof web3_js.Keypair) {
return this.payer;
}
throw new Error(`Client is not initialized with payer secret key`);
}
/**
* Create a community creation transaction that can be signed elsewhere
* @param payer Public key of the payer account
* @param communityName The name of the community to create
* @returns Transaction to sign
**/
async createCommunityTransaction(communityName) {
communityName = this.ensureCommunityName(communityName);
const transaction = new web3_js.Transaction();
// Form a program instruction
transaction.add(await createCommunityInstruction({
programId: this.programId,
payer: this.payerPublicKey,
communityName,
}));
return { transaction };
}
/**
* Create and sign a community creation transaction
* @param payer Payer's key pair to sign the transaction
* @param communityName The name of the community to create
* @param authority (Optional) Public key of an authority of the community (defaults to payer public key)
* @returns Transaction hash, account address and entity data
**/
async createCommunity(communityName) {
communityName = this.ensureCommunityName(communityName);
const { transaction } = await this.createCommunityTransaction(communityName);
const txHash = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.payerKeypair], { commitment: "confirmed" });
const { data, account } = await Community.retrieve(this.programId, this.connection, communityName);
return { account, data, txHash };
}
/**
* Create a network creation transaction that can be signed elsewhere
* @param payer Public key of the payer account (must be the community authority)
* @param communityName The name of the community that the network should belong to
* @param networkName The name of the network to create
* @returns Transaction to sign
**/
async createNetworkTransaction(networkName, communityName) {
communityName = this.ensureCommunityName(communityName);
const transaction = new web3_js.Transaction();
// Form a program instruction
transaction.add(await createNetworkInstructions({
programId: this.programId,
payer: this.payerPublicKey,
communityName,
networkName,
}));
return { transaction };
}
/**
* Create and sign a network creation transaction
* @param payer Payer's key pair to sign the transaction
* @param communityName The name of the community that the network should belong to
* @param networkName The name of the network to create
* @returns Transaction hash, account address and entity data
**/
async createNetwork(networkName, communityName) {
communityName = this.ensureCommunityName(communityName);
const { transaction } = await this.createNetworkTransaction(networkName, communityName);
const txHash = await web3_js.sendAndConfirmTransaction(this.connection, transaction, [this.payerKeypair], { commitment: "confirmed" });
const { data, account } = await Network.retrieve(this.programId, this.connection, communityName, networkName);
return { account, data, txHash };
}
/**
* Create a reporter creation transaction that can be signed elsewhere
* @param payer Public key of the payer account (must be the community authority)
* @param communityName The name of the community that the network should belong to
* @param reporterPubkey Public key of the reporter
* @param reporterType Type of the reporter
* @param reporterName The name of the reporter to create
* @returns Transaction to sign
**/
async createReporterTransaction(reporterPubkey, reporterType, re