Skip to content

Commit 2728414

Browse files
committed
feat: refactor state functions into Actions
1 parent 00435e9 commit 2728414

File tree

9 files changed

+353
-175
lines changed

9 files changed

+353
-175
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { voidAsyncFunction } from '../types';
2+
3+
export interface ActionParams {
4+
debug?: boolean;
5+
function: voidAsyncFunction;
6+
}
7+
8+
export class Action {
9+
protected readonly debug;
10+
private readonly function: voidAsyncFunction;
11+
12+
constructor(params: ActionParams) {
13+
this.debug = params.debug;
14+
this.function = params.function;
15+
}
16+
17+
async run() {
18+
return this.function();
19+
}
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export * from './action';
2+
export * from './lit-action';
3+
export * from './log-context';
4+
export * from './mint-capacity-credit';
5+
export * from './mint-pkp';
6+
export * from './transaction';
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { AutomationError } from '@lit-protocol/constants';
2+
3+
import { Action } from './action';
4+
import { executeLitAction } from '../litActions';
5+
import { StateMachine } from '../state-machine';
6+
import { ContextOrLiteral, PKPInfo } from '../types';
7+
8+
interface LitActionActionParams {
9+
debug?: boolean;
10+
stateMachine: StateMachine;
11+
code?: ContextOrLiteral<string>;
12+
ipfsId?: ContextOrLiteral<string>;
13+
jsParams?: Record<string, unknown>;
14+
}
15+
16+
export class LitActionAction extends Action {
17+
constructor(params: LitActionActionParams) {
18+
const litActionFunction = async () => {
19+
const activePkp = params.stateMachine.resolveContextPathOrLiteral({
20+
contextPath: 'activePkp',
21+
}) as unknown as PKPInfo;
22+
if (!activePkp) {
23+
throw new AutomationError(
24+
{
25+
info: {
26+
machineId: params.stateMachine.id,
27+
activePkp,
28+
},
29+
},
30+
`There is no active pkp. Must configure it to run a Lit Action`
31+
);
32+
}
33+
34+
const litActionResponse = await executeLitAction({
35+
litNodeClient: params.stateMachine.litNodeClient,
36+
capacityTokenId: params.stateMachine.resolveContextPathOrLiteral({
37+
contextPath: 'activeCapacityTokenId',
38+
}) as unknown as string,
39+
pkpEthAddress: activePkp.ethAddress,
40+
pkpPublicKey: activePkp.publicKey,
41+
authSigner: params.stateMachine.signer,
42+
ipfsId:
43+
'ipfsId' in params
44+
? params.stateMachine.resolveContextPathOrLiteral(params.ipfsId)
45+
: undefined,
46+
code:
47+
'code' in params
48+
? params.stateMachine.resolveContextPathOrLiteral(params.code)
49+
: undefined,
50+
jsParams: params.jsParams,
51+
});
52+
53+
// TODO send user this result with a webhook and log
54+
params.stateMachine.setToContext(
55+
'lastLitActionResponse',
56+
litActionResponse
57+
);
58+
};
59+
60+
super({
61+
debug: params.debug,
62+
function: litActionFunction,
63+
});
64+
}
65+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Action } from './action';
2+
import { StateMachine } from '../state-machine';
3+
4+
interface LogContextActionParams {
5+
debug?: boolean;
6+
stateMachine: StateMachine;
7+
path?: string;
8+
}
9+
10+
export class LogContextAction extends Action {
11+
constructor(params: LogContextActionParams) {
12+
const logContextFunction = async () => {
13+
console.log(
14+
`State Machine context: `,
15+
params.stateMachine.getFromContext(params.path)
16+
);
17+
};
18+
19+
super({
20+
debug: params.debug,
21+
function: logContextFunction,
22+
});
23+
}
24+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Action } from './action';
2+
import { StateMachine } from '../state-machine';
3+
4+
interface MintPkpActionParams {
5+
debug?: boolean;
6+
stateMachine: StateMachine;
7+
daysUntilUTCMidnightExpiration: number;
8+
requestPerSecond: number;
9+
}
10+
11+
export class MintCapacityCreditAction extends Action {
12+
constructor(params: MintPkpActionParams) {
13+
const mintPkpFunction = async () => {
14+
const capacityCreditNFT =
15+
await params.stateMachine.litContracts.mintCapacityCreditsNFT({
16+
requestsPerSecond: params.requestPerSecond,
17+
daysUntilUTCMidnightExpiration: params.daysUntilUTCMidnightExpiration,
18+
});
19+
const capacityTokeId = capacityCreditNFT.capacityTokenIdStr;
20+
params.debug && console.log(`Minted PKP: ${capacityTokeId}`);
21+
params.stateMachine.setToContext(`activeCapacityTokenId`, capacityTokeId);
22+
};
23+
24+
super({
25+
debug: params.debug,
26+
function: mintPkpFunction,
27+
});
28+
}
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Action } from './action';
2+
import { StateMachine } from '../state-machine';
3+
4+
interface MintPkpActionParams {
5+
debug?: boolean;
6+
stateMachine: StateMachine;
7+
}
8+
9+
export class MintPkpAction extends Action {
10+
constructor(params: MintPkpActionParams) {
11+
const mintPkpFunction = async () => {
12+
const mintingReceipt =
13+
await params.stateMachine.litContracts.pkpNftContractUtils.write.mint();
14+
const pkp = mintingReceipt.pkp;
15+
params.debug && console.log(`Minted PKP: ${pkp}`);
16+
params.stateMachine.setToContext('activePkp', pkp);
17+
};
18+
19+
super({
20+
debug: params.debug,
21+
function: mintPkpFunction,
22+
});
23+
}
24+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { ethers } from 'ethers';
2+
3+
import { AutomationError } from '@lit-protocol/constants';
4+
5+
import { Action } from './action';
6+
import { executeLitAction, signWithLitActionCode } from '../litActions';
7+
import { StateMachine } from '../state-machine';
8+
import { Address, ContextOrLiteral, PKPInfo } from '../types';
9+
import { getEvmChain } from '../utils/chain';
10+
11+
interface TransactionActionParams {
12+
debug?: boolean;
13+
stateMachine: StateMachine;
14+
evmChainId: ContextOrLiteral<number>;
15+
contractABI: ethers.ContractInterface;
16+
contractAddress: ContextOrLiteral<Address>;
17+
method: ContextOrLiteral<string>;
18+
params?: ContextOrLiteral<unknown>[];
19+
value?: ContextOrLiteral<string>;
20+
}
21+
22+
export class TransactionAction extends Action {
23+
constructor(params: TransactionActionParams) {
24+
const litActionFunction = async () => {
25+
const activePkp = params.stateMachine.resolveContextPathOrLiteral({
26+
contextPath: 'activePkp',
27+
}) as unknown as PKPInfo;
28+
if (!activePkp.ethAddress) {
29+
throw new AutomationError(
30+
{
31+
info: {
32+
machineId: params.stateMachine.id,
33+
activePkp,
34+
},
35+
},
36+
`There is no active pkp. Must configure it to run a transaction`
37+
);
38+
}
39+
40+
const yellowstoneMachineSigner = params.stateMachine.signer;
41+
42+
const chainId = params.stateMachine.resolveContextPathOrLiteral(
43+
params.evmChainId
44+
);
45+
const chain = getEvmChain(chainId);
46+
const chainProvider = new ethers.providers.JsonRpcProvider(
47+
chain.rpcUrls[0],
48+
chain.chainId
49+
);
50+
51+
const contract = new ethers.Contract(
52+
params.stateMachine.resolveContextPathOrLiteral(params.contractAddress),
53+
params.contractABI,
54+
chainProvider
55+
);
56+
57+
const txParams = (params.params || []).map(
58+
params.stateMachine.resolveContextPathOrLiteral.bind(
59+
params.stateMachine
60+
)
61+
);
62+
const txMethod = params.stateMachine.resolveContextPathOrLiteral(
63+
params.method
64+
);
65+
const txData = await contract.populateTransaction[txMethod](...txParams);
66+
const gasLimit = await chainProvider.estimateGas({
67+
to: params.stateMachine.resolveContextPathOrLiteral(
68+
params.contractAddress
69+
),
70+
data: txData.data,
71+
from: activePkp.ethAddress,
72+
});
73+
const gasPrice = await chainProvider.getGasPrice();
74+
const nonce = await chainProvider.getTransactionCount(
75+
activePkp.ethAddress
76+
);
77+
78+
const rawTx = {
79+
chainId: chain.chainId,
80+
data: txData.data,
81+
gasLimit: gasLimit.toHexString(),
82+
gasPrice: gasPrice.toHexString(),
83+
nonce,
84+
to: params.stateMachine.resolveContextPathOrLiteral(
85+
params.contractAddress
86+
),
87+
};
88+
const rawTxHash = ethers.utils.keccak256(
89+
ethers.utils.serializeTransaction(rawTx)
90+
);
91+
92+
// Sign with the PKP in a LitAction
93+
const litActionResponse = await executeLitAction({
94+
litNodeClient: params.stateMachine.litNodeClient,
95+
capacityTokenId: params.stateMachine.resolveContextPathOrLiteral({
96+
contextPath: 'activeCapacityTokenId',
97+
}) as unknown as string,
98+
pkpEthAddress: activePkp.ethAddress,
99+
pkpPublicKey: activePkp.publicKey,
100+
authSigner: yellowstoneMachineSigner,
101+
code: signWithLitActionCode,
102+
jsParams: {
103+
toSign: ethers.utils.arrayify(rawTxHash),
104+
publicKey: activePkp.publicKey,
105+
sigName: 'signedTransaction',
106+
},
107+
});
108+
109+
const signature = litActionResponse.response as string;
110+
const jsonSignature = JSON.parse(signature);
111+
jsonSignature.r = '0x' + jsonSignature.r.substring(2);
112+
jsonSignature.s = '0x' + jsonSignature.s;
113+
const hexSignature = ethers.utils.joinSignature(jsonSignature);
114+
115+
const signedTx = ethers.utils.serializeTransaction(rawTx, hexSignature);
116+
117+
const receipt = await chainProvider.sendTransaction(signedTx);
118+
119+
// TODO send user this result with a webhook and log
120+
params.stateMachine.setToContext('lastTransactionReceipt', receipt);
121+
};
122+
123+
super({
124+
debug: params.debug,
125+
function: litActionFunction,
126+
});
127+
}
128+
}

0 commit comments

Comments
 (0)