@lit-protocol/vincent-scaffold-sdk
Version:
Shared build configuration and utilities for Vincent abilities and policies
332 lines (300 loc) ⢠9.49 kB
text/typescript
import { LIT_NETWORK } from "@lit-protocol/constants";
import { LitContracts } from "@lit-protocol/contracts-sdk";
import { getClient } from "@lit-protocol/vincent-contracts-sdk";
import { ethers } from "ethers";
import { ENV, StateManager } from "../managers";
import {
createPermitAppVersionFunction,
createRegisterAppFunction,
createValidateAbilityExecutionFunction,
} from "../managers/chain-client";
import {
fundPKPIfNeeded,
mintCapacityCredits,
mintNewPkp,
setupAccount,
} from "../utils";
// Type definitions for init function return value
export interface InitAccounts {
funder: {
ethersWallet: ethers.Wallet;
};
agentWalletPkpOwner: {
ethersWallet: ethers.Wallet;
mintAgentWalletPkp: (params: {
abilityAndPolicyIpfsCids: string[];
}) => Promise<{
tokenId: string;
publicKey: string;
ethAddress: string;
}>;
permittedAuthMethods: (params: {
agentWalletPkp: {
tokenId: string;
publicKey: string;
ethAddress: string;
};
abilityAndPolicyIpfsCids: string[];
}) => Promise<string[]>;
permitAppVersion: ReturnType<typeof createPermitAppVersionFunction>;
};
appManager: {
ethersWallet: ethers.Wallet;
registerApp: ReturnType<typeof createRegisterAppFunction>;
validateAbilityExecution: ReturnType<
typeof createValidateAbilityExecutionFunction
>;
};
delegatee: {
ethersWallet: ethers.Wallet;
};
}
export interface InitResult {
accounts: InitAccounts;
ethersAccounts: Record<string, never>;
}
const setupFunderWallet = async (
provider: ethers.providers.JsonRpcProvider
) => {
const funderWallet = new ethers.Wallet(ENV.TEST_FUNDER_PRIVATE_KEY as string, provider);
console.log(
"ā
Funder Wallet Ethers Wallet:",
funderWallet.address
);
const funderBalance = await provider.getBalance(funderWallet.address);
console.log(" ā³ Balance:", ethers.utils.formatEther(funderBalance));
if (funderBalance.lt(ethers.utils.parseEther("0.13"))) {
const errorMessage = `ā Insufficient funder balance. Current balance is below the required 0.13 threshold. Please top up your funder wallet at: https://chronicle-yellowstone-faucet.getlit.dev/`;
console.log(errorMessage);
throw new Error(errorMessage);
}
return funderWallet;
};
const setupAccountManagers = async (
stateManager: StateManager,
funderWallet: ethers.Wallet,
provider: ethers.providers.JsonRpcProvider,
network: "datil" | "datil-test" | "datil-dev",
appManagerFundAmount: ethers.BigNumber,
agentWalletPkpOwnerFundAmount: ethers.BigNumber
) => {
const appManagerResult = await setupAccount(
"appManager",
"App Manager Ethers Wallet",
ENV.TEST_APP_MANAGER_PRIVATE_KEY,
stateManager,
funderWallet,
provider,
appManagerFundAmount
);
const agentWalletPkpOwnerResult = await setupAccount(
"agentWalletPkpOwner",
"Agent Wallet PKP Owner Ethers Wallet",
ENV.TEST_AGENT_WALLET_PKP_OWNER_PRIVATE_KEY,
stateManager,
funderWallet,
provider,
agentWalletPkpOwnerFundAmount
);
const agentWalletContractsClient = new LitContracts({
signer: agentWalletPkpOwnerResult.wallet,
network:
LIT_NETWORK[network as keyof typeof LIT_NETWORK] || LIT_NETWORK.Datil,
});
await agentWalletContractsClient.connect();
return {
appManagerResult,
agentWalletPkpOwnerResult,
agentWalletContractsClient,
};
};
const setupDelegateeWallet = async (
stateManager: StateManager,
funderWallet: ethers.Wallet,
provider: ethers.providers.JsonRpcProvider,
delegateeFundAmount: ethers.BigNumber
) => {
const delegateeResult = await setupAccount(
"appDelegatee",
"Delegatee Ethers wallet",
ENV.TEST_APP_DELEGATEE_PRIVATE_KEY,
stateManager,
funderWallet,
provider,
delegateeFundAmount
);
return {
delegateeAccount: delegateeResult.account,
delegateeEthersWallet: delegateeResult.wallet
};
};
export const init = async ({
network,
fundAmount = "0.01",
fundingAmounts,
deploymentStatus = "dev",
}: {
network: "datil" | "datil-test" | "datil-dev";
fundAmount?: string;
fundingAmounts?: {
appManager?: string;
agentWalletPkpOwner?: string;
delegatee?: string;
pkp?: string;
};
deploymentStatus?: "dev" | "staging" | "production";
}): Promise<InitResult> => {
const _fundingAmounts = {
appManager: ethers.utils.parseEther(fundingAmounts?.appManager || fundAmount),
agentWalletPkpOwner: ethers.utils.parseEther(
fundingAmounts?.agentWalletPkpOwner || fundAmount
),
delegatee: ethers.utils.parseEther(fundingAmounts?.delegatee || fundAmount),
pkp: ethers.utils.parseEther(fundingAmounts?.pkp || fundAmount),
};
/**
* ====================================
* Initialize State Manager
* ====================================
*/
// Auto-detect test filename and use a temporary config hash that will be updated later
const detectedTestFileName = StateManager.autoDetectTestFileName();
const temporaryConfigHash = 'pending'; // Will be updated when configuration is provided
console.log(`\nšļø Initialising E2E environment for ${detectedTestFileName} (configuration will be detected during app registration)`);
const stateManager = new StateManager(network, detectedTestFileName, temporaryConfigHash);
await stateManager.loadState();
/**
* ====================================
* Setup all components using helper functions
* ====================================
*/
// Create ethers provider for Yellowstone
const provider = new ethers.providers.JsonRpcProvider(ENV.YELLOWSTONE_RPC_URL);
const funderWallet = await setupFunderWallet(provider);
const {
appManagerResult,
agentWalletPkpOwnerResult,
agentWalletContractsClient,
} = await setupAccountManagers(
stateManager,
funderWallet,
provider,
network,
_fundingAmounts.appManager,
_fundingAmounts.agentWalletPkpOwner
);
const { delegateeAccount, delegateeEthersWallet } =
await setupDelegateeWallet(
stateManager,
funderWallet,
provider,
_fundingAmounts.delegatee
);
/**
* ====================================
* Create Vincent contracts SDK instances
* ====================================
*/
const appManagerContractClient = getClient({ signer: appManagerResult.wallet });
const agentWalletPkpOwnerContractClient = getClient({ signer: agentWalletPkpOwnerResult.wallet });
/**
* ====================================
* Mint capacity credits for the delegatee
* ====================================
*/
const { capacityCredits: mintCCRes, isNew: isNewCC } =
await stateManager.getOrMintCapacityCredits(async () => {
return await mintCapacityCredits(delegateeEthersWallet, network);
});
console.log(
"ā
Delegatee capacity credits:",
mintCCRes,
isNewCC ? "(newly minted)" : "(from cache)"
);
/**
* ====================================
* Create and return a function to mint an Agent Wallet PKP
* ====================================
*/
const mintAgentWalletPkp = async ({
abilityAndPolicyIpfsCids,
}: {
abilityAndPolicyIpfsCids: string[];
}) => {
const { pkp, isNew } = await stateManager.getOrMintPKP(async () => {
return await mintNewPkp(
agentWalletPkpOwnerResult.privateKey,
network,
...abilityAndPolicyIpfsCids
);
});
// Fund the PKP if it's newly minted
await fundPKPIfNeeded(
pkp.ethAddress,
isNew,
funderWallet,
provider,
_fundingAmounts.pkp
);
// Save state after PKP operations
await stateManager.saveState();
return pkp;
};
// Save state if any new accounts were generated
await stateManager.saveState();
console.log("\n----- (init.ts) Initialisation Completed \n");
return {
accounts: {
funder: {
ethersWallet: funderWallet,
},
agentWalletPkpOwner: {
ethersWallet: agentWalletPkpOwnerResult.wallet,
mintAgentWalletPkp: mintAgentWalletPkp,
permittedAuthMethods: async ({
agentWalletPkp,
abilityAndPolicyIpfsCids,
}: {
agentWalletPkp: {
tokenId: string;
publicKey: string;
ethAddress: string;
};
abilityAndPolicyIpfsCids: string[];
}) => {
const results: string[] = [];
for (const ipfsCid of abilityAndPolicyIpfsCids) {
const result = await agentWalletContractsClient.addPermittedAction({
pkpTokenId: agentWalletPkp.tokenId,
ipfsId: ipfsCid,
authMethodScopes: [
1, // Sign Anything
],
});
results.push(result.transactionHash);
}
return results;
},
permitAppVersion: createPermitAppVersionFunction(
stateManager,
agentWalletPkpOwnerContractClient
),
},
appManager: {
ethersWallet: appManagerResult.wallet,
registerApp: createRegisterAppFunction(
stateManager,
appManagerContractClient,
delegateeAccount
),
validateAbilityExecution: createValidateAbilityExecutionFunction(
appManagerContractClient
),
},
delegatee: {
ethersWallet: delegateeEthersWallet,
},
},
ethersAccounts: {},
};
};