@neardefi/shade-agent-js
Version:
A library for creating Shade Agent agents in JavaScript and TypeScript
821 lines (810 loc) • 27.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ShadeClient: () => ShadeClient,
sanitize: () => sanitize,
toThrowable: () => toThrowable
});
module.exports = __toCommonJS(index_exports);
// src/utils/near.ts
var import_providers = require("@near-js/providers");
var import_signers = require("@near-js/signers");
var import_accounts = require("@near-js/accounts");
var import_crypto = require("@near-js/crypto");
var import_tokens = require("@near-js/tokens");
var import_transactions = require("@near-js/transactions");
// src/utils/sanitize.ts
var import_deep_redact = require("@hackylabs/deep-redact/index.ts");
var REDACTED = "[REDACTED]";
var SENSITIVE_KEYS = ["privateKey", "private_key", "secretKey", "secret_key"];
var deepRedact = new import_deep_redact.DeepRedact({
serialise: false,
blacklistedKeys: SENSITIVE_KEYS,
stringTests: [
{
pattern: /privateKey|private_key|secretKey|secret_key/i,
replacer: () => REDACTED
},
{
pattern: /ed25519:[^\s]+/g,
replacer: (value, pattern) => value.replace(pattern, REDACTED)
},
{
pattern: /secp256k1:[^\s]+/g,
replacer: (value, pattern) => value.replace(pattern, REDACTED)
}
]
});
function sanitize(value) {
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean" || typeof value === "symbol" || typeof value === "bigint") {
return value;
}
if (typeof value === "string") {
const result = deepRedact.redact(value);
return String(result);
}
if (value instanceof Error) {
const result = deepRedact.redact(value.message);
return new Error(String(result) || "An error occurred");
}
if (typeof value === "object") {
const result = deepRedact.redact(value);
return typeof result === "object" && result !== null ? result : {};
}
return value;
}
function toThrowable(error) {
const result = sanitize(error);
if (result instanceof Error) return result;
if (typeof result === "object" && result !== null) {
return new Error(JSON.stringify(result));
}
return new Error(String(result) || "An error occurred");
}
// src/utils/near.ts
function createDefaultProvider(networkId) {
return new import_providers.JsonRpcProvider(
{
url: networkId === "testnet" ? "https://test.rpc.fastnear.com" : "https://free.rpc.fastnear.com"
},
{
retries: 3,
backoff: 2,
wait: 1e3
}
);
}
function createAccountObject(accountId, provider, signer) {
try {
return new import_accounts.Account(accountId, provider, signer);
} catch (error) {
throw new Error(`Failed to create account object`);
}
}
async function internalFundAgent(agentAccountId, sponsorAccountId, sponsorPrivateKey, amount, provider) {
const signer = import_signers.KeyPairSigner.fromSecretKey(
sponsorPrivateKey
);
const account = new import_accounts.Account(sponsorAccountId, provider, signer);
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const fundResult = await account.transfer({
token: import_tokens.NEAR,
amount: import_tokens.NEAR.toUnits(amount),
receiverId: agentAccountId
});
if (typeof fundResult.status === "object" && fundResult.status.Failure) {
const rawMsg = fundResult.status.Failure.error_message || fundResult.status.Failure.error_type;
const sanitized = sanitize(String(rawMsg));
const errorMsg = typeof sanitized === "string" ? sanitized : String(sanitized);
const error = new Error(`Transfer transaction failed: ${errorMsg}`);
if (attempt === maxRetries) {
throw error;
}
continue;
} else {
return;
}
} catch {
if (attempt === maxRetries) {
throw new Error(
`Failed to fund agent account ${agentAccountId} after ${maxRetries} attempts`
);
}
}
}
}
async function addKeysToAccount(account, secrets) {
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const actions = secrets.map((secretKey) => {
const keyPair = import_crypto.KeyPair.fromString(secretKey);
return import_transactions.actionCreators.addKey(
keyPair.getPublicKey(),
import_transactions.actionCreators.fullAccessKey()
);
});
const tx = await account.createSignedTransaction(
account.accountId,
actions
);
const txResult = await account.provider.sendTransaction(tx);
if (typeof txResult.status === "object" && txResult.status.Failure) {
if (attempt === maxRetries) {
throw new Error(`Failed to add keys after ${maxRetries} attempts`);
}
continue;
} else {
return;
}
} catch {
if (attempt === maxRetries) {
throw new Error(`Failed to add keys after ${maxRetries} attempts`);
}
}
}
}
async function removeKeysFromAccount(account, secrets) {
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const actions = secrets.map((secretKey) => {
const keyPair = import_crypto.KeyPair.fromString(secretKey);
return import_transactions.actionCreators.deleteKey(keyPair.getPublicKey());
});
const tx = await account.createSignedTransaction(
account.accountId,
actions
);
const txResult = await account.provider.sendTransaction(tx);
if (typeof txResult.status === "object" && txResult.status.Failure) {
if (attempt === maxRetries) {
throw new Error(`Failed to remove keys after ${maxRetries} attempts`);
}
continue;
} else {
return;
}
} catch {
if (attempt === maxRetries) {
throw new Error(`Failed to remove keys after ${maxRetries} attempts`);
}
}
}
}
// src/utils/tee.ts
var import_fs = require("fs");
var import_dstack_sdk = require("@phala/dstack-sdk");
// src/utils/attestation-transform.ts
function hexToBytes(hexStr) {
if (!hexStr || hexStr === "") {
return [];
}
try {
return Array.from(Buffer.from(hexStr, "hex"));
} catch {
throw new Error("Failed to decode hex string");
}
}
function bytesToHex(bytes) {
if (bytes.length === 0) {
return "";
}
return Buffer.from(bytes).toString("hex");
}
function transformQuote(quoteHex) {
const cleanedHex = quoteHex.replace(/^0x/, "");
return Array.from(Buffer.from(cleanedHex, "hex"));
}
function transformCollateral(rawCollateral) {
return {
pck_crl_issuer_chain: rawCollateral.pck_crl_issuer_chain || "",
root_ca_crl: hexToBytes(rawCollateral.root_ca_crl),
pck_crl: hexToBytes(rawCollateral.pck_crl),
tcb_info_issuer_chain: rawCollateral.tcb_info_issuer_chain || "",
tcb_info: rawCollateral.tcb_info || "",
tcb_info_signature: hexToBytes(rawCollateral.tcb_info_signature),
qe_identity_issuer_chain: rawCollateral.qe_identity_issuer_chain || "",
qe_identity: rawCollateral.qe_identity || "",
qe_identity_signature: hexToBytes(rawCollateral.qe_identity_signature)
};
}
function transformTcbInfo(dstackTcbInfo) {
return {
mrtd: dstackTcbInfo.mrtd || "",
rtmr0: dstackTcbInfo.rtmr0 || "",
rtmr1: dstackTcbInfo.rtmr1 || "",
rtmr2: dstackTcbInfo.rtmr2 || "",
rtmr3: dstackTcbInfo.rtmr3 || "",
os_image_hash: dstackTcbInfo.os_image_hash || "",
compose_hash: dstackTcbInfo.compose_hash || "",
device_id: dstackTcbInfo.device_id || "",
app_compose: dstackTcbInfo.app_compose || "",
event_log: (dstackTcbInfo.event_log || []).map(
(event) => ({
imr: event.imr,
event_type: event.event_type,
digest: event.digest,
event: event.event,
event_payload: event.event_payload
})
)
};
}
function attestationForContract(attestation) {
return {
quote: attestation.quote,
collateral: {
pck_crl_issuer_chain: attestation.collateral.pck_crl_issuer_chain,
root_ca_crl: bytesToHex(attestation.collateral.root_ca_crl),
pck_crl: bytesToHex(attestation.collateral.pck_crl),
tcb_info_issuer_chain: attestation.collateral.tcb_info_issuer_chain,
tcb_info: attestation.collateral.tcb_info,
tcb_info_signature: bytesToHex(attestation.collateral.tcb_info_signature),
qe_identity_issuer_chain: attestation.collateral.qe_identity_issuer_chain,
qe_identity: attestation.collateral.qe_identity,
qe_identity_signature: bytesToHex(
attestation.collateral.qe_identity_signature
)
},
tcb_info: attestation.tcb_info
};
}
function getFakeAttestationInternal() {
const ZERO_48_HEX = "0".repeat(96);
const ZERO_32_HEX = "0".repeat(64);
return {
quote: [],
collateral: {
pck_crl_issuer_chain: "",
root_ca_crl: [],
pck_crl: [],
tcb_info_issuer_chain: "",
tcb_info: "",
tcb_info_signature: [],
qe_identity_issuer_chain: "",
qe_identity: "",
qe_identity_signature: []
},
tcb_info: {
mrtd: ZERO_48_HEX,
rtmr0: ZERO_48_HEX,
rtmr1: ZERO_48_HEX,
rtmr2: ZERO_48_HEX,
rtmr3: ZERO_48_HEX,
os_image_hash: "",
compose_hash: ZERO_32_HEX,
device_id: ZERO_32_HEX,
app_compose: "",
event_log: []
}
};
}
function getFakeAttestation() {
return attestationForContract(getFakeAttestationInternal());
}
// src/utils/tee.ts
async function getDstackClient() {
if (!(0, import_fs.existsSync)("/var/run/dstack.sock")) {
return void 0;
}
try {
const client = new import_dstack_sdk.DstackClient();
await client.info();
return client;
} catch {
return void 0;
}
}
async function internalGetAttestation(dstackClient, agentAccountId, keysDerivedWithTEE) {
if (!dstackClient || !keysDerivedWithTEE) {
return getFakeAttestation();
}
const info = await dstackClient.info();
const dstackTcbInfo = info.tcb_info;
const accountIdBytes = Buffer.from(agentAccountId, "hex");
const reportData = Buffer.alloc(64);
accountIdBytes.copy(reportData, 0);
const quoteResponse = await dstackClient.getQuote(reportData);
const quote_hex = quoteResponse.quote;
const quote = transformQuote(quote_hex);
const formData = new FormData();
formData.append("hex", quote_hex.replace(/^0x/, ""));
let collateral;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3e4);
const collateralUrl = "https://cloud-api.phala.network/api/v1/attestations/verify";
const response = await fetch(collateralUrl, {
method: "POST",
body: formData,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(
`Failed to get quote collateral: HTTP ${response.status}`
);
}
const resHelper = await response.json();
collateral = transformCollateral(resHelper.quote_collateral);
} catch {
throw new Error("Failed to get quote collateral");
}
const tcb_info = transformTcbInfo(dstackTcbInfo);
const attestation = {
quote,
collateral,
tcb_info
};
return attestationForContract(attestation);
}
// src/utils/agent.ts
var import_node_crypto = require("crypto");
var import_near_seed_phrase = require("near-seed-phrase");
var import_crypto2 = require("@near-js/crypto");
var import_signers2 = require("@near-js/signers");
var import_accounts2 = require("@near-js/accounts");
async function generateAgent(dstackClient, derivationPath) {
try {
const { hash, usedTEE } = await deriveHash(dstackClient, derivationPath);
const seedInfo = (0, import_near_seed_phrase.generateSeedPhrase)(hash);
const accountId = Buffer.from(import_crypto2.PublicKey.from(seedInfo.publicKey).data).toString("hex").toLowerCase();
return {
accountId,
agentPrivateKey: seedInfo.secretKey,
derivedWithTEE: usedTEE
};
} catch {
throw new Error("Failed to create agent");
}
}
function deriveHashFromPath(derivationPath) {
return (0, import_node_crypto.createHash)("sha256").update(Buffer.from(derivationPath)).digest();
}
function deriveHashFromRandom() {
const randomArray = new Uint8Array(32);
crypto.getRandomValues(randomArray);
const randomString = Buffer.from(randomArray).toString("hex");
return (0, import_node_crypto.createHash)("sha256").update(Buffer.from(randomString)).digest();
}
async function deriveHashForTEE(dstackClient) {
const randomArray = new Uint8Array(32);
crypto.getRandomValues(randomArray);
const randomString = Buffer.from(randomArray).toString("hex");
const keyFromTee = (await dstackClient.getKey(randomString)).key;
return Buffer.from(
await crypto.subtle.digest(
"SHA-256",
Buffer.concat([randomArray, keyFromTee])
)
);
}
async function deriveHash(dstackClient, derivationPath) {
let hash;
let usedTEE;
if (!dstackClient && derivationPath) {
hash = deriveHashFromPath(derivationPath);
usedTEE = false;
} else if (!dstackClient && !derivationPath) {
hash = deriveHashFromRandom();
usedTEE = false;
} else {
hash = await deriveHashForTEE(dstackClient);
usedTEE = true;
}
return { hash, usedTEE };
}
async function manageKeySetup(agentAccount, numAdditionalKeys, dstackClient, derivationPath) {
try {
const keysOnAccount = await agentAccount.getAccessKeyList();
const numKeysOnAccount = keysOnAccount.keys.length;
const numExistingAdditionalKeys = numKeysOnAccount - 1;
const numKeysToDerive = Math.max(
numAdditionalKeys,
numExistingAdditionalKeys
);
const { keys, allDerivedWithTEE } = await deriveAdditionalKeys(
numKeysToDerive,
dstackClient,
derivationPath
);
if (numExistingAdditionalKeys < numAdditionalKeys) {
const keysToAdd = keys.slice(
numExistingAdditionalKeys,
numAdditionalKeys
);
await addKeysToAccount(agentAccount, keysToAdd);
} else if (numExistingAdditionalKeys > numAdditionalKeys) {
const excessKeys = keys.slice(numAdditionalKeys);
await removeKeysFromAccount(agentAccount, excessKeys);
}
const keysToSave = keys.slice(0, numAdditionalKeys);
return {
keysToSave,
allDerivedWithTEE
};
} catch (error) {
throw toThrowable(error);
}
}
async function deriveAdditionalKeys(numKeys, dstackClient, derivationPath) {
const keyPromises = Array.from({ length: numKeys }, async (_, index) => {
const i = index + 1;
const keyDerivationPath = derivationPath ? `${derivationPath}-${i}` : void 0;
const { hash, usedTEE } = await deriveHash(dstackClient, keyDerivationPath);
const seedInfo = (0, import_near_seed_phrase.generateSeedPhrase)(hash);
return {
key: seedInfo.secretKey,
derivedWithTEE: usedTEE
};
});
const results = await Promise.all(keyPromises);
const keys = results.map((r) => r.key);
const allDerivedWithTEE = results.every((r) => r.derivedWithTEE);
return { keys, allDerivedWithTEE };
}
function getAgentSigner(agentPrivateKeys, currentKeyIndex) {
try {
if (agentPrivateKeys.length === 0) {
throw new Error("No agent keys available");
}
if (agentPrivateKeys.length === 1) {
return {
signer: import_signers2.KeyPairSigner.fromSecretKey(
agentPrivateKeys[0]
),
keyIndex: 0
};
}
currentKeyIndex++;
if (currentKeyIndex > agentPrivateKeys.length - 1) {
currentKeyIndex = 0;
}
return {
signer: import_signers2.KeyPairSigner.fromSecretKey(
agentPrivateKeys[currentKeyIndex]
),
keyIndex: currentKeyIndex
};
} catch (error) {
throw toThrowable(error);
}
}
async function ensureKeysSetup(agentAccountId, agentPrivateKeys, rpc, numKeys, dstackClient, derivationPath, keysDerivedWithTEE, keysChecked) {
try {
if (keysChecked) {
return { keysToAdd: [], wasChecked: true };
}
const signer = import_signers2.KeyPairSigner.fromSecretKey(
agentPrivateKeys[0]
);
const agentAccount = new import_accounts2.Account(agentAccountId, rpc, signer);
const { keysToSave, allDerivedWithTEE } = await manageKeySetup(
agentAccount,
numKeys - 1,
dstackClient,
derivationPath
);
if (!allDerivedWithTEE) {
if (keysDerivedWithTEE) {
throw new Error(
"First key was derived with TEE but additional keys were not. Something went wrong with the key derivation."
);
}
}
return { keysToAdd: keysToSave, wasChecked: true };
} catch (error) {
throw toThrowable(error);
}
}
// src/utils/validation.ts
async function validateShadeConfig(config) {
if (config.networkId === void 0) {
config.networkId = "testnet";
}
if (config.networkId !== "testnet" && config.networkId !== "mainnet") {
throw new Error("networkId must be either 'testnet' or 'mainnet'");
}
if (config.sponsor) {
if (!config.sponsor.accountId || config.sponsor.accountId.trim() === "") {
throw new Error("sponsor.accountId is required when sponsor is provided");
}
if (!config.sponsor.privateKey || config.sponsor.privateKey.trim() === "") {
throw new Error(
"sponsor.privateKey is required when sponsor is provided"
);
}
}
if (config.numKeys === void 0) {
config.numKeys = 1;
}
if (!Number.isInteger(config.numKeys) || config.numKeys < 1 || config.numKeys > 100) {
throw new Error("numKeys must be an integer between 1 and 100");
}
if (!config.rpc) {
config.rpc = createDefaultProvider(config.networkId);
}
const rpcNetworkId = await config.rpc.getNetworkId();
if (rpcNetworkId !== config.networkId) {
throw new Error(
`Network ID mismatch: config.networkId is "${config.networkId}" but RPC provider is connected to "${rpcNetworkId}"`
);
}
}
// src/api.ts
var import_tokens2 = require("@near-js/tokens");
var ShadeClient = class _ShadeClient {
// true if the number of keys have been checked (happens on the first call), false otherwise
// Private constructor so only `create()` can be used to create an instance
constructor(config, dstackClient, accountId, agentPrivateKeys, keysDerivedWithTEE) {
this.config = config;
this.dstackClient = dstackClient;
this.agentAccountId = accountId;
this.agentPrivateKeys = agentPrivateKeys;
this.currentKeyIndex = 0;
this.keysDerivedWithTEE = keysDerivedWithTEE;
this.keysChecked = false;
}
/**
* Creates a new ShadeClient instance asynchronously
* @param config - Configuration object for the Shade client (see ShadeConfig interface for details)
* @returns Promise that resolves to a ShadeClient instance
* @throws Error if configuration is invalid, network ID mismatch, or key generation fails
*/
static async create(config) {
try {
await validateShadeConfig(config);
const dstackClient = await getDstackClient();
const agentPrivateKeys = [];
const { accountId, agentPrivateKey, derivedWithTEE } = await generateAgent(dstackClient, config.derivationPath);
agentPrivateKeys.push(agentPrivateKey);
return new _ShadeClient(
config,
dstackClient,
accountId,
agentPrivateKeys,
derivedWithTEE
);
} catch (error) {
throw toThrowable(error);
}
}
/**
* Gets the NEAR account ID of the agent
* @returns The agent's account ID
*/
accountId() {
return this.agentAccountId;
}
/**
* Gets the NEAR balance of the agent account in human readable format (e.g. 1 = one NEAR)
* @returns Promise that resolves to the account balance in NEAR tokens, if the agent account does not exist, returns 0
* @throws Error if network request fails
*/
async balance() {
const account = createAccountObject(this.agentAccountId, this.config.rpc);
try {
const balance = await account.getBalance();
return parseFloat(import_tokens2.NEAR.toDecimal(balance));
} catch (error) {
const err = error;
if (err?.type === "AccountDoesNotExist") {
return 0;
}
throw toThrowable(error);
}
}
/**
* Registers the agent in the agent contract
* @returns Promise that resolves to true if registration was successful
* @throws Error if agentContractId is not configured, if fetching attestation fails (network errors, timeouts), or if contract call fails
*/
async register() {
if (!this.config.agentContractId) {
throw new Error("agentContractId is required for registering the agent");
}
try {
const contractAttestation = await internalGetAttestation(
this.dstackClient,
this.agentAccountId,
this.keysDerivedWithTEE
);
return await this.call({
methodName: "register_agent",
args: {
attestation: contractAttestation
},
deposit: "5000000000000000000000",
// 0.005 NEAR
gas: BigInt("300000000000000")
// 300 TGas
});
} catch (error) {
throw toThrowable(error);
}
}
/**
* Call a view function on the agent contract and return the result
* @param params
* @param params.methodName The method that will be called
* @param params.args Arguments as a valid JSON Object
* @param params.blockQuery (optional) Block reference for the query
* @returns A promise that resolves with the result of the view function call
* @throws Error if agentContractId is not configured or if RPC call fails
*/
async view(params) {
if (!this.config.agentContractId) {
throw new Error("agentContractId is required for view calls");
}
try {
return await this.config.rpc.callFunction(
this.config.agentContractId,
params.methodName,
params.args,
params.blockQuery
);
} catch (error) {
throw toThrowable(error);
}
}
/**
* Call a function on the agent contract and return the result
* @param params
* @param params.methodName The method that will be called
* @param params.args Arguments, either as a valid JSON Object or a raw Uint8Array
* @param params.deposit (optional) Amount of NEAR Tokens to attach to the call
* @param params.gas (optional) Amount of GAS to use attach to the call
* @param params.waitUntil (optional) Transaction finality to wait for
* @returns A promise that resolves with the result of the contract function call
* @throws Error if agentContractId is not configured, if key derivation fails, or if transaction fails
*/
async call(params) {
if (!this.config.agentContractId) {
throw new Error("agentContractId is required for call functions");
}
try {
const { keysToAdd, wasChecked } = await ensureKeysSetup(
this.agentAccountId,
this.agentPrivateKeys,
this.config.rpc,
this.config.numKeys,
this.dstackClient,
this.config.derivationPath,
this.keysDerivedWithTEE,
this.keysChecked
);
this.agentPrivateKeys.push(...keysToAdd);
if (wasChecked) {
this.keysChecked = true;
}
const { signer, keyIndex } = getAgentSigner(
this.agentPrivateKeys,
this.currentKeyIndex
);
this.currentKeyIndex = keyIndex;
const account = createAccountObject(
this.agentAccountId,
this.config.rpc,
signer
);
return await account.callFunction({
contractId: this.config.agentContractId,
methodName: params.methodName,
args: params.args,
gas: params.gas,
deposit: params.deposit,
waitUntil: params.waitUntil
});
} catch (error) {
throw toThrowable(error);
}
}
/**
* Gets the TEE attestation for the agent in contract format (ready to be sent to the contract)
* @returns Promise that resolves to the contract-formatted attestation object
* @throws Error if fetching quote collateral fails (network errors, HTTP errors, timeouts)
*/
async getAttestation() {
try {
return await internalGetAttestation(
this.dstackClient,
this.agentAccountId,
this.keysDerivedWithTEE
);
} catch (error) {
throw toThrowable(error);
}
}
/**
* Funds the agent account with NEAR tokens from the sponsor account
* @param fundAmount - Amount of NEAR tokens to transfer to the agent account in human readable format (e.g. 1 = one NEAR)
* @returns Promise that resolves when funding is complete
* @throws Error if sponsor is not configured or if transfer fails after retries
*/
async fund(fundAmount) {
if (!this.config.sponsor) {
throw new Error("sponsor is required for funding the agent account");
}
try {
await internalFundAgent(
this.agentAccountId,
this.config.sponsor.accountId,
this.config.sponsor.privateKey,
fundAmount,
this.config.rpc
);
} catch (error) {
throw toThrowable(error);
}
}
/**
* Gets the agent's private keys (use with caution)
* @param params - Must pass `{ acknowledgeRisk: true }`
* @returns Array of private key strings
*/
getPrivateKeys(params) {
if (!params.acknowledgeRisk) {
throw new Error(
"WARNING: Exporting private keys from the library is a risky operation, you may accidentally leak them from the TEE. Do not use the keys to sign transactions other than to the agent contract. Please acknowledge the risk by setting acknowledgeRisk to true."
);
}
console.log(
"WARNING: Exporting private keys from the library is a risky operation, you may accidentally leak them from the TEE. Do not use the keys to sign transactions other than to the agent contract."
);
return this.agentPrivateKeys;
}
/**
* Checks if the agent is whitelisted for local mode
* @returns Promise that resolves to true if the agent is whitelisted, false if the agent is not whitelisted, or null if the agent contract requires TEE
* @throws Error if agentContractId is not configured or if view call fails
*/
async isWhitelisted() {
if (!this.config.agentContractId) {
throw new Error(
"agentContractId is required for checking if the agent is whitelisted"
);
}
try {
const res = await this.view({
methodName: "get_contract_info",
args: {}
});
if (res.requires_tee) {
return null;
}
const whitelisted_agents = await this.view({
methodName: "get_whitelisted_agents_for_local",
args: {}
});
return whitelisted_agents.includes(this.agentAccountId);
} catch (error) {
throw toThrowable(error);
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ShadeClient,
sanitize,
toThrowable
});