Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions src/configs/acpConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class AcpContractConfig {
public maxRetries: number,
public rpcEndpoint?: string,
public x402Config?: X402Config,
public retryConfig?: {
intervalMs: number;
multiplier: number;
maxRetries: number;
}
) {}
}

Expand All @@ -31,7 +36,7 @@ const baseSepoliaAcpConfig = new AcpContractConfig(
ACP_ABI,
V1_MAX_RETRIES,
undefined,
undefined,
undefined
);

const baseSepoliaAcpX402Config = new AcpContractConfig(
Expand All @@ -45,7 +50,7 @@ const baseSepoliaAcpX402Config = new AcpContractConfig(
undefined,
{
url: "https://dev-acp-x402.virtuals.io",
},
}
);

const baseSepoliaAcpConfigV2 = new AcpContractConfig(
Expand All @@ -57,7 +62,7 @@ const baseSepoliaAcpConfigV2 = new AcpContractConfig(
ACP_V2_ABI,
V2_MAX_RETRIES,
undefined,
undefined,
undefined
);

const baseSepoliaAcpX402ConfigV2 = new AcpContractConfig(
Expand All @@ -71,7 +76,7 @@ const baseSepoliaAcpX402ConfigV2 = new AcpContractConfig(
undefined,
{
url: "https://dev-acp-x402.virtuals.io",
},
}
);

const baseAcpConfig = new AcpContractConfig(
Expand All @@ -83,7 +88,7 @@ const baseAcpConfig = new AcpContractConfig(
ACP_ABI,
V1_MAX_RETRIES,
undefined,
undefined,
undefined
);

const baseAcpX402Config = new AcpContractConfig(
Expand All @@ -97,7 +102,7 @@ const baseAcpX402Config = new AcpContractConfig(
undefined,
{
url: "https://acp-x402.virtuals.io",
},
}
);

const baseAcpConfigV2 = new AcpContractConfig(
Expand All @@ -109,7 +114,7 @@ const baseAcpConfigV2 = new AcpContractConfig(
ACP_V2_ABI,
V2_MAX_RETRIES,
undefined,
undefined,
undefined
);

const baseAcpX402ConfigV2 = new AcpContractConfig(
Expand All @@ -123,7 +128,7 @@ const baseAcpX402ConfigV2 = new AcpContractConfig(
undefined,
{
url: "https://acp-x402.virtuals.io",
},
}
);

export {
Expand Down
81 changes: 51 additions & 30 deletions src/contractClients/acpContractClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ class AcpContractClient extends BaseAcpContractClient {
protected PRIORITY_FEE_MULTIPLIER = 2;
protected MAX_FEE_PER_GAS = 20000000;
protected MAX_PRIORITY_FEE_PER_GAS = 21000000;
private RETRY_CONFIG = {
intervalMs: 200,
multiplier: 1.1,
maxRetries: 10,
};

private _sessionKeyClient: ModularAccountV2Client | undefined;
private _acpX402: AcpX402 | undefined;

constructor(
agentWalletAddress: Address,
config: AcpContractConfig = baseAcpConfig,
config: AcpContractConfig = baseAcpConfig
) {
super(agentWalletAddress, config);
}
Expand All @@ -41,12 +46,9 @@ class AcpContractClient extends BaseAcpContractClient {
walletPrivateKey: Address,
sessionEntityKeyId: number,
agentWalletAddress: Address,
config: AcpContractConfig = baseAcpConfig,
config: AcpContractConfig = baseAcpConfig
) {
const acpContractClient = new AcpContractClient(
agentWalletAddress,
config,
);
const acpContractClient = new AcpContractClient(agentWalletAddress, config);
await acpContractClient.init(walletPrivateKey, sessionEntityKeyId);
return acpContractClient;
}
Expand Down Expand Up @@ -76,15 +78,22 @@ class AcpContractClient extends BaseAcpContractClient {
);

const account = this.sessionKeyClient.account;
const sessionSignerAddress: Address = await account.getSigner().getAddress();
const sessionSignerAddress: Address = await account
.getSigner()
.getAddress();

if (!await account.isAccountDeployed()) {
if (!(await account.isAccountDeployed())) {
throw new AcpError(
`ACP Contract Client validation failed: agent account ${this.agentWalletAddress} is not deployed on-chain`
);
}

await this.validateSessionKeyOnChain(sessionSignerAddress, sessionEntityKeyId);
await this.validateSessionKeyOnChain(
sessionSignerAddress,
sessionEntityKeyId
);

this.RETRY_CONFIG = this.config.retryConfig || this.RETRY_CONFIG;

console.log("Connected to ACP with v1 Contract Client (Legacy):", {
agentWalletAddress: this.agentWalletAddress,
Expand Down Expand Up @@ -129,46 +138,56 @@ class AcpContractClient extends BaseAcpContractClient {
return finalMaxFeePerGas;
}

async handleOperation(operations: OperationPayload[]): Promise<{ userOpHash: Address , txnHash: Address }> {
const payload: any = {
async handleOperation(
operations: OperationPayload[]
): Promise<{ userOpHash: Address; txnHash: Address }> {
const basePayload: any = {
uo: operations.map((op) => ({
target: op.contractAddress,
data: op.data,
value: op.value,
})),
overrides: {
nonceKey: this.getRandomNonce(),
},
};

let retries = this.config.maxRetries;
let iteration = 0;
let finalError: unknown;

while (retries > 0) {
while (iteration < this.config.maxRetries) {
try {
if (this.config.maxRetries > retries) {
const gasFees = await this.calculateGasFees();

payload["overrides"] = {
maxFeePerGas: `0x${gasFees.toString(16)}`,
};
}
const currentMultiplier = 1 + 0.1 * (iteration + 1);

const payload: any = {
...basePayload,
overrides: {
nonceKey: this.getRandomNonce(),
maxFeePerGas: {
multiplier: currentMultiplier,
},
maxPriorityFeePerGas: {
multiplier: currentMultiplier,
},
},
};

const { hash } = await this.sessionKeyClient.sendUserOperation(payload);

const txnHash = await this.sessionKeyClient.waitForUserOperationTransaction({
hash,
});
const txnHash =
await this.sessionKeyClient.waitForUserOperationTransaction({
hash,
tag: "pending",
retries: this.RETRY_CONFIG,
});

return { userOpHash: hash, txnHash };
} catch (error) {
retries -= 1;
if (retries === 0) {
iteration++;

if (iteration === this.config.maxRetries) {
finalError = error;
break;
}

await new Promise((resolve) => setTimeout(resolve, 2000 * retries));
await new Promise((resolve) => setTimeout(resolve, 2000 * iteration));
}
}

Expand All @@ -180,7 +199,9 @@ class AcpContractClient extends BaseAcpContractClient {
clientAddress: Address,
providerAddress: Address
) {
const result = await this.sessionKeyClient.getUserOperationReceipt(createJobUserOpHash);
const result = await this.sessionKeyClient.getUserOperationReceipt(
createJobUserOpHash
);

if (!result) {
throw new AcpError("Failed to get user operation receipt");
Expand Down
81 changes: 49 additions & 32 deletions src/contractClients/acpContractClientV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class AcpContractClientV2 extends BaseAcpContractClient {
private PRIORITY_FEE_MULTIPLIER = 2;
private MAX_FEE_PER_GAS = 20000000;
private MAX_PRIORITY_FEE_PER_GAS = 21000000;
private GAS_FEE_MULTIPLIER = 0.5;
private RETRY_CONFIG = {
intervalMs: 200,
multiplier: 1.1,
maxRetries: 10,
};

private _sessionKeyClient: ModularAccountV2Client | undefined;
private _acpX402: AcpX402 | undefined;
Expand All @@ -34,7 +40,7 @@ class AcpContractClientV2 extends BaseAcpContractClient {
private memoManagerAddress: Address,
private accountManagerAddress: Address,
agentWalletAddress: Address,
config: AcpContractConfig = baseAcpConfigV2,
config: AcpContractConfig = baseAcpConfigV2
) {
super(agentWalletAddress, config);
}
Expand All @@ -43,7 +49,7 @@ class AcpContractClientV2 extends BaseAcpContractClient {
walletPrivateKey: Address,
sessionEntityKeyId: number,
agentWalletAddress: Address,
config: AcpContractConfig = baseAcpConfigV2,
config: AcpContractConfig = baseAcpConfigV2
) {
const publicClient = createPublicClient({
chain: config.chain,
Expand Down Expand Up @@ -82,7 +88,7 @@ class AcpContractClientV2 extends BaseAcpContractClient {
memoManagerAddress.result as Address,
accountManagerAddress.result as Address,
agentWalletAddress,
config,
config
);

await acpContractClient.init(walletPrivateKey, sessionEntityKeyId);
Expand Down Expand Up @@ -115,15 +121,22 @@ class AcpContractClientV2 extends BaseAcpContractClient {
);

const account = this.sessionKeyClient.account;
const sessionSignerAddress: Address = await account.getSigner().getAddress();
const sessionSignerAddress: Address = await account
.getSigner()
.getAddress();

if (!await account.isAccountDeployed()) {
if (!(await account.isAccountDeployed())) {
throw new AcpError(
`ACP Contract Client validation failed: agent account ${this.agentWalletAddress} is not deployed on-chain`
);
}

await this.validateSessionKeyOnChain(sessionSignerAddress, sessionEntityKeyId);
await this.validateSessionKeyOnChain(
sessionSignerAddress,
sessionEntityKeyId
);

this.RETRY_CONFIG = this.config.retryConfig || this.RETRY_CONFIG;

console.log("Connected to ACP:", {
agentWalletAddress: this.agentWalletAddress,
Expand Down Expand Up @@ -168,52 +181,56 @@ class AcpContractClientV2 extends BaseAcpContractClient {
return finalMaxFeePerGas;
}

async handleOperation(operations: OperationPayload[]): Promise<{ userOpHash: Address , txnHash: Address }> {
const payload: any = {
async handleOperation(
operations: OperationPayload[]
): Promise<{ userOpHash: Address; txnHash: Address }> {
const basePayload: any = {
uo: operations.map((operation) => ({
target: operation.contractAddress,
data: operation.data,
value: operation.value,
})),
overrides: {
nonceKey: this.getRandomNonce(),
},
};

let retries = this.config.maxRetries;
let iteration = 0;
let finalError: unknown;

while (retries > 0) {
while (iteration < this.config.maxRetries) {
try {
if (this.config.maxRetries > retries) {
const gasFees = await this.calculateGasFees();

payload["overrides"] = {
maxFeePerGas: `0x${gasFees.toString(16)}`,
};
}
const currentMultiplier = 1 + 0.1 * (iteration + 1);

const payload: any = {
...basePayload,
overrides: {
nonceKey: this.getRandomNonce(),
maxFeePerGas: {
multiplier: currentMultiplier,
},
maxPriorityFeePerGas: {
multiplier: currentMultiplier,
},
},
};

const { hash } = await this.sessionKeyClient.sendUserOperation(payload);

const txnHash = await this.sessionKeyClient.waitForUserOperationTransaction({
hash,
tag: "pending",
retries: {
intervalMs: 200,
multiplier: 1.1,
maxRetries: 10,
},
});
const txnHash =
await this.sessionKeyClient.waitForUserOperationTransaction({
hash,
tag: "pending",
retries: this.RETRY_CONFIG,
});

return { userOpHash: hash, txnHash };
} catch (error) {
retries -= 1;
if (retries === 0) {
iteration++;

if (iteration === this.config.maxRetries) {
finalError = error;
break;
}

await new Promise((resolve) => setTimeout(resolve, 2000 * retries));
await new Promise((resolve) => setTimeout(resolve, 2000 * iteration));
}
}

Expand Down
Loading